Files
Shade/.scaffold/logs/2026-08-14-shade-vault.md
2026-08-14 11:58:49 +02:00

3.5 KiB

title, date, author
title date author
@shade/vault — server-side kryptert fillager (V4.13) 2026-08-14 Claude

Shade kunne flytte filer mellom peers (@shade/files) og lagre én liten profil-blob per konto (/v1/blob/<slotId>), men hadde ingen alltid-på lagring av krypterte filer. Uten den kan ingen Shade-app tilby backup, og ingen klient kan lese data mens peeren som eier dem er avslått. @shade/vault fyller det hullet.

Modellen

Tre ideer, og formen følger av dem.

Objekter er innholdsadresserte. Et objekts navn er SHA-256 av ciphertexten, så relayen kan lagre, deduplisere og verifisere uten å forstå noe. Den regner om hashen ved opplasting og avviser et objekt som ikke er det det utgir seg for — uten å eie en nøkkel.

Et manifest navngir samlingen. Stier bor i manifestet, ikke i objektnavn: relayen skal ikke lære at brukeren har en fil som heter Projects/Skilsmisse/plan.md. Manifestet er selv kryptert og lagret som et objekt.

Loggen er append-only. Hver commit legger til en rad som peker på et manifest. Historikk, rollback og «hva endret seg sist tirsdag» faller ut av det — versjoneringshalvdelen av bestillingen.

Valg verdt å begrunne

Egen HKDF-gren (shade-vault-{id,content,sig}-v1) ved siden av shade-blob-*-v1. En vault-nøkkel leser hver eneste fil; en profil-blob-nøkkel leser en vertsliste. Delt derivasjon ville gjort ett kompromiss om til det andre.

Ingen konvergent kryptering. Identisk klartekst gir ulike objektnavn fordi nonce er fersk. Dedupliseringen skjer derfor innenfor en versjonskjede — en uendret fil beholder hashen sin fordi klienten gjenbruker objektet den allerede lastet opp — ikke på tvers av uavhengige forseglinger. Konvergent kryptering ville lekket hvilke filer to brukere deler, som er nøyaktig det en blind butikk ikke skal.

Commit verifiserer at objektene finnes. Ellers ville loggen kunne publisere en versjon som ikke lar seg gjenopprette, og det er verdt en ekstra rundtur å utelukke.

Pubkeyen er inne i signaturen. Første forsøk la den på utenfor, som både brøt verifyPayload (den kanonikaliserer alle felt) og — hvis skjemaet hadde ignorert det — ville latt hvem som helst bytte identitet i transit på den TOFU-pinnende førsteskrivingen. Fanget av testene.

Verifisering

16 tester, alle mot de ekte rutehåndtererne gjennom Honos fetch — ikke mot en mock-transport, som ville vært enig med klienten per konstruksjon og ikke bevist noe om wire-kontrakten.

Dekket: rundtur bit-for-bit; gjenoppretting på en fersk klient som bare har kontonøkkelen; feil master-nøkkel når ikke fram; tre versjoner med rollback til hver enkelt; en 50 KB uendret fil lastes ikke opp på nytt (bytesUploaded under 1 KB på andre push); slettet fil borte i ny versjon, intakt i gammel; lagrede objekter inneholder verken innhold eller sti; feilnavngitt objekt avvist; SEQ_CONFLICT ved commit fra utdatert head; MISSING_OBJECTS ved manglende referanse; fremmed nøkkel avvist mot en pinnet vault; usignert skriving avvist; og at vault-grenen er forskjellig fra blob-grenen.

Typecheck ren. @shade/storage-encrypted fortsatt 87/87 etter endringene i kdf.ts.

Gjenstår

Ruting i shade-server/src/standalone.ts ved siden av createBlobRoutes, en varig VaultStore (SQLite/Postgres — bare MemoryVaultStore finnes), docs/vault.md, og deploy. Ingenting av det er gjort: deploy og publisering er utenfor mandatet for en uovervåket økt.

Arbeidet ligger ukommittert i arbeidstreet, sammen med de eksisterende endringene fra juli.