cmux machine vault: per-entry delivery policy (--host, --no-hosts, --env)
Leo, 2026-11-03: "yeah let's do alllll of those things! :DDD", about closing this gap: vault values are put into the environment of a machine's agent processes, so any agent can read them and send them anywhere. Agents now get a placeholder (cmux-secret://NAME), and a local egress proxy puts the real value in request headers only toward each secret's allowed hosts. A per-entry opt-out keeps a real env var, labelled weaker.
This is the CLI part. It pairs with the acpmux egress proxy PR (branch lane/vault-egress-proxy) and the hub's vault policy endpoints.
Commands
cmux machine vault set NAME [--host H]... [--no-hosts | --env]: with a flag, the PUT body also carriesdeliveryandallowedHosts. With no flag neither is sent, so the hub keeps the entry's policy (a new entry gets the hub's default for its name). The value still comes from the hidden prompt or stdin, never argv.cmux machine vault policy NAME (--host H... | --no-hosts | --env):PUT /v1/vault/env/NAME/policy, which changes no value and no revision.vault.no_entryreads "your vault has no NAME", andvault.invalid_hostsnames the host rule.cmux machine vault ls: each entry ends withproxy: api.github.com, github.com,proxy: no hosts (placeholder only)orenv (weaker: agents can read the value). The JSON output carriesdeliveryandallowedHosts. An answer with nodelivery(an older hub, which puts values in the env) reads asenv.- Usage errors:
--envwith--hostor--no-hosts,--hostwith--no-hosts, more than 16 hosts, and policy flags on other verbs. Each--hostis lowercased and checked against the hub's pattern (a DNS name, optional*.and:port, with no scheme, path, IP or trailing dot) before anything is sent. Hosts in answers are checked the same way and dropped if they fail, never printed.
Tests
New tests in vault/tests.rs, using the local fake hub:
- flag parsing and refusals, including bad hosts
set --hostsends the policy andsetwith no flag sends nonepolicyPUTs the policy path in all three forms, never sends a value, and maps both error codeslsrenders all three kinds, and its JSON carries the fields
cargo test -p cmux-tui machine_join passes on a fleet Linux box, and so does cargo fmt --check. clippy reports nothing in these files; the clippy errors in automation.rs and teleport.rs are on base too.
Changelog
- Added:
cmux machine vault set --host/--no-hosts/--envandcmux machine vault policychoose how a vault value reaches a machine's agents, andvault lsshows it.
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Lets vault entries define a delivery policy so a machine's agents can't read secrets they don't need. cmux machine vault now takes --host H..., --no-hosts, or --env to choose between a placeholder replaced only for allowed hosts and the real env var, and ships a policy verb and policy-aware ls.
set NAMEwith a flag sendsdeliveryandallowedHostswith the value; without flags the hub keeps the entry's policy.policy NAME (--host H... | --no-hosts | --env)PUTs/v1/vault/env/NAME/policywithout touching the value.lsprints each entry's delivery (proxy: hosts,proxy: no hosts (placeholder only), orenv (weaker: agents can read the value)), and its JSON carriesdeliveryandallowedHosts; answers without either read asenv(older hub).- Conflicting flags, more than 16 hosts, and policy flags on other verbs are usage errors; hosts are validated against the hub's pattern before anything is sent.