Deploy a broker with isolated custody¶
Choose where the broker's authority lives before connecting agents. Public core v0.6.0 provides local brokers on macOS and Linux; it does not provide an enterprise fleet control plane. For the overall topology, see deployment patterns.
Two deployment modes¶
| Session mode | Trust-domain split | |
|---|---|---|
| Broker account | Your user account | Dedicated non-root service account |
| Custody | Shared account can obtain broker keys and forge locally valid history | Agent account cannot read/write broker custody; startup verifies ownership and permissions |
| Native approval | Requires the user's active GUI session | Broker has no GUI session; choose an applicable out-of-band factor |
| Service | LaunchAgent / systemd user service | LaunchDaemon / systemd system service |
| Configuration | ~/.opaque/config.toml |
Sealed service-account configuration, typically /etc/opaque/config.toml |
Session mode is useful for local workflow evaluation. It does not separate the agent from broker keys. Split mode still trusts the host, root and broker administrator, and cannot close unrelated provider access paths.
Trust-domain split (service-account mode)¶
Start from the reference configuration. Provision the dedicated account, client group and private state as described in the systemd unit or LaunchDaemon plist. Their headers contain the installation commands; adapt paths and account names before installing them.
# Daemon settings must precede the first [[rules]] table.
require_seal = true
[trust_domain]
enforce = true
socket_group = "opaque-clients"
socket_path = "/run/opaque/opaqued.sock"
With enforcement enabled:
- Existing custody files must belong exclusively to the broker UID, with no group/other permission bits or substituted symlinks. Foreign ownership is fatal; loose modes on broker-owned files are tightened at startup.
- The broker refuses connections from its own UID. Keep agent workloads out of the service account.
- Client-group access opens the transport boundary; identity and policy still
decide request authority. Every startup records
trust_domain.posture.
| Shared transport path | Mode | Owner/group |
|---|---|---|
| Socket directory | 0750 |
Broker / client group |
| Operation socket | 0660 |
Broker / client group |
| Daemon handshake token | 0640 |
Broker / client group |
Configuration, seals, audit/identity/task state, keys, pairing state and profiles
remain private. The broker needs the client group in its own supplementary group
set to change transport ownership. socket_group also accepts a numeric GID.
When no per-user socket exists, CLI/MCP clients try /run/opaque/opaqued.sock
and check socket ownership, world access and directory permissions before
connecting. Follow hardening for identity, policy,
reviewer enrollment and export settings.
Containers and Kubernetes¶
The Compose reference and Kubernetes manifest run broker and agent under distinct UIDs (7381/7382), sharing only a socket volume. Custody is mounted only in the broker container. A one-shot bootstrap initializes ownership and seals configuration; the running broker and agent stay non-root.
Keep the numeric socket GID (7999) in the required supplementary groups. The
Kubernetes reference disables automatic API tokens, drops capabilities, uses
read-only root filesystems and separates custody storage from the memory-backed
socket volume. Do not use fsGroup to group-share custody files: startup refuses
those permissions.
# Isolated fixture: builds and checks the Compose custody/transport boundary.
scripts/compose-smoke.sh
This test is fixture evidence, not qualification of an adapted deployment. The reference uses file-based keys. KMS/CSI delivery, SPIFFE identity and hardware attestation need separately implemented and qualified integration. The Kubernetes operator belongs to the private enterprise package; the public manifest is a static deployment example.
Approval placement¶
Approval support follows the request contract, not just the platform:
| Work | Supported review path |
|---|---|
| Generic operations | Applicable native, paired-device or FIDO2 factor selected by policy |
| Secret publishing, staging release, fixed inference tasks | Full native review or configured paired-workstation review |
| SSH health tasks and third-party MCP invocations | Local native full review |
See bounded tasks, workstation review and
MCP qualification for exact prerequisites. A headless
split broker cannot perform a workflow that currently requires local native
review. ios_faceid names the desktop paired-device wire factor; no iOS client
is released.
macOS session mode¶
The installed LaunchAgent is scoped to an Aqua session. LocalAuthentication needs
the user's GUI/keychain context; a LaunchDaemon does not supply that context.
Current source checks local authentication capability at startup when the session
factor is local_bio and trust-domain enforcement is off. Each prompt also fails
closed when authentication is unavailable. That preflight does not qualify
screen-lock, clamshell, remote-desktop or Fast User Switching behavior; test the
chosen host and factor.
Release archives and the separate reviewer application are described in
installable releases. A proposed daemon .pkg or
SMAppService installer is not the current installation workflow.
Linux session mode¶
The native flow presents reviewed intent, then requests polkit authentication.
Install the polkit action, provide zenity or kdialog and a
running graphical-session authentication agent. Missing approval prerequisites
cause denial; they do not authorize skipping review.
The CLI installs a user service; it does not provision split custody. Inspect the
generated unit before relying on graphical-session dependencies or additional
hardening. For a dedicated broker use the shipped system unit, including its
supplementary client group and NoNewPrivileges setting.
Sandbox prerequisites¶
Linux sandbox.exec requires a usable namespace wrapper. The broker probes the
actual namespace shape, kernel Landlock ABI and seccomp capability at startup and
before execution; installing a binary alone is insufficient.
| Recorded strategy | Requirement |
|---|---|
bubblewrap+landlock+seccomp |
Working bwrap, Landlock and seccomp |
bubblewrap+seccomp |
Working bwrap; Landlock unavailable |
unshare+landlock+seccomp |
Working unshare fallback, Landlock and seccomp |
unshare+seccomp |
Working unshare; Landlock unavailable |
| Refused | Neither namespace wrapper can establish the required shape |
Missing layers produce warnings; sandbox.created and sandbox.completed record
the strategy actually used. A failed probe refuses execution before spawning the
workload. The namespace wrapper performs setup, then
opaqued __opaque-sandbox-helper installs the supported restrictions before
executing the command. Keep the daemon binary outside .opaque, .ssh and
.gnupg, which are hidden in the sandbox.
# Debian/Ubuntu package; use your distribution's equivalent elsewhere.
sudo apt-get install bubblewrap
opaque service logs
opaque exec --profile dev -- echo "sandbox probe"
opaque audit tail --limit 20
The final two commands require a configured dev profile, policy and approval
factor. Inspect status and strategy; CLI/MCP withhold child stdout/stderr content.
On Ubuntu or in containers, host AppArmor, capabilities and seccomp policies may
prevent namespace creation. Qualify the required namespace permissions in the
chosen host policy rather than disabling confinement globally. A broker may start
while refusing sandbox.exec; provider operations do not require that sandbox.
macOS execution uses Seatbelt via the deprecated sandbox-exec facility and is
best-effort containment. sandbox = false provides environment sanitization only.
Neither platform makes secrets confidential from an agent-selected command that
receives them; permitted egress and output metadata remain disclosure paths.
See profiles and agent integration.
Upgrading existing custody¶
Before replacing a running binary, close admission, reconcile in-flight work and stop/fence every writer. The current writer requires an authenticated retained audit head and refuses unsupported older stores. Follow the explicit audit-store upgrade, which requires independently retained evidence. Hashing a suspect store is not a substitute; preserve original custody when migration is refused.
Do not restore task, identity, revocation or replay ledgers to an older state to recover service. An intact old snapshot can verify and still resurrect authority. See storage and recovery.
Verify the deployed boundary¶
- Verify the selected release artifact and
record its version. Inspect the active config,
opaque policy checkwarnings, custody ownership and startup posture. - Test permitted and denied work with a disposable target and real reviewer. Test replay/revocation in isolated fixtures, and inspect interrupted outcomes.
- Confirm the agent cannot read custody or access the provider through an unrelated credential. Match approval provenance to the intended factor.
- Export and independently retain evidence with receiver continuity state. Preserve custody bindings and consumption ledgers in recovery procedures.
Deployment verification gives the inspection steps; hardening gives configuration fields. Approval, current authority, durable consumption and provider success are separate checks.