colonizer
install
docs Prompt screening

#Prompt screening

A colony reads attacker-controlled repositories and writes text that ends up in a pull request. A page, a commit message or a README it reads can carry instructions that ride outside what a reviewer sees: Unicode tag characters encoding ASCII inside an innocent-looking line, bidi controls reordering the line a reviewer reads (Trojan Source), variation selectors smuggling one byte each. Screening is the harness's answer, and it runs at the only point where it can stop the damage: publish time, on the mothership, before anything leaves the machine.

#What is screened, and where

There are two scanners, on the two sides of the trust boundary, on purpose:

Colony-side preflightMothership-side screening (this page)
Modulethe claude-code agent module's scan and scan_command settings (other agent modules have none)the screen module, provider promptdecode
Whenbefore the agent startsafter the colony ends, before push/PR
Seesthe whole base repositorythe colony's final diff, the PR title and the PR body
Scannerone you mount (scan_command)built in: a code-point decoder
Why therefull scanners run parser-rich code over hostile input; that belongs on the disposable side of the boundary, where the credentials never arethe publish gate runs where the GitHub token lives, so it must be tiny, deterministic and local — no scanner code, no network, no model

The built-in decoder classifies three families and nothing else:

The false-positive budget is deliberate: emoji ZWJ sequences and skin tones, CJK, and Arabic or Hebrew prose that uses no direction controls — the marks above are fine — all pass clean. What the decoder deliberately does not do is read the change for meaning — a plain-language injection that uses no hidden code points is not a finding here.

Each scan keeps at most 50 findings, and a decoded payload is cut at 200 characters (MAX_FINDINGS and MAX_DECODED in crates/colonizer/src/screen.rs).

#What is scanned, exactly

The diff (origin/<base>...HEAD, the merge-base diff the pull request would show — added lines only, new-side line numbers, quoted paths decoded, binary sections skipped), the pull request title, and the pull request description body. The title is scanned as its own location, pr.md:title, because it is not just the top of the description: it becomes the commit subject too, and the commit body is Colonizer's own trailer — so screening the title screens the whole commit message. Description locations are character offsets from the top of the body.

The diff read runs under a 30-second deadline like the publish's other git calls, and carries a 16 MB cap. A branch over the cap fails the publish in both nonzero modes: screening will not vouch for a diff it has not seen all of. Split the branch, or set the module's mode to off to publish unscreened.

#Off, warn, block

The screen module is off until you configure it, like notify. Once on, its publish setting decides what a finding does (default warn):

Either nonzero mode scans once per publish attempt, after the commit (so the diff is final, any restack included) and before the push (the first external effect), and records one screening event on the colony's event log — {mode, outcome, findings} where each finding is only its location, class, severity and decode. The event never reaches the runner, and the notify webhook carries none of it: the webhook fires off session status changes, not event payloads.

#Pointing a custom scanner

The built-in decoder is intentionally not extensible: if you want a full scanner (a vendored preflight scanner, a secret scanner, a policy engine), keep it on the colony side. The claude-code agent module has two settings for this: scan (off, warn or block, default off) and scan_command, a command resolved inside the colony, for example a scanner you mount through Plugin directories. It runs inside the microVM before the agent starts. warn reports findings and carries on; block ends the colony when the scanner exits non-zero. The module itself calls this advisory, not a security boundary. Find both in Settings → Modules → Agent. The other agent modules (Codex, Pi, OpenCode, Grok Build, Hermes, ACP) have no preflight scan yet.

#Not done yet