Repository navigation
Conversation
…known values by name, detected ones by fingerprint, kept only where a workstream says so - secrets/: a detector after atuin's and gitleaks' patterns, exact matching of every value codeoid knows (credentials, accounts, endpoints, OAuth tokens, the daemon's keys), HMAC fingerprints, per-tool-call verdicts (a command that prints credentials keeps no output, a credential file's write no content), a streaming scrubber for replies, values found by shape matched exactly from then on (memory only) - applied to every message a session stores or sends, the canonical history, memory, share bundles, and MCP results through the hub - captureSecrets: "vault" keeps detected values encrypted per workstream; secret.list / reveal / forget (owner-only, audited); codeoid secret ls | reveal | forget | promote | scrub - docs/secrets-and-memory-design.md (S1–S4), CONFIGURATION.md, an e2e journey Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ackends as written, code isn't taken for a secret, and every write path is scrubbed - a stateless backend's current prompt is the user's own (Session#historyForTurn) - detection: identifiers, dotted paths and calls are code; env-style values stop at the next word; a private key only with armour's body, found linearly, as is curl -u; .netrc; codeoid's own token formats; the budget checked inside each pattern - exact matching in one Aho-Corasick pass, never inside a reference (scrubbing is idempotent); only distinct formats are remembered, per tenant, as whole tokens, apart from the known set - streaming holds from where a credential may start, judged with the line so far, never inside a spaced known value, and flushes before a tool call - scrubbed now: a hook-rewritten input, the turn index, a fork's seeded history, pending approvals by verdict, a session's last error, settled dispatch tasks, routine run outputs, MCP results through the relay; pinned credential files and withheld outputs' spill files are dropped - verdicts: source / . / cp and *.pem no longer withhold; $HOME paths, git show/diff, dd if= and the unparsed fallback do - codeoid secret scrub: no budget, nothing remembered, unchecked text kept and counted, rotated segments, databases rebuilt so old text is gone - key files written whole and linked into place, permissions checked; GCM tag length pinned; the canonical scrub fails closed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es found, every backend's credential-file writes withheld - detection: boundaries checked in code instead of leading lookbehinds (a 0.6 MB file: 726 ms to 30 ms on Bun 1.3.13), keyword-first name patterns, a linear YAML value - found now: unquoted credential headers, YAML values before a comment and with '=', URL query tokens, mysql -p / sshpass -p / docker login -p, and snake_case or camelCase values of env-style assignments (spaced code assignments stay code); values stop at '&' and a literal \n - remembered values indexed by prefix (no rebuild when one is learned); reference overlap by binary search; deadlines inside both exact passes - known values only as whole tokens; environment values only when they look like a secret - verdicts: export -p / declare -x, env with options only, echo $SECRET, .env globs and braces, commands run through ssh / docker exec / kubectl exec; Codex, pi and apply_patch writes of credential files; drop covers argv - structured MCP output scrubbed string by string; dispatch events, audit details and reclaimed tasks scrubbed; offline scrub leaves live tasks alone - the stream judges a piece with the last 512 characters of its line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on every engine, fewer false positives - a value stops at & only before another name=, at \\ only at an escape - the env pattern finds its name and separator, then its value with a sticky regex: the combined one ran uncompiled on Bun 1.3 (216 ms to 1 ms over 2 MB); JWTs found in one linear pass (repeated eyJ: 3.2 s to 47 ms) - known and remembered values match after a JSON escape or a %XX - unquoted header and query values only when they look like a key; mysql's -p only as mysql's own; sshpass -p and login -p right after the program - docker exec / compose exec / kubectl exec commands judged with their quoting; ssh host -- CMD Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
pr-shepherd: CI is green on a1a810e (daemon-check, e2e, web-check, Socket x2). I made no changes: no failures, the branch is level with main (0 behind), and every commit subject passes the prefix check. Oracle hasn't reviewed yet. Asked for review in #javelin-pr. |
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.
Codeoid stores everything a session does: its transcript, the history other backends are seeded from, the scrollback clients replay, memory, share bundles. Until now a token printed by
gh auth token, acat .env, or a key pasted into a prompt ended up in all of them in plain text. After this change, none of those hold a secret's value.Design for this and the next three phases:
docs/secrets-and-memory-design.md. The user guide is indocs/CONFIGURATION.md, under "Secrets in what codeoid stores".What changes for a user
‹credential:gh-highflame›, and the value stays where it already lives.DB_PASSWORD. It appears as‹secret:github-token:3f9a2c1b›, and the same secret gets the same reference everywhere. A distinct token format is also remembered for the rest of the daemon's run, per tenant, so a bare repeat is caught.gh auth token,printenv,op read,cat .env,kubectl get secret, …) is stored without its output.captureSecrets: "vault": encrypted, kept to that workstream and tenant, and revealed only to the owner's own token, with an audit entry.codeoid secret ls | reveal | forget | promote | scrub.scrubrewrites data stored before this change, and refuses to run while the daemon is up.secrets.beforeModel, on by default).Limits (documented)
~/.claude/projects).Verification
mainon this machine.e2e/specs/secrets.spec.ts) passes on a real daemon. It covers the web UI, transcripts on disk, a reload, andcodeoid secretreveal and forget.For agents: where things are
src/daemon/secrets/:detect.ts: patterns after atuin and gitleaks (MIT). Boundaries are checked in code; private keys and JWTs are found in linear passes; the env value is a sticky second match.known.ts: Aho-CorasickKnownSecrets, andLearnedSecretsindexed by prefix. Both match whole tokens and never inside a reference.scrub.ts:ScrubberandStreamScrubber.verdict.tsandfiles.ts: what of a tool call is stored.vault.ts: AES-256-GCM, bound to tenant, workstream and fingerprint.service.ts:SecretServiceandaddDaemonKnownSources.scrub-stored.ts: the offline scrub.session.ts:#scrubMessagein#persistAndBufferand#commitStreamed.#recordedToolInputby verdict, at tool_start, approvals and hook-rewritten inputs.#historyForTurnsends the raw current prompt.scrubTurn(partial) andscrubSeed(whole).providers/canonical.ts,memory/engine.ts(scrubText),share/pack.ts,mcp/hub.ts(setResultScrub,scrubRelayed),mcp/proxy.ts.store.ts:captured_secrets,knownSecretValues,scrubTextfor settled dispatch tasks, events and audit.routines/store.ts,server.tsandcli.ts. Onlycodeoid startcreates the service (secretsConfigDir).secret.list,secret.reveal,secret.forget. Config:secrets.*andworkstreams.items[].captureSecrets.🤖 Generated with Claude Code