Masking & Streaming Enforcement

Detected sensitive spans have to be redacted inline, in a live byte stream, without buffering an entire command's output and without corrupting whatever structure the surrounding data has (JSON-RPC frames, terminal escape sequences, partial lines). This chapter covers the mechanics of that.

The streaming problem

A naive approach — buffer everything, scan once, emit — defeats the purpose of a terminal or an MCP transport: output has to appear as it's produced. But a secret can straddle the boundary between two write calls (half of an API key in one chunk, half in the next), so the engine cannot simply scan-and-emit each chunk independently either.

%%{init: {"theme": "base", "themeVariables": {"fontFamily": "-apple-system, Segoe UI, Helvetica, Arial, sans-serif", "primaryColor": "#eef2f7", "primaryTextColor": "#0b0b0b", "primaryBorderColor": "#5b6b7c", "lineColor": "#5b6b7c", "secondaryColor": "#eef4ff", "tertiaryColor": "#f4f3f0", "clusterBkg": "#f9f9f7", "clusterBorder": "#c3c2b7", "edgeLabelBackground": "#fcfcfb", "actorBkg": "#eef2f7", "actorBorder": "#5b6b7c", "actorTextColor": "#0b0b0b", "actorLineColor": "#5b6b7c", "signalColor": "#52514e", "signalTextColor": "#0b0b0b", "labelBoxBkgColor": "#eef4ff", "labelBoxBorderColor": "#5b6b7c", "labelTextColor": "#0b0b0b", "loopTextColor": "#0b0b0b", "noteBkgColor": "#fdf3d9", "noteBorderColor": "#c98500", "noteTextColor": "#3d2f00", "activationBorderColor": "#5b6b7c", "activationBkgColor": "#eef4ff", "sequenceNumberColor": "#ffffff"}}}%% flowchart LR In["Incoming bytes\n(command output / JSON-RPC)"] --> Pending["Pending buffer\n(max 64 KB)"] Pending --> Scan["Scan for findings,\nsorted by offset"] Scan --> Straddle{"A finding straddles\nthe write boundary?"} Straddle -- yes --> Retain["Hold back the tail\n(≥ longest matchable span,\ndefault 16 KB)"] Straddle -- no --> Emit["Emit safely up to\nthe safe boundary"] Retain --> Idle{"Idle for 20 ms\nwith no new data?"} Idle -- yes --> ForceEmit["Force-flush\n(don't withhold short values\nlike interactive prompts)"] Idle -- no --> Pending

Mechanics and constants

  • Offset-based substitution, single pass, findings sorted by position; where findings overlap, the longer match wins. An empty placeholder leaves the underlying region unchanged (a no-op substitution).
  • Retained tail: bytes held back at the safe-emit boundary, sized to at least the longest matchable span the active detector set can produce (default 16 KB) — this is what prevents a secret split across two writes from slipping through half-masked.
  • Max pending buffer: 64 KB. Under memory pressure, the buffer is cut at the end of a straddling finding — never mid-value — so bounding memory usage never costs a disclosure; it costs, at worst, an earlier forced flush.
  • Idle flush: 20 ms. Without this, a short value (an interactive prompt, a shell echo) that never reaches the tail-size threshold on its own would be withheld indefinitely. This is a deliberate trade-off in favor of terminal responsiveness.
  • Flush drains everything — a final/explicit flush has no retained tail left over.
  • Placeholders are substituted inline — a masked PEM block renders as -----[MASKED:PRIVATE_KEY]-----, preserving the surrounding structure.

Performance

  • A synthetic-detector benchmark (BenchmarkStreamingWriter100MB, a repeatable Go benchmark) measures throughput around 1.5 GB/s for the masking mechanism itself on the hardware it was run on.

Continue to Policy Engine & Trust Chain.

results matching ""

    No results matching ""