# Pithos in the browser (plan) Status: 2026-10-05. Phases 1-3 built and tested locally (crypto port, gateway / + /_app/ route, sign-in with a key file, browse, view, download, upload, new folder, rename, delete, remember). Not deployed: needs DNS for pithos.silentmode.st. Decided: `https://pithos.silentmode.st`, sign-in only, "Remember on this device" allowed. ## Goal Open your drives from any browser, on any device, with nothing installed: browse, upload, download, rename, delete and share files on **Drives on Silent Mode** (the hosted gateway at `https://s3.silentmode.st`). Out of scope: a drive on your own computer (`http://127.0.0.1:8000`). s3d sends no CORS headers, so a web page cannot talk to it; for that the website hands over to the local Pithos ("Open my Pithos", port 47621). ## Where it lives: its own origin **`https://pithos.silentmode.st`**: a vhost of its own that serves the app files and forwards `/hosted/v1/*`, `/api/*` and the S3 paths to the hosted gateway (127.0.0.1:8720), so the page and the API share one origin. - Not `app.pithos.sia` either: outside Theseus BNS names are paths of navigate.st (and BNS folds subdomains into the parent name's folder), so the origin would still be navigate.st. Per-name origins on navigate.st (`.navigate.st`, wildcard certificate) would fix that for every BNS site; a separate project. - Not on `navigate.st/bns/pithos.sia/app`: every BNS site served through navigate.st shares the origin `navigate.st`, so any other `.sia`/`.bch` site could open the app's window (same origin) and read the keys out of it, or read anything it stored. - Same origin as the API, so **no CORS** has to be opened on the gateway at all, and the browser keeps the keys away from every other site. - `pithos.sia/app` keeps its meaning: in Theseus it is the full app (the add-on answers it); in any other browser the website's app-address hint gets a third button, "Open in the browser app", linking to `https://pithos.silentmode.st`. - Strict CSP (`default-src 'self'`, `connect-src 'self'`, no inline script, `frame-ancestors 'none'`), no third-party scripts, files served by the gateway from `Pithos/web/` (versioned with Pithos). ## Signing in Two kinds of key, both loaded from a file (drag and drop or picker) or pasted: 1. **Your Pithos key** (owner, full access, encrypted drives readable). - Without Theseus: the existing export, `pithos-hosted-key.json` (`{v:1, secret}`), Accounts › On Silent Mode › Export key. From the secret: identity seed = HKDF(secret, "", "pithos/hosted-identity/v1", 32), encryption master = HKDF(secret, "", "pithos/hosted-encryption/v1", 32). - With Theseus there is no file: the keys come from the vault. New export **"Key for the browser app"** (vault unlocked + Theseus approval prompt): `{v:2, identitySeed, encryptionKey}`, the two 32-byte values derived from the vault paths `pithos/hosted-identity/v1` and `pithos/hosted-encryption/v1`. NEVER change those paths. The file is as powerful as the vault for these drives; the dialog says so. - Flow: `GET /hosted/v1/challenge` → Ed25519 signature in WebCrypto (PKCS8 wrapper `302e020100300506032b657004220420 || seed`) → `POST /hosted/v1/session` → Bearer token → `GET /api/keys` → the account's S3 key → S3 calls signed in the page. 2. **An S3 key file** (the Export JSON format `{endpoint, accessKey, secretKey, bucket}`), including folder keys (grant keys `PG…`) from Share…. No encryption key: plain files and shared folders (stored readable) work, encrypted names show as "encrypted file". The endpoint must be the gateway. Keys live **in memory only** by default; closing the tab signs out. "Remember on this device" (allowed): the key file is wrapped with a passphrase (PBKDF2-SHA256 600k + AES-GCM, as Save credentials… does) in IndexedDB on the pithos.silentmode.st origin; the next visit asks for the passphrase only. Sign out wipes it and calls `DELETE` on the session. ## Crypto in the page Port `core/crypt.js` and the signing part of `core/s3.js` to browser ESM in `Pithos/web/lib/`, sharing test vectors with the Node versions. - HKDF-SHA256, AES-256-GCM (bodies), AES-CTR, HMAC-SHA256 (SigV4): WebCrypto. - AES-SIV (RFC 5297) for names is not in WebCrypto: - CMAC/S2V from AES-CBC with a zero IV (WebCrypto appends a PKCS#7 block: take the block before it); one-block AES-ECB the same way. - SIV-CTR with AES-CTR `counter = V'` (bits cleared as crypt.js does), `length: 128`. - No library: @noble/ciphers' `siv` is AES-GCM-SIV (RFC 8452), not RFC 5297. - Tests: RFC 5297 vectors, plus Node-encrypts / browser-decrypts and back for names (with the bucket + parent-path AD) and bodies (chunk sizes, ranges, empty file), run under Node's WebCrypto in `npm test`. ## What the app does (v1) - Drives list, folder browsing (merged encrypted + plain listing, as `core/hosted.js` does), icons/list views. - Upload: drag and drop; encrypted chunk by chunk (1 MiB) in the page; multipart above ~64 MiB so big files never sit in memory whole. - Download: decrypt as a stream (File System Access API where available, else a Blob; very large files on browsers without it get a warning). - Rename, move, delete, new folder (renames re-encrypt, as the desktop does). - Share: public folder / link / folder key through `/hosted/v1/rules`. - Viewer for pictures and text, audio/video player using range reads. - Free space and account state (`/hosted/v1/me`, `/api/space`). Later: the docked music player, offline cache, PWA install. ## Server changes - DNS `pithos.silentmode.st` → the VPS; certificate; an Angie vhost that serves `Pithos/web/` at `/` (strict CSP, versioned file names, long cache) and proxies `/hosted/v1/`, `/api/` and S3 requests to 127.0.0.1:8720. - The gateway must accept that Host for SigV4: the page signs requests for host `pithos.silentmode.st`; owner keys go raw to s3d, which checks the signature over the Host it receives, so the vhost passes `Host` through unchanged and `publicUrl`-based checks (challenge message) need the second name (the challenge text names the URL the client signed in at). - No CORS: same origin. Path rules unchanged (owner keys raw-proxied to s3d; grant keys checked by `rules.js`). Sign-in rate limits apply. ## Phases 1. Crypto + SigV4 port with cross-tests (largest risk, done first). 2. Gateway `/app` route + sign-in with a key file + read-only browsing and downloads. 3. Uploads, folders, rename/delete. 4. Sharing, viewer and player. 5. "Key for the browser app" export in Theseus; website button; docs. ## Open questions - Decided 2026-10-05: `pithos.silentmode.st`; sign-in only (accounts are made in Pithos); "Remember on this device" allowed (passphrase-wrapped). - Still open: does s3d accept SigV4 for a Host other than the one it was set up with? (It checks the signature over whatever Host arrives, so it should; verify in phase 2.)