feat(secrets): streaming CoT-redaction prototype + feasibility (roadmap-v0.6 E.4)#84
Merged
Conversation
…ap-v0.6 E.4) Adds crustcore_secrets::token_stream::TokenRedactor + the supporting Redactor::longest_dangling_prefix / max_needle_len. TokenRedactor buffers streamed model tokens to a redaction boundary (newline), scans with the existing Redactor, and emits only fully redacted chunks — a secret split across tokens or lines is caught before any byte reaches the user (invariants 2, 3). A forced (no-boundary) emit retains only the longest dangling needle-prefix (the start of a not-yet-finished secret), keeping the buffer bounded without ever emitting a partial secret. 6 red-team tests; docs/cot-streaming.md concludes token-level CoT streaming is feasible behind the existing reveal_reasoning opt-in, with the latency bound (<500ms) and the one constraint (boundary char not inside a secret). Analysis + a pure core; zero nano impact. `cargo xtask verify` green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds token_stream::redact_stream — the transport-agnostic streaming pipeline the live CoT loop runs: pulls each deframed provider token, hands every redacted chunk to an emit sink in order, flushes the buffered tail at end-of-stream. Streamed tokens are model output (untrusted, invariant 7); every chunk passes through the redactor before emit (invariants 1-3). A secret split across any number of tokens is still caught (TokenRedactor buffers to a safe boundary and only emits a leak-free prefix). This makes E.4 a real pipeline, not just the analysis doc + the buffering core: the only remaining live inch is the SSE/websocket socket producing the tokens, which lives in the daemon/net layer (this crate stays std-only). 2 new CI tests (split-secret end-to-end + empty stream); no lib.rs change (not a further contract-file edit); zero nano impact. verify green. 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.
Summary
Roadmap-v0.6 E.4 (analysis-first) — decides whether token-by-token chain-of-thought streaming is compatible with the redaction boundary. Conclusion: feasible, with a working prototype + a feasibility doc.
crustcore_secrets::token_stream::TokenRedactor— buffers streamed tokens to a redaction boundary (newline), scans with the existingRedactor, emits only fully redacted chunks. A forced (no-boundary) emit retains only the longest dangling needle-prefix suffix (Redactor::longest_dangling_prefix) — the start of a not-yet-finished secret — so the buffer stays bounded and a partial secret is never emitted.docs/cot-streaming.md— the design, red-team analysis, latency bound, the one constraint, and the wiring (live seam reuses the net token stream behindreveal_reasoning).CI core vs live seam
max_buffer+ a token).reveal_reasoning. No new#[ignore]test (the prototype is pure).Trust boundary
No unredacted secret reaches the user mid-stream (invariants 2, 3); the buffer is bounded (invariant 11); behind the existing
reveal_reasoningopt-in — no invariant relaxation. Adds two read-only methods toRedactor(length-only; leak nothing).Tests run
cargo xtask verify— green. Nano unchanged at 53.5%. Independent offmain.🤖 Generated with Claude Code