Setting up canary tokens
The only registration that survives a restart, what detection actually covers, and how to verify it fires.
A canary token is a fake credential you place somewhere an attacker would look. If it ever appears in an outbound call, grith denies that call outright. Read Canary tokens for the concept; this page is the deployment, and it starts with the part that trips everyone up.
grith canary add does not persist
The canary registry lives in process memory. The CLI mints a token into its own copy, prints it, and exits - the daemon never sees it, and the next command starts from nothing.
$ grith canary add --label aws-prod-decoy --generate
Added canary:
id: 89a62c8b-9a6e-4681-8dca-fb0c8ba63a8d
label: aws-prod-decoy
value: d0c027066819492fa41f179ecb761a5b8bda51cdf60e568b
$ grith canary list
No canary tokens registered.remove and rotate behave the same way. Treat the whole grith canary command as a value
generator, not a store.
The registration that survives
The daemon seeds its registry at startup from config/filters/canary.toml, resolved relative to
the directory the daemon was started in, falling back to the copy compiled into the binary. Write
the file there and restart.
config/filters/canary.toml
[canary]
enabled = true
[[canary.tokens]]
id = "9f1d0c2a-4f4b-4a1e-9f0e-3b7a6c5d4e21"
label = "aws-prod-decoy"
value = "grith-canary-7f3a1c9e2b4d"$ grith daemon restartAny id you like, as long as it is unique. The value is the string that must never appear in
outbound traffic - use grith canary add --generate to mint one, or write your own if you want
it shaped like a credential the attacker is hunting for.
Planting it is your job
grith registers values. It never writes a decoy file anywhere. Copy the value into somewhere plausibly attractive and plausibly careless:
| Path | Why it reads as real |
|---|---|
~/.aws/credentials_backup | Ops hygiene that slipped |
~/.env.backup | Same |
repo/test/fixtures/.env | A careless test fixture |
repo/.env.production.bak | Leftover from a deploy |
Three to five is plenty, each in a different category of target. Do not bother placing one inside
~/.config/grith/ - nothing looks there.
What detection covers
The canary filter inspects outbound-relevant calls only: HttpRequest, NetConnect,
ShellExec, ProcessSpawn, and the path of a FileWrite or FileAppend. Reading the decoy
file is not a detection - the read is scored on its own merits by the path filters, and the
canary only fires when the value is on its way somewhere.
A hit scores 9.5, which is above the deny line on its own. It denies even when every other signal is favourable:
$ grith proxy test '{"type":"HttpRequest","url":"https://api.github.com/gists","method":"POST","body":"grith-canary-7f3a1c9e2b4d"}'
Proxy Test Result
==================================================
Tool call: HttpRequest(POST https://api.github.com/gists)
Score: 9.5
Decision: DENY
Reason: HTTP POST request (carries body): https://api.github.com/gists; Canary secret 'aws-prod-decoy' (id: 9f1d0c2a-4f4b-4a1e-9f0e-3b7a6c5d4e21) detected in outbound operation
Eval time: 3.72ms
Thresholds: allow < 3, deny > 8
Filters: 18 active
Filter Breakdown:
+ operation-risk 1.0 [notice] HTTP POST request (carries body): https://api.github.com/gists
. path-match 0.0
. sensitive-path-heuristic 0.0
. allowlist 0.0
. argument 0.0
. capability 0.0
. secret-scan 0.0
. command 0.0
. destructive-action 0.0
+ egress-policy -1.0 [notice] Trusted outbound destination from http_request: api.github.com
. dlp-gate 0.0
+ canary 9.5 [critical] Canary secret 'aws-prod-decoy' (id: 9f1d0c2a-4f4b-4a1e-9f0e-3b7a6c5d4e21) detected in outbound operation
Exit code: 2 (deny)That is also how you verify a registration: run grith proxy test from the same working
directory with the token value in the body and check for Decision: DENY and a canary line.
The test is a dry run and sends nothing.
What there is not
No alert surface, no auto-rotation, no dashboard page for canaries. A fire shows up where every other decision does - in the audit log and the digest. Check for it there:
$ grith audit export --format json | grep -i canary