1 How a byte becomes a record
mikebrandon edited this page 2026-09-04 11:52:54 +00:00

How a byte becomes a record

Five gates, in this order, all of them before the write. That last part is the whole design: a secret that reaches disk and is scrubbed before upload has already been written to a file a backup client can read. So the pipeline runs at ingest, not at egress.

collector -> admission -> L0 scope -> L1 structural -> hold -> L2 secrets -> L3 pixels -> store
            (declared?)   (in scope?)  (safe to look?) (visible?) (in the text?) (in the image?)

Admission — is this event even a thing?

The gate joins the incoming event type against the generated registry and the grants table. An event type nobody declared in contracts/ is refused over the wire, so a buggy or tampered collector cannot invent a new kind of data to send.

A collector also cannot send a signal the person has not granted: it learns its own grant from the gate's reply to a heartbeat. That is why the screenshot collector takes no pixels at all until screenshots are switched on, rather than capturing and letting the gate discard the result. Every collector must read that reply, or the pipe desynchronises.

L0 — scope

Is the app or site in scope? Two modes live in the settings table: deny_by_default (the default) and allow_by_default. Deny rules beat the blanket allow in both. An absent setting means deny, so an old store cannot widen its own scope by being upgraded. Switching modes is itself written to the gate log as a decision.

One subtlety that was a live bug: collectors report the full image path (C:\Windows\System32\cmd.exe), and rules are written as file names (cmd.exe). The comparison handles both. A rule containing a path separator means that one path only.

L1 — structural suppression

Cheap, certain refusals that need no content inspection: a password or other secure field has focus, capture is paused, or the window is one the policy never looks inside. This gate is structural-first on purpose — the reliable way not to capture a secret is not to read the field, rather than to read it and then decide.

The hold — no verified indicator, no content capture

Before any content is written, the gate asks whether the indicator has proved itself visible and recent. Both halves matter: a hung indicator that reported "visible" once and then froze used to keep content flowing forever, so the check requires a report inside the last five seconds. If the indicator cannot prove itself, the gate holds and records a gap — an observable absence, not silence.

L2 — secrets in text

Pattern detection over anything textual, before it is stored: tokens, keys, connection strings, private keys. Measured against a corpus that includes real command lines from a working machine: 0% leak, 1.9% over-redaction against a 3% bar. The one false positive is a folder name that genuinely reads as password-like. It was left alone deliberately — loosening a password detector to tidy up a path is the wrong trade.

L3 — pixels

Redaction inside captured images: secure-field rectangles and anything else the policy marks. When a frame is downscaled to fit the transport, redaction rectangles are scaled outward, so no redaction ever shrinks under rounding.

Then it is written, and what it is written as

Every row carries a gate_state:

gate_state Meaning
pass stored as captured
redacted stored, with something removed, and the row says so
suppressed timing only — the event happened, the content did not survive

Content fields are marked x-content in the schemas, and suppression strips exactly those fields. This is what makes a denied app absent rather than masked: no pixels are captured for it at all, because capture is per-window rather than per-screen, and its row keeps only timing.

Getting that marking right is fiddly and worth checking when you add a field. The clipboard's keyed fingerprint was once not marked as content, which meant a denied app's copies stayed linkable to an allowed app's — a hash is not a safe thing to keep just because it is not the text.

Payloads and blobs are then encrypted with AES-256-GCM under a hardware-wrapped key. The index is deliberately not encrypted, and every document says so in the same words: timestamps, event types and app ids stay readable so the person can be shown what exists without the process having to decrypt it first. Deletion tombstones stay as a literal {}, so a deleted record is visibly deleted from outside the process.

And then the person is told

A capture from an app nobody has been told about always raises a card on screen, at every rung of the verbosity ladder. The ladder climbs on cards shown (10, then 30, then 60), never on elapsed time — sitting idle for a week does not earn you silence. An explicit "quiet this" beats the ladder; a newly seen app beats the quiet.