Deferred hardening: /capture secret, ja/ko/es/fr UI, compiled dist automation - #6
Merged
Conversation
Add full UIStrings sets for ja/ko/es/fr to the table (previously they fell back to English). Dates use Intl.DateTimeFormat per locale; labels, signal/device strings, figures, colophon, archive, and email subject are translated. en and zh-TW unchanged. Resolves the "English content inside English chrome" gap for non-CJK/non-English readers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Host + Content-Type gates block a cross-origin webpage drive-by, but that rests entirely on browser CORS behaviour and does not stop a non-browser local process (e.g. another OS user on a shared Mac) from POSTing fabricated captures. Add a per-install shared secret: the extension holds it (baked in at `npm run build:ext` via an esbuild define, read from the Keychain `browstack-capture`) and sends it as `X-Browstack-Token`; the server validates it constant-time and fails closed. - src/captureSecret.ts: Keychain-backed secret (get/ensure/cached/check), sha256 + timingSafeEqual. - server.ts: /capture requires a valid token after the Host and content-type gates (401 otherwise); createBrowstackServer accepts an injectable getCaptureSecret for tests. - extension/background.ts: sends X-Browstack-Token (a compile-time __BROWSTACK_CAPTURE_TOKEN__). - scripts/build-ext.mjs: provisions the secret and injects it via `--define`; dist/ stays gitignored. - tests + grep-gate updated. BREAKING for extension users: after upgrading, run `npm run build:ext`, reload the unpacked extension in chrome://extensions, and kickstart the receiver — otherwise the old bundle's POSTs are rejected (401). Chrome-history ingest and non-extension flows are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The LaunchAgents ran tsx off a node path baked in at install time. Two fragilities: a node upgrade orphaned the baked path, and better-sqlite3's native ABI could drift from the running node and crash-loop the resident receiver. - tsconfig.build.json + `npm run build`: emit runnable dist/. - scripts/run-with-node.sh: LaunchAgents exec through a wrapper that resolves node at runtime (launchd PATH lists the install-time node dir first), so a node upgrade no longer strands a stale path. - install-weekly.mjs: build + `npm rebuild better-sqlite3` before install, and a second preflight that refuses to install if the target node can't load the jsdom-backed compiled pipeline (fail loud now, not silently every Saturday). Serve plist now runs dist/server.js, not tsx. - weekly.mjs: run compiled dist/ entries via the resolved node, no npm/tsx indirection. jsdom's dep tree does require(ESM), unflagged only on Node 20.19 / 22.0+; on 20.17-20.18 or 21.x it opts in with --experimental-require-module, gated on the resolved node's version so a future node that drops the flag isn't handed it. - engines.node >= 20.17 (the floor where the flag exists); AGENTS.md gains the run-model note: editing src/ has no effect on the automation until `npm run build`. Interactive `npm run <ingest|serve|...>` still use tsx; only the installed automation runs compiled. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the three items we deferred from the archive/security work. Each is an independent commit.
①
POST /captureper-install secret (46ede54)The extension's landing endpoint was gated only by the Host + Content-Type checks. Adds a per-install capability secret (Keychain
browstack-capture, sha256 +timingSafeEqual, fail-closed) that the extension presents on every capture. Mirrors the archive-token design.scripts/build-ext.mjsmints/reads the secret and bakes it into the bundle at build time; a new security gate asserts the comparison stays constant-time.Breaking for extension users: requires
npm run build:ext+ reloading the unpacked extension, or captures return 401.② UI locales ja / ko / es / fr (
8e7b5da)Extends the
i18n.tsUI string table with Japanese, Korean, Spanish, and French, usingIntl.DateTimeFormatfor locale-correct dates. Content is still generated in the reading language; image prompts stay English.③ Compiled
dist/under a runtime-resolved node (ba4b1e4)The LaunchAgents ran tsx off a node path baked in at install time — a node upgrade orphaned the path, and better-sqlite3's native ABI could drift and crash-loop the resident receiver.
tsconfig.build.json+npm run buildemit runnabledist/.scripts/run-with-node.sh: agents exec through a wrapper that resolves node at runtime.install-weekly.mjs: builds +npm rebuild better-sqlite3before install; two preflights refuse to install if the target node can't load better-sqlite3 or the jsdom-backed compiled pipeline. Serve plist runsdist/server.js.weekly.mjs: runs compileddist/entries via the resolved node. jsdom's tree doesrequire(ESM)(unflagged only on Node 20.19 / 22.0+); on 20.17–20.18 or 21.x it opts in with--experimental-require-module, gated on the resolved node's version.engines.node→>=20.17; AGENTS.md documents the run model (editingsrc/needsnpm run buildto reach the automation).Deploy step: re-run
npm run schedule:weeklyto rebuild + reinstall the agents on the compiled path.Verification
npm run build,npm run typecheck,npm run security-gatesgreen; 20/20 tests pass (node 20).cli.jsandrender/email.js(the jsdom path) load and run with the version-gated flag; the email render produced issue №6 (3 deep reads, 4 social echoes) without sending.Not auto-merged
Merge and the two deploy steps above are left for you.
🤖 Generated with Claude Code