Policy
~/.conn/policy.yaml (or the app’s selected configuration directory). An example file is written on first run.
Format (v0.3)
Section titled “Format (v0.3)”There are three kinds of rule.
| Kind | What it matches | Example |
|---|---|---|
pattern |
a regex over the text of one segment (a line is split on && ; |) |
pattern: 'kubectl delete' |
command (+args) |
the command that actually runs. \rm, /bin/rm, command rm, r''m, sudo rm and env X=1 rm all count as rm. args is a regex over the joined arguments |
command: rm / args: '(^|\s)-[a-zA-Z]*[rR]' |
protected |
globs over the target paths of delete / move / chmod commands, resolved to absolute paths against the shell’s cwd | '~/.ssh/**' |
And three switches.
| Key | Default | Meaning |
|---|---|---|
opaque |
confirm |
the verdict for constructs whose effect cannot be inspected (sh -c, eval, source, xargs, find -delete/-exec, python -c, ssh, $( )) |
isolate_dangerous |
true |
when a confirm/deny-level segment shares a line with other segments, deny instead of asking. One approval = one dangerous action |
require_intent |
true |
an agent’s ENTER without a one-line intent does not run |
Access does not depend on window focus or the visible tab. A legacy unattended key is ignored: it neither grants nor restricts anything. Sharing, mode, control and these rules decide what an agent may do.
The basic form
Section titled “The basic form”deny: - pattern: 'rm -rf /(\s|$)' label: 'delete root' - pattern: ':\(\)\{.*\};:' label: 'fork bomb'
confirm: - command: rm args: '(^|\s)-[a-zA-Z]*[rR]' label: 'recursive delete' - pattern: 'kubectl delete' label: 'delete resource' - command: git args: '^(clean|reset --hard|push .*--force|push .*-f\b)' label: 'destructive git' - command: sudo label: 'privilege escalation' - pattern: '(^|[^>&])>[^>&]' label: 'overwrite file' - pattern: 'DROP TABLE' label: 'drop table'
protected: - '~/.ssh/**' - '~/.conn/**'
default: allow # allow | confirm | denypattern: Rustregexsyntax,is_match(partial match) over the segment. Add^/$yourself when you need anchors.label: the phrase shown at the top of the approval card. Keep it short and concrete so the human can decide in three seconds.[A] allow for this sessionworks per label.default: the verdict for a command no rule matched.
Evaluation
Section titled “Evaluation”The line is split into segments, and each segment takes the first match in the order deny → confirm → (opaque, if opaque) → default. The line’s verdict is the worst of its segments. A target path that matches protected is a deny. With isolate_dangerous on, a dangerous segment that shares the line with any other segment is denied (label “run dangerous commands alone”).
A deny cannot be lifted by an approval or by an [A] session allow. Session allows work per segment label.
The approval card shows, in this order: the intent the agent wrote, the target paths the engine computed (existence, git repository, entry count, protected), the per-segment verdicts, and the raw line. What the human has to compare is the intent against the targets.
Policy applies only to an agent’s ENTER. Human commands are recorded, not checked.
Keep it narrow
Section titled “Keep it narrow”Approvals are read only when they are rare. A system that asks about everything effectively asks about nothing.
- Put only hard-to-undo things under
confirm: deletes, force pushes, privilege escalation, production changes. - Do not list read-only commands (
get,ls,status,log). - When the same label keeps appearing in repetitive work, allow it for the session with
[A]. It resets when the session ends.
Startup
Section titled “Startup”An invalid or unreadable policy prevents a new shell from starting. Conn records policy_load_failed; fix the file and retry. A missing file creates the example policy. Existing sessions retain their last good rules if a later reload fails.
Reload
Section titled “Reload”The proxy checks the file’s mtime every second and re-reads it when it changes. On a parse failure (bad YAML, bad regex) it keeps the previous policy, prints one warning line to the terminal and writes policy_reload_failed to the audit log. It never falls back to having no policy.
Limits
Section titled “Limits”The structural analysis is deterministic and conservative. It catches or questions the common evasions (\rm, /bin/rm, sudo, cd .. && rm, sh -c), but it cannot see aliases, commands inside script files, or commands assembled through variable substitution. It guards against mistakes and casual workarounds, not against a malicious agent (security.md).
Command extraction
Section titled “Command extraction”The string that is checked is the input line at ENTER. conn tracks the line from the bytes written to the PTY, and after tab completion or history navigation it uses the VT model’s cursor row with the prompt removed. The cmd in the audit log is this value.
Reduce environment checks
Section titled “Reduce environment checks”The default remains one review per command in an unverified execution environment. When an environment review is pending in the local owner UI, Reduce environment checks lets you select one exact agent connection in the current session, read the warning, and acknowledge that you will check the execution target yourself. The decision lasts exactly 15 minutes in memory. It cannot be saved as a default, extended by polling, inherited by another connection with the same name, or created through agent IPC/MCP. Closing and reopening the controls does not grant anything. The visible banner shows the session, connection ID, remaining time and Stop.
This first version reduces only the environment overlay for supported simple queries in an explicit POSIX target dialect: :, true, false, pwd, echo, printf, ls, cat, head, tail, wc, grep, fgrep, egrep, cut, tr, cmp, readlink, realpath, basename, dirname, uname, id, whoami, uptime, df, du, free and ps. Policy deny and risk-confirm rules still apply first, including any custom rules for these queries. Existing command-category allowances do not remove those risk reviews in an unverified environment.
All other commands keep review, even when the policy default is allow. Leading assignments, wrappers, printf -v, incomplete quoting, parameter/pathname/history expansion, quick history substitution and unsupported compound grammar also keep review. Quoted literal text remains supported. printf supports literal text, %s and %%; variable assignments and other format conversions keep review. Remote protected file targets are never checked against the host filesystem, so arbitrary file-changing commands and launchers do not qualify for reduction. Conn keeps its existing structural policy model; aliases, functions and executable identity remain unverified.
Deny rules, command separation, dangerous-command confirmation, Observe, Co-pilot’s manual commit, participation, Privacy, control leases and operating-system authentication retain their requirements. Pending approval cards are never approved by enabling this option.
Conn cannot verify the final execution target or internal transitions. Nested remote connections may change the final host without detection. This feature does not verify target identity. Conn does not infer the end of password entry from prompts or ECHO and never automatically shares private input.
Stop, expiry, disconnect/reconnect, session exit, sharing changes, participant removal, profile/policy changes, an observed transport change and direct human takeover revoke the decision. Natural control-lease expiry does not revoke it, but actual input still requires a current lease. A queued command is checked again immediately before PTY submission; an expired, revoked or changed context cancels it and retains the existing explicit input-recovery requirement. A new decision cannot retroactively authorize it. Already running processes are not killed.
Audit records for this decision contain only grant/session/connection IDs, context revision, duration, timestamps and end reason; no terminal content, raw human input, secrets or environment dump is added.