diff --git a/CLAUDE.md b/CLAUDE.md index 57bff24..5e744c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,8 +74,35 @@ Choke points: **Encryption:** All credential fields are encrypted with the vault key via Pack/Unpack in dbcore.go. This is the ONLY encryption path. Never encrypt/decrypt fields outside of it. +## Session & Key Architecture (DO NOT VIOLATE) + +**One session key, one salt, one source of truth.** + +- Session key: `v1984_master` in `sessionStorage` — 32-byte master secret, base64-encoded +- HKDF salt: `vault1984-master-v2` — used everywhere, no alternatives +- L1 = bytes[0..8], L2 = bytes[0..16], L3 = bytes[0..32] — all derived from `v1984_master` +- **webauthn.js** is the ONLY module that derives and stores the master key +- **topbar.js** is the ONLY module that clears it (on lock/logout/401) +- **crypto.js** is the ONLY module that encrypts/decrypts fields — shared between CLI and browser + +**Rules:** +- NEVER create a second session key (no `v1984_l2key`, no `v1984_foo`) +- NEVER derive keys with a different salt +- NEVER derive or store keys outside webauthn.js +- NEVER encrypt/decrypt outside crypto.js +- Registration = unlocked. One tap stores the master key. No second tap. +- `isUnlocked()` checks sessionStorage — if false, user is logged out + +**Shared JS (crypto/ directory):** +- `crypto/crypto.js` and `crypto/totp.js` are the source of truth +- Makefile copies them to `app/cmd/vault1984/web/` before building +- NEVER edit the copies in `web/` directly — edit `crypto/` and rebuild +- CLI (QuickJS) and browser (Web Crypto) use the same code + ## Key Files -- `L2_AGENT_ENCRYPTION.md` — WebAuthn L2 encryption spec +- `L2_AGENT_ENCRYPTION.md` — WebAuthn L2 encryption spec (SUPERSEDED by truncation model) - `docs/` — architecture docs - `app/cmd/vault1984` — main entry point +- `crypto/` — shared JS crypto (source of truth for CLI + browser) +- `cli/` — vault1984-cli (C + QuickJS + BearSSL) diff --git a/Makefile b/Makefile index a9f4d55..6057336 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ GOFLAGS := -trimpath all: app website app: + cp crypto/*.js $(APP_DIR)/cmd/vault1984/web/ cd $(APP_DIR) && go build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o vault1984 $(APP_ENTRY) @echo "built $(APP_BIN) (FIPS)" diff --git a/marketing/images/twitter-banner-v2.jpg b/marketing/images/twitter-banner-v2.jpg new file mode 100644 index 0000000..7ceb2d6 Binary files /dev/null and b/marketing/images/twitter-banner-v2.jpg differ diff --git a/marketing/images/twitter-banner-v2.png b/marketing/images/twitter-banner-v2.png new file mode 100644 index 0000000..79b64da Binary files /dev/null and b/marketing/images/twitter-banner-v2.png differ diff --git a/marketing/images/twitter-banner.jpg b/marketing/images/twitter-banner.jpg new file mode 100644 index 0000000..a85543a Binary files /dev/null and b/marketing/images/twitter-banner.jpg differ diff --git a/marketing/images/twitter-banner.png b/marketing/images/twitter-banner.png new file mode 100644 index 0000000..167a892 Binary files /dev/null and b/marketing/images/twitter-banner.png differ diff --git a/marketing/vault1984-twitter-schedule.html b/marketing/vault1984-twitter-schedule.html index ff12960..cc4736f 100644 --- a/marketing/vault1984-twitter-schedule.html +++ b/marketing/vault1984-twitter-schedule.html @@ -13,9 +13,9 @@ .week-label { font-size: 11px; text-transform: uppercase; letter-spacing: 2px; color: #555; margin-bottom: 16px; border-bottom: 1px solid #222; padding-bottom: 8px; } .posts { display: flex; flex-direction: column; gap: 16px; } .post { display: grid; grid-template-columns: 180px 1fr 280px; gap: 20px; background: #161616; border: 1px solid #222; border-radius: 10px; padding: 20px; align-items: start; } - .post.posted { border-left: 3px solid #1d9bf0; } + .post.posted { border-left: 3px solid #1d9bf0; } .post.scheduled { border-left: 3px solid #555; } - .post.pending { border-left: 3px solid #333; opacity: 0.7; } + .post.pending { border-left: 3px solid #333; opacity: 0.7; } .meta { display: flex; flex-direction: column; gap: 6px; } .date { font-size: 15px; font-weight: 600; color: #fff; } .time { font-size: 13px; color: #888; } @@ -24,7 +24,6 @@ .zh { background: #3a1a1a; color: #f07b7b; } .de { background: #1a2e1a; color: #7bf07b; } .pt { background: #2e2a1a; color: #f0d07b; } - .es { background: #2e1a2e; color: #d07bf0; } .status { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; margin-top: 8px; } .status.live { color: #1d9bf0; } .status.queued { color: #aaa; } @@ -33,9 +32,7 @@ .hashtags { color: #1d9bf0; } .city-img { width: 100%; } .city-img img { width: 100%; border-radius: 8px; display: block; aspect-ratio: 16/9; object-fit: cover; } - @media (max-width: 800px) { - .post { grid-template-columns: 1fr; } - } + @media (max-width: 800px) { .post { grid-template-columns: 1fr; } }
@@ -70,7 +67,7 @@ Free till May 1st.























