Hvordan Dreamalizing kjører en språkmodell uten sky
En drøm er det mest personlige et menneske kan skrive ned. Derfor vil drømmeteksten i Dreamalizing aldri sendes til en ekstern AI-tjeneste. Denne artikkelen beskriver hvordan dette fungerer teknisk, hvilke valg som ble tatt som følge av dette, og hva som gikk galt underveis.
Den er skrevet for utviklere og for alle som vil kontrollere om personvernløftet er mer enn bare en setning på hjemmesiden.
Løftet, i én setning
Drømmeteksten din behandles av vår egen språkmodell på servere innenfor Den europeiske union. Dataene dine lagres kryptert, og drømmeteksten din sendes ikke til eksterne AI-tjenester.
Den setningen står i appen, i butikkene og på dette nettstedet. Alt nedenfor eksisterer for å oppfylle den, selv om noen gjør en skrivefeil i en konfigurasjonsfil.
Kjeden, fra telefon til modell
Appen (React Native, Expo) snakker med et API (NestJS, PostgreSQL) på en leid server i EU. Denne serveren har ingen språkmodell. Den når via en WireGuard-tunnel en gateway på egen maskinvare, og den gatewayen snakker med modellen.
Modellen er Gemma 4 med 26 milliarder parametere i et mixture-of-experts-oppsett, hvorav omtrent 4 milliarder er aktive per token, i 8-bits kvantisering. Én modell gjør alt: spørsmålene, analysen og den strukturerte ekstraksjonen. Kontekstvinduet er fastsatt til 32 768 tokens, fordi den tyngste analysen så langt brukte 13 671 tokens og utdataene kan være opptil 8 192 tokens.
Frem til august kjørte vi to modeller: en tyngre for spørsmål og analyse, og en lettere for ekstraksjon. Det viste seg å være dyrere enn det ga avkastning. De to modellene fortrengte hverandre i minnet, noe som førte til at en modell ble lastet tre ganger per drøm, med 7 til 10 sekunder per gang. Den tyngre modellen gjør ekstraksjonen 3,7 sekunder tregere, men sparer 27 sekunder lastetid.
Lokal-kun som overvåking, ikke som hensikt
API-et har et rutingslag som kan sende til flere leverandører. Det er praktisk under utvikling, men farlig i produksjon. Derfor finnes det tre låser.
For det første et hardt flagg i kjøretiden (runtime). Hvis dette er på, vil API-et avvise ethvert kall med drømmeinnhold til en ikke-lokal leverandør. En nødløsning eksisterer, men er bundet til en sluttdato på maksimalt fjorten dager og utløper automatisk; en ugyldig eller for fjern dato blir ignorert, og overvåkingen forblir stengt.
For det andre gatewayen. Hver modell-id som API-et etterspør, starter med prefikset local-. Gatewayen leser dette som lokal-kun og vil derfor nekte å levere en slik forespørsel til en skyleverandør, selv om en reservekjede peker dit.
For det tredje tester. En test tvinger API-et til aldri å fjerne prefikset, en annen sikrer at modellkonfigurasjonen i test- og produksjonsmiljøet er identisk. Den første testen eksisterer fordi API-et faktisk fjernet prefikset frem til slutten av august. Dermed kunne gatewayens reservevei i teorien ha sendt drømmetekst til skyen, med status 200 og uten varsel. Appen var ikke lansert ennå og hadde ingen aktive brukere utenom oss selv. Vi har stengt stien og lagt til testen, slik at det ikke lenger er mulig, heller ikke umerket.
Ventetid er et designvalg
En lokal modell på én maskin er tregere enn et sky-API. Spørsmålene er klare etter 20 til 40 sekunder, analysen etter 30 til 60. Vi skjuler ikke dette; appen sier hva som skjer og omtrent hvor lang tid det tar.
Rekkefølgen er tilrettelagt for dette. Først går spørsmålene til appen, basert på den rå drømmeteksten. Deretter, parallelt, bygger et andre kall opp de strukturerte dataene som analysen senere trenger. Drømmeren svarer i mellomtiden på spørsmål og merker ingenting til det andre kallet.
På maskinen kjører en begrenser som tillater ett kall per bruker om gangen, og som aldri lar valgfrie kall gå før de obligatoriske. Det siste lærte vi fra en produksjonshendelse: et valgfritt kall fortrengte det obligatoriske, og drømmeren ventet på ingenting.
Lagring og offline
Drømmetekst og svar lagres kryptert per bruker med AES-256-GCM og en egen nøkkel per bruker, som igjen ligger under en hovednøkkel. På telefonen ligger køen av ennå ikke sendte drømmer i en kryptert SQLCipher-database med en nøkkel i enhetens sikre lagring.
Hver skriveoperasjon bærer en stabil operasjons-id. Hvis tilkoblingen brytes etter sending, men før svaret, vil en gjentakelse gi samme drøm, ikke en ny. Loggen over disse id-ene inneholder aldri drømmetekst, bare typen handling, en hash og resultatet.
Hva det koster
Én maskin er ett tak. Ved mange samtidige brukere hoper kallene seg opp og ventetiden øker. Det er prisen for ingen sky, og grunnen til at vi startet i Nederland og ikke globalt.
Den andre prisen er at modellen er mindre god enn de største skymodellene. Vi måler derfor alltid endringer i prompter på den lokale modellen, aldri på en skymodell; det som fungerer på en større modell, fungerer ikke nødvendigvis her.
Hva som følger
Spørsmål om arkitekturen er velkomne. Det som står her, er tilstanden i september 2026; hvis noe endres, vil denne artikkelen oppdateres.