Repository navigation
feat: async gray-zone approval system + Approvals TUI - #4
Merged
Merged
Conversation
Add an opt-in, async (non-blocking) approval path for gray-zone (default-deny) commands. Explicit deny rules stay unoverridable hard blocks; only Rule=="default-deny" becomes approvable. Backend (internal/approval): injection-safe Generalize (fully \A..\z anchored, [ \t] never \s, byte-level ASCII tokenizer, interpreter/priv/ destructive denylists, safe-prefix default), split-engine Authorize (grants consulted only in the default-deny branch so they never shadow an explicit allow/deny), session grant store (flock+atomic, host-bound, TTL, once consumed under lock), pending/responses with >=96-bit ids, O_EXCL and req_digest (resolution is notification-only; run re-derives via Authorize on re-run). Wiring: run returns exit 7 on NeedsApproval (default-off => unchanged exit 6); multi-host preflights before executing; new operator CLI approval ls/grant/deny + session end, agent-readable approval status/wait. policy exports RuleDefaultDeny + reserves __agentssh_approval group. audit gains approval_requested/granted/denied + 4 omitempty fields mirrored into Record and canonicalRecord (old chains still verify). Verified locally: gofmt/vet/build clean, go test -race ./... all green. Docs: docs/plans/approval-system.md (spec, v1.1 with Codex review fixes), approval-system-codex-review.md, TUI approvals prototypes. TUI tab and overview.md update land in a follow-up commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an Approvals top-level tab to `agentssh tui` so the operator can adjudicate gray-zone approval requests without leaving the console (previously CLI-only via `agentssh approval`). Design (docs/plans/tui-approvals-final.html): one line per request, no per-row key column, and a single "consequence-first" footer that — for the focused request — states in plain English what an [h] host-allow would free (`systemctl status *`), or why host is unavailable (sudo) / exact-only (pipe). The list polls pending/ on a 1.5s tick so the queue and a status-bar "N pending" badge stay live from any tab. Keys o/s/h/d decide via the same approval.ApplyDecision wiring as the CLI (reloads inventory+policy from disk; the TUI is the operator console so no extra master/TTY gate). Host scope on a privileged request is refused in place without writing. English UI throughout. internal/tui/app.go gains the third entry tab (tab/1-3 cycle, %3), routing, polling, help, and the badge. New ChannelTUI constant. overview.md §5/§10 updated: the optional async approval is shipped (default-off, exit 7, TUI adjudicates); TUI is no longer audit-only. Tests: render + cursor-driven consequence + privileged-host refusal + disabled-off-note. gofmt/vet clean, go test -race ./... all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…system Address correctness and safety issues found in the async gray-zone approval review (git diff main...HEAD). Security-critical: - F1-F3: harden safe-prefix host generalization. Remove journalctl safe-prefix case; force exact for git --output/-o and kubectl identity options (--as, --as-group, --token, --kubeconfig, --server) and other side-effecting flags; pin git/kubectl first positional so grants do not widen across resources/revisions. - F4: make the O_EXCL response resolve the first mutating step in ApplyDecision; only the winner applies grants and audit side effects. Correctness: - F5: abort TUI approval decisions on inventory/policy load errors instead of saving an empty policy. - F6: gate approval TUI verdict keys, queue polling, and pending badge on runtime.Enabled. - F7: session end <base> removes derived base@host approval files. - F8: when approval is disabled, evaluate the full policy including persisted __agentssh_approval host rules (do not strip them). - F9: include Matcher.Promotable in the matcher SHA-256 digest. - F10: corrupt/empty/ID-mismatch response files return ErrCorruptResolution instead of wedging status/adjudication. - F11: drop single-host session-file binding; grants carry Host. - F12: dedupe pending on SessionID+Host+CmdSHA256; reap resolved pending/response files past TTL. - F13: invalid optional approval runtime config warns (English) and degrades to disabled instead of exiting. - F14: policy test is a static policy-engine check, no session reads. - F15: approval-pending human output is English. Mechanical: - audit record for not_run preflight-blocked targets - add cmd to runResponse and populate it (incl. approval-pending) - add approval.ChannelExit constant for exit-channel audit records All F1-F15 plus mechanical items covered by tests; go build ./... and go test ./... pass. See docs/plans/approval-review-fixes.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approvals tab polish: - Replace the hand-rolled fixed-width row renderer with the shared responsive renderTable: aligned ID/HOST/COMMAND/KIND columns that span the frame (COMMAND takes the slack, HOST is no longer truncated), a header row, and full-row selection highlight — matching the other tabs. - Add an Enter-driven verdict chooser that replaces the o/s/h/d footer hints with a selectable once/session/host/deny menu: ←/→ (or tab) move, enter/space apply, esc cancel. While open the tab captures the keyboard so esc/enter aren't treated as quit/tab-nav. - The menu omits host for non-promotable (privileged) requests, and pins to the targeted request across background polls so a reload can't misapply the verdict. The direct o/s/h/d shortcuts still work. Also seed approval data in the e2e script: enable approval in the demo policy and produce a six-request pending queue via `run` (exit 7 in the gray zone, no SSH), covering every KIND/consequence, and refresh the manual checklist for the current 3-tab layout. Tests: chooser open/cancel/nav, host omitted on privileged, and apply resolving a real pending request. go build ./... and go test ./... pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First release to include the async gray-zone approval system (run exit 7 + operator adjudication via CLI and the TUI Approvals tab). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the async gray-zone approval system: when a command lands in the default-deny gray zone,
runreturns exit 7 and writes a pending request; an operator adjudicates it out-of-band via the CLI (agentssh approval grant/deny/ls/status/wait) or the new Approvals tab in the TUI. Grant scopes are once / session / host.Backend
~/.agentssh/approvals/(pending / responses / sessions), O_EXCL atomic claims, SHA-256 request/matcher digests, session-file flock.exact/safe-prefix/prefixhost-grant modes) with anchored RE2 matchers; safe-prefix hardened so identity/side-effecting flags force exact.TUI — Approvals tab
[h]host-allow would free.Review + polish
docs/plans/approval-review-fixes.md.Testing
go build ./...andgo test ./...pass. New tests cover the safe-prefix escalations, decision atomicity, corrupt-response handling, and the Enter chooser (open/cancel/nav, host-omitted-on-privileged, apply-resolves-request).🤖 Generated with Claude Code