Skip to content

feat(mediation): DSL redesign — explicit defaults, grants, three-action set#38

Open
kipz wants to merge 1 commit into
developfrom
kipz/intercept-matchers-and-sandboxes
Open

feat(mediation): DSL redesign — explicit defaults, grants, three-action set#38
kipz wants to merge 1 commit into
developfrom
kipz/intercept-matchers-and-sandboxes

Conversation

@kipz

@kipz kipz commented May 13, 2026

Copy link
Copy Markdown
Owner

Summary

Full redesign of nono's command mediation DSL and security model, implementing ETI gap-closure plans 1–4.

DSL changes

Explicit default block. Every mediation.commands entry now has a default with an action (allow/deny) and optional sandbox. There is no implicit passthrough — unmatched invocations are governed by the default, which must be declared.

Three action types (renamed from run/respond):

  • allow — execute the binary. Carries optional script.
  • deny — return canned stdout/stderr/exit_code without running anything.
  • capture — intercept credential-fetching commands, return a nonce, promote to a real token only when an authorised command requests it.

deny_unknown_fields on all eight mediation structs. Invalid profile fields are rejected at load time rather than silently ignored.

Authorization graph — can_use / session_can_use

Replaces caller_policy. session_can_use at the mediation root lists commands the agent entry-point may invoke directly; can_use on each command entry lists commands it may invoke as children. The graph is validated at profile load: every name must resolve to a known command.

from{caller} passthrough

Replaces allow_commands. Per-caller behaviour map on CommandEntry, keyed by calling command name. { "action": "passthrough" } bypasses intercept rules for that caller, enabling credential chains (e.g. ddtool → curl) without exposing raw tokens to the primary sandbox. Requires mutual consent at validation time.

Binary identity pinning

Captures each mediated binary's dev/inode/size/mtime/sha256 at session start (O_NOFOLLOW). Re-verifies before every spawn. Fail-closed: a missing or mismatched pin blocks the exec.

Symlink fix: Homebrew installs binaries as symlinks under /opt/homebrew/bin. O_NOFOLLOW returns ELOOP on symlinks. Both pin_binary and verify_pin now canonicalize the path before opening.

Dynamic token expansion

expand_profile_tokens_with previously only walked the legacy cmd.sandbox field, so @git:config-files tokens in cmd.default.sandbox (the new DSL path) were never expanded. Fixed by walking cmd.default.sandbox and per-intercept sandboxes. New unit test covers this.

Regex: unicode-perl feature

The regex crate was configured without unicode-perl, so \s, \d, \w in intercept match patterns failed at runtime when a profile was loaded. Added the feature and a regression test.

@kipz kipz marked this pull request as ready for review May 13, 2026 14:04
@kipz kipz force-pushed the kipz/intercept-matchers-and-sandboxes branch from 9f7a17b to b89b03d Compare May 14, 2026 12:06
@kipz kipz changed the title feat(mediation): rich argv matchers + per-intercept sandbox feat(mediation): DSL redesign — explicit defaults, grants, three-action set May 18, 2026
@kipz kipz force-pushed the kipz/intercept-matchers-and-sandboxes branch from bffb937 to ec9f806 Compare May 19, 2026 10:52
@kipz kipz force-pushed the kipz/intercept-matchers-and-sandboxes branch 3 times, most recently from b03321f to 5acc580 Compare June 2, 2026 14:26
@kipz kipz force-pushed the develop branch 3 times, most recently from 86b464a to 4f89e43 Compare June 4, 2026 17:07
@kipz kipz force-pushed the kipz/intercept-matchers-and-sandboxes branch from e1072fd to 46b6f4d Compare June 4, 2026 17:18
@kipz kipz force-pushed the kipz/intercept-matchers-and-sandboxes branch from 20f53ac to 9f75de5 Compare June 15, 2026 09:49
… pinning

Full redesign of nono's command mediation DSL and security model.

## DSL changes

**Explicit default block.** Every `mediation.commands` entry now has a
`default` with an `action` (allow/deny) and optional `sandbox`. There is
no implicit passthrough — unmatched invocations are governed by the
default, which must be declared.

**Three action types** (renamed for clarity):
- `allow` — execute the binary (was `run`). Carries optional `script`.
- `deny` — return canned stdout/stderr/exit_code without running anything
  (was `respond`).
- `capture` — intercept credential-fetching commands, return a nonce,
  promote to a real token only when an authorised command requests it.

**deny_unknown_fields** on all eight mediation structs (CommandSandbox,
NetworkConfig, EnvPolicy, CallerPolicy, CommandEntry, InterceptRule,
DefaultEntry, MediationConfig). Invalid profile fields are rejected at
load time rather than silently ignored.

## Authorization graph (replaces caller_policy)

- `can_use` on each `CommandEntry` — lists commands this command may
  invoke as mediated children.
- `session_can_use` on `MediationConfig` — lists commands the agent
  entry-point may invoke directly.
- The graph is validated at profile load time: every name must resolve
  to a known command.

## from{caller} passthrough (replaces allow_commands)

Per-caller behaviour map on `CommandEntry`, keyed by calling command
name. `{ "action": "passthrough" }` bypasses intercept rules for that
caller, enabling credential chains (e.g. ddtool → curl) without
exposing raw tokens to the primary sandbox. Requires mutual consent:
if `curl.from["dd-auth"]` is set, `dd-auth.can_use` must contain
"curl". Validated at load.

## Binary identity pinning

Captures each mediated binary's dev/inode/size/mtime/sha256 at session
start (O_NOFOLLOW). Before every spawn, re-verifies the binary hasn't
been replaced. Fail-closed: a missing or mismatched pin blocks the exec.

Symlink fix: Homebrew installs binaries as symlinks under
/opt/homebrew/bin. O_NOFOLLOW returns ELOOP on symlinks. Both
pin_binary (session.rs) and verify_pin (policy.rs) now canonicalize the
path before opening.

## Dynamic token expansion

expand_profile_tokens_with previously only walked the legacy cmd.sandbox
field. Tokens in cmd.default.sandbox (the new DSL path) and per-intercept
sandboxes were never expanded, so @git:config-files had no effect.
Fixed by walking all three locations. New unit test covers the case.

## Regex: unicode-perl feature

The regex crate was configured without unicode-perl, so \s, \d, \w in
intercept match patterns failed at runtime. Added the feature and a
regression test.
@kipz kipz force-pushed the kipz/intercept-matchers-and-sandboxes branch from 23c1be5 to bda3e68 Compare June 15, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants