grithdocs

The quarantine digest

What happens to a call that scores between 3.0 and 8.0, who answers it, and what happens if nobody does.

A call that scores above 3.0 but not above 8.0 is neither clearly safe nor clearly hostile, so grith does not guess. It holds the calling thread at its kernel stop, writes the call to the digest queue, and asks you.

Resolution is real-time and blocking. There is no batching, no schedule, no delivery interval: the syscall is paused until somebody answers or the review times out.

What is held, and what is not

Only the thread that made the syscall. Its siblings keep running - a Node-based agent carries on rendering its UI while the prompt is on screen. grith does not stop the process tree.

That matters both ways. It keeps a supervised tool responsive, and it means a multi-threaded tool can do other work on other threads while a decision is pending.

Who does the asking

In an interactive grith exec session, the prompt is an overlay in the terminal:

KeyAction
aAllow this request; the exact target stays allowed for this session.
dBlock it; identical retries are blocked without asking again for 60 seconds.
lAllow and save a permanent rule for this exact target. Sensitive targets are never saved.
sAllow a directory, for the operations you pick, this session only.
bBlock a directory for the rest of the session - no more prompts for it.
tDeny and stop the supervised tool.
iShow the raw arguments and the request id.
escDeny this request.

s and b are offered only when a scope makes sense for the call. A call that was denied outright - scored above 8.0 - is shown as a read-only record of what was blocked, with no actions on it.

Nothing here outlives the session unless you saved it with l.

The dashboard at http://127.0.0.1:3141 shows the same queue live, and grith digest review walks it from the terminal.

If a prompt is still waiting after about 15 seconds, grith raises a desktop notification and the dashboard updates live.

When the answer never comes

The review timeout is 300 seconds (supervisor.freeze_timeout_seconds). On expiry the queue does one final re-read - so an answer given elsewhere in the last moment is not stomped - and then records the item as denied with the action auto_deny_timeout.

A timeout is treated exactly like a deny: the held syscall returns EPERM and the thread resumes. The process is not killed unless you explicitly chose deny-and-terminate.

Headless sessions fail closed

With no terminal and no dashboard overlay there is nobody to answer, so grith does not wait 300 seconds to find out. A queued call in a non-interactive session is denied immediately and recorded as informational, with a log line telling you the three ways out: allowlist it in the profile, run with a terminal, or pass --allow-queued.

--allow-queued flips that to allow-and-log - except for calls that were queued because of containment or taint. Those still fail closed.

⚠️This is the usual CI surprise

A pipeline step that legitimately runs docker build under grith exec will be denied, because the delegating spawn escalates into the queue band and there is no reviewer. See running in CI.

Short-circuits that resolve a call without asking

Four things can settle a queued call before a prompt is ever raised:

  • Deny replay. An identical call denied within the last 60 seconds is denied again.
  • Approve replay. An identical call approved within the last 60 seconds is allowed again - disabled while containment is active, and it earns no reputation credit, so a loop cannot launder trust.
  • Control-socket replay. An answer about a session D-Bus, X11 or tmux connection is remembered for the session. This one is not disabled under containment.
  • Scope drain. If a grant you made on another prompt now covers this target, the call resolves against it with none of the side effects of a manual approval.

What is stored

The queue is its own SQLite database next to the audit log. Each item holds the call type, an argument summary, the decision reason, the composite score, the full filter breakdown, and - once resolved - the action taken and who took it. Where a DLP filter matched, the argument summary is redacted before it is written.

Items are Pending, Approved, Denied, Expired or Escalated. Only pending and escalated items are actionable, and informational items never are.

ℹ️The `[digest]` config block does nothing

interval_active, interval_idle, delivery and max_queue_size parse and are never read. There is no scheduled or batched delivery to configure.

See also

Last updated: 2026-08-24Edit this page on GitHub →