42 lines
3.5 KiB
Markdown
42 lines
3.5 KiB
Markdown
---
|
|
title: "@shade/vault — server-side kryptert fillager (V4.13)"
|
|
date: 2026-08-14
|
|
author: 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.
|