Teknik

Hur Dreamalizing kör en språkmodell utan molnet

En dröm är det mest personliga en människa kan skriva ner. Därför skickas drömtexten i Dreamalizing aldrig till en extern AI-tjänst. Denna artikel beskriver hur det fungerar tekniskt, vilka val som gjordes och vad som gick fel längs vägen.

Den är skriven för utvecklare och för alla som vill kontrollera om integritetslöftet är mer än bara en mening på startsidan.

Löftet, i en mening

Din drömtext bearbetas av vår egen språkmodell på servrar inom Europeiska unionen. Dina uppgifter lagras krypterat och din drömtext skickas inte till externa AI-tjänster.

Den meningen finns i appen, i butikerna och på denna webbplats. Allt nedanför existerar för att uppfylla den, även om någon gör ett stavfel i en konfigurationsfil.

Kedjan, från telefon till modell

Appen (React Native, Expo) kommunicerar med ett API (NestJS, PostgreSQL) på en hyrd server inom EU. Den servern har ingen språkmodell. Den når via en WireGuard-tunnel en gateway på egen hårdvara, och den gatewayen kommunicerar med modellen.

Modellen är Gemma 4 med 26 miljarder parametrar i en mixture-of-experts-konfiguration, varav cirka 4 miljarder är aktiva per token, i 8-bitars kvantisering. En modell gör allt: frågorna, analysen och den strukturerade extraktionen. Kontextfönstret är fastställt till 32 768 tokens, eftersom den tyngsta analysen hittills använde 13 671 tokens och utdata får vara upp till 8 192 tokens.

Fram till augusti kördes två modeller: en tyngre för frågor och analys, en lättare för extraktion. Det visade sig vara dyrare än vad det gav. De två modellerna trängde undan varandra i minnet, vilket gjorde att en modell laddades tre gånger per dröm, med 7 till 10 sekunder per gång. Den tyngre modellen gör extraktionen 3,7 sekunder långsammare, men sparar 27 sekunder laddningstid.

Lokal-only som säkerhet, inte som avsikt

API:et har ett ruttningslager som kan skicka till flera leverantörer. Det är praktiskt vid utveckling men farligt i produktion. Därför finns det tre lås.

För det första en hård flagga i runtime. Om den är på, nekar API:et varje anrop med dröminnehåll till en icke-lokal leverantör. En nödlösning finns, men är bunden till ett slutdatum på maximalt fjorton dagar och löper ut automatiskt; ett ogiltigt eller för långt framtida datum ignoreras och säkerhetskontrollen förblir stängd.

För det andra gatewayen. Varje modell-id som API:et efterfrågar börjar med prefixet local-. Gatewayen läser detta som lokal-only och vägrar därmed att leverera ett sådant anrop till en molnleverantör, även om en reservkedja pekar dit.

För det tredje tester. Ett test tvingar API:et att aldrig ta bort prefixet, ett annat att modellkonfigurationen för test- och produktionsmiljö är identisk. Det första testet finns för att API:et faktiskt tog bort prefixet fram till slutet av augusti. Det innebar att gatewayens reservväg i teorin kunde ha skickat drömtext till molnet, med status 200 och utan signal. Appen hade då ännu inte lanserats och hade, förutom oss själva, inga aktiva användare. Vi har stängt vägen och lagt till testet så att det inte längre är möjligt, inte ens o märkt.

Väntetid är ett designval

En lokal modell på en maskin är långsammare än ett moln-API. Svaren på frågorna kommer efter 20 till 40 sekunder, analysen efter 30 till 60. Vi döljer inte detta; appen säger vad som händer och ungefär hur lång tid det tar.

Ordningen är anpassad efter detta. Först går frågorna till appen, baserat på den råa drömtexten. Därefter, parallellt, bygger ett andra anrop de strukturerade data som analysen senare behöver. Under tiden besvarar drömmaren frågor och märker ingenting av det andra anropet.

På maskinen körs en begränsare som tillåter ett anrop åt gången per användare och som aldrig låter valfria anrop gå före obligatoriska. Det senare lärde vi oss från en produktionsincident: ett valfritt anrop trängde undan det obligatoriska, och drömmaren väntade på ingenting.

Lagring och offline

Drömtext och svar lagras krypterat per användare med AES-256-GCM och en egen nyckel per användare, som i sin tur ligger under en huvudnyckel. På telefonen finns kön med ännu ej skickade drömmar i en krypterad SQLCipher-databas med en nyckel i enhetens säkra lagring.

Varje skrivåtgärd bär på ett stabilt operations-id. Om anslutningen bryts efter sändning men före svar, kommer en upprepning att leverera samma dröm, inte en andra. Loggen för dessa id:n innehåller aldrig drömtext, bara typ av åtgärd, en hash och resultatet.

Vad det kostar

En maskin är ett tak. Vid samtidiga användare staplas anropen på varandra och väntetiden ökar. Det är priset för inget moln, och anledningen till att vi började i Nederländerna och inte globalt.

Det andra priset är att modellen är sämre än de största molnmodellerna. Därför mäter vi alltid ändringar i prompter på den lokala modellen, aldrig på en molnmodell; vad som fungerar på en större modell fungerar inte automatiskt här.

Vad som följer

Frågor om arkitekturen är välkomna. Det som står här är tillståndet i september 2026; om något ändras, ändras även denna artikel.