fix: six confirmed audit findings (token lifecycle, msg-drop, recall scaling) - #12
Merged
Conversation
…scaling) Adversarially-verified high-severity findings from a full-codebase audit. (The scarier "stuck-status" race claims were refuted on verification and are not touched here.) #1 Dropped message after interrupt-then-fast-send (session.ts). The consumer `finally` nulled #inputQueue/#consumerTask without the identity guard it already used for #query/#abortController, so an un-awaited interrupt() + a fast send() let the stale loop clobber the new loop's queue/task and the next push was silently dropped. Capture a loop-local queue/task snapshot and guard the nulls by identity. #2 Token never re-verified after handshake (auth.ts/server.ts/types.ts). An open socket honored an expired/revoked token forever. Carry `exp` into AuthContext, reject missing/expired exp in verifyToken (60s skew), and close 4003 on a per-message expiry check. (Instant revocation of a still -valid token still needs a periodic re-verify — tracked separately.) #3 Web reconnect replayed the dead JWT forever (ws.ts/connection.ts). Add a getToken() supplier called on every (re)connect open that re-exchanges the stored zid_sk_ key for a fresh JWT; fall back to the last token if none. +2 tests. #4 Vector recall had no cache despite the comment (memory/store.ts). Every recall re-read + re-decoded all embeddings and brute-forced cosine. Memoize the decoded matrix per workspace; invalidate on insert-with -embedding / setEmbedding. #5 Memory init was all-or-nothing (engine.ts). An embedder download hiccup nulled the whole engine, also killing FTS recall + usage persistence. Wrap embedder.init() in try/catch and run FTS-only (vector signal off) on failure; recall and the embed pump guard on the ready flag. #6 Unbounded session resume blocked startup (session-manager.ts, issue #6). Sort newest-first, cap to RESUME_MAX_SESSIONS, time-box to RESUME_DEADLINE_MS, and log what was left on disk. Typecheck clean; 554 daemon tests + 95 web tests pass; build OK; live smoke (auth+exp accepted, session.list) verified against the dev daemon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 14, 2026
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.
What
Fixes the six confirmed, high-severity findings from a full-codebase audit. Each was adversarially verified against the actual source before fixing (one skeptical verifier per finding, quoting the code). The scarier "stuck-status / race" claims from the first pass were refuted on verification and are deliberately not touched.
Fixes
session.tsfinallynulled#inputQueue/#consumerTaskwithout the identity guard it already used for#query/#abortController; an un-awaitedinterrupt()+ fastsend()let the stale loop clobber the new loop and the next push was silently dropped. → Loop-local queue/task snapshot + identity-guarded nulls.auth.ts·server.ts·types.tsexpintoAuthContext, reject missing/expiredexpinverifyToken(60s skew), close4003on a per-message expiry check.web/lib/ws.ts·connection.tsgetToken()supplier re-exchanges the storedzid_sk_key on every (re)connect; falls back to the last token. +2 tests.memory/store.tsrecall()re-read + re-decoded all embeddings and brute-forced cosine. → Memoize the decoded matrix per workspace; invalidate on insert-with-embedding /setEmbedding.memory/engine.tstry/catchembedder.init(), run FTS-only on failure; recall + embed-pump guard on a ready flag.session-manager.tsThe #2 + #3 pair closes the token-lifecycle gap that made the documented "kill a token → immediately lose access" guarantee false on long-lived connections. (Instant revocation of a still-valid token still needs a periodic re-verify — tracked as a follow-up.)
Verification
bun run typecheckclean.bun testglobbing the web vitest files reports false fails — those are the knownvi.advanceTimersByTimeAsync-under-bun incompatibility, green undercd web && bun run test.)bun run buildproducescli.js.expcheck accepted a valid dev token,auth.ok+session.listsucceeded, clean boot.Closes #6.
The remaining medium/low findings (input schema validation, WS payload/backpressure limits,
synchronous=NORMAL, shutdown ordering, retention/pruning,auddefault, empty-tenancy reject, search-snippet tenant scoping, deadretry.ts, Telegram client pruning) are filed as separate issues.🤖 Generated with Claude Code