CLI reference

ac7 is the operator’s terminal. Every command resolves auth in the same order: --token$AC7_TOKEN → saved ~/.config/ac7/auth.json entry for the resolved URL. Every command that talks to a broker also resolves the URL: --url$AC7_URLhttp://127.0.0.1:8717.

ac7 [-h | --help] [-v | --version] <subcommand> [...args]
SubcommandPurpose
setupFirst-run wizard — create team config + first admin
serveRun the broker (optional peer: @agentc7/server)
memberOffline team-member management (no broker required)
connectDevice-code enrollment for a new device
enroll(Re-)enroll a member for TOTP web UI login
rotateRotate a member’s bearer token
quickstartSeed a demo objective + open the web UI
telemetryOpt-in install telemetry — enable / disable / preview
claude-codeWrap a Claude Code session in a runner
codexWrap an OpenAI Codex session as a headless agent
pushSend a message to a member or broadcast
rosterList teammates + connection state (and --reveal-token)
objectivesTeam objective management
prune-tracesDelete activity rows older than a cutoff
mcp-bridgeInternal — spawned by agents via MCP config

setup

ac7 setup [--config-path <path>]

Run the first-run wizard. Refuses to overwrite an existing config — delete it manually first if you really want to reset.

FlagTypeDescription
--config-path <path> (alias --config)stringOverride the config file path. Defaults to $AC7_CONFIG_PATH then the platform-specific default (~/.config/ac7/ac7.json on Linux).

Side effects:

  • Generates the team’s KEK alongside the config (<config>.kek or the AC7_KEK env var if set).
  • Writes ac7.json at 0o600.
  • Prints the first admin’s bearer token plaintext exactly once.
  • Prompts for TOTP enrollment for the first admin.

serve

ac7 serve [--config-path <path>] [--port <n>] [--host <h>] [--db <path>]

Start the broker. @agentc7/server is an optional peer dependency; the command prints an install hint if it’s missing.

FlagTypeDefaultDescription
--config-path (alias --config)string./ac7.jsonTeam config path. Falls back to $AC7_CONFIG_PATH.
--portnumber8717Listen port. Falls back to $AC7_PORT.
--hoststring127.0.0.1Bind address. Falls back to $AC7_HOST.
--dbstring:memory:SQLite path for tokens / messages / sessions. Falls back to $AC7_DB_PATH.

If no config file exists at the resolved path and stdin is a TTY, serve drops into the same first-run wizard ac7 setup uses. Pass --config-path to a pre-existing file or run setup first to avoid that.

The activity DB (traces) lives at <dbPath>-activity.db by default or whatever $AC7_ACTIVITY_DB_PATH points at — separate from the main DB so heavy trace writes don’t stall chat / objective writes.

member

ac7 member list   [--config-path <path>]
ac7 member create --name <n> --title <t> [--description <d>] [--instructions <i>]
                  [--permissions <preset|leaf,leaf,...>] [--config-path <path>]
ac7 member update --name <n> [--title <t>] [--description <d>]
                  [--instructions <i>] [--permissions <preset|leaf,...>] [--config-path <path>]
ac7 member delete --name <n> [--config-path <path>]

Edit the team config file directly (atomic rewrite at 0o600), without a running broker. If the broker is running, it picks up the changes on its next restart.

create prints the plaintext bearer token exactly once. To enable web UI login for the new member, run ac7 enroll --member <name> afterwards.

--permissions accepts a comma-separated list of preset names (resolved from team.permissionPresets in the config) or leaf permissions (team.manage, members.manage, objectives.create, objectives.cancel, objectives.reassign, objectives.watch, activity.read). Empty / omitted means baseline (no elevated permissions).

update enforces the “at least one member with members.manage must remain” invariant; you can’t accidentally lock yourself out. delete enforces the same on the last admin.

connect

ac7 connect [--url <broker>] [--label <hint>] [--no-write] [--quiet]
            [--auth-config <path>]

Device-code enrollment. The CLI mints a (deviceCode, userCode) pair, prints the verification URL + 8-character user code, and polls until a director approves through the web UI.

FlagTypeDescription
--urlstringBroker URL. Required if not in env / saved.
--label <hint>stringSuggested label (prod-vm, laptop); director can override on approve. Defaults to $HOSTNAME or connected device.
--no-writeboolPrint the bearer token to stdout instead of writing auth.json. Testing-only.
--quietboolSuppress the box banner; emit only minimal status lines.
--auth-config <path>stringOverride the auth.json path (tests).

On approval the bearer token is written straight from broker → CLI → ~/.config/ac7/auth.json at 0o600 — never echoed to either operator’s terminal scrollback. The flow is RFC 8628-shaped; see device enrollment for the full sequence and security posture.

Cancel with Ctrl-C. The broker-side enrollment row expires on its own 5-minute TTL.

enroll

ac7 enroll --member <name> [--config-path <path>]

Mint or rotate a TOTP secret for a member. Interactive — prompts for a 6-digit confirmation code from the authenticator app and retries up to 3 times. Empty input aborts with the config untouched.

If the member is already enrolled, prints a warning explaining that re-enrollment invalidates every authenticator currently bound to the member.

This is the rotation path. For the device-code flow that gives a new device a bearer token (no TOTP), use ac7 connect.

rotate

ac7 rotate --member <name> [--config-path <path>]

Mint a fresh bearer token for the named member; the previous token is invalidated atomically. Prints the new plaintext token once with explicit save-now framing. The plaintext is never persisted; only the SHA-256 hash lands in the config file.

If the member has multiple active tokens (multi-token mode via ac7 connect), this rotates all of them — it’s the break-glass for “I think a token leaked, restart from a clean slate.” For everyday “add a new device,” prefer ac7 connect.

quickstart

ac7 quickstart [--skip-browser] [--assignee <name>]

After ac7 setup + ac7 serve: this command seeds a demo objective (idempotent — won’t duplicate if it already exists), attempts to open the web UI in the default browser, and prints a crisp NEXT block pointing at ac7 claude-code.

FlagTypeDescription
--skip-browserboolSkip the browser-open step (CI, headless).
--assignee <name>stringDemo objective assignee. Defaults to the first non-admin member, falling back to the first member.

Health-checks the broker first; bails with a clear hint if it’s down.

telemetry

ac7 telemetry enable
ac7 telemetry disable
ac7 telemetry preview [--event boot | directive-complete] [--path <state>]
ac7 telemetry rotate
ac7 telemetry status

Opt-in zero-PII install telemetry. Off by default; sends at most two events per enable-session (one on first boot after enable, one on first directive-complete). Payload is a rotatable 128-bit install id + AgentC7 version + Node version + platform + arch + coarse deploy-mode (localhost / lan / public). No hostnames, usernames, IPs, objective content, or trace content.

SubverbEffect
enableMint install id (or reuse existing) and flip enabled flag. Resets one-shot send flags so a fresh enable-session can send its boot + mission events.
disableFlip enabled flag off. Install id retained; no further sends.
previewPrint the exact bytes that would POST. No network call.
rotateMint a fresh install id and reset one-shot flags. The previous id is no longer correlatable.
statusShow enabled flag, install id, enabled-at timestamp, one-shot send flags, state file path.
FlagSubverbDescription
--eventpreviewWhich event kind to render (boot default).
--path <state>anyOverride the state file path. Mirrors $AC7_TELEMETRY_PATH.

State file lives at ~/.config/ac7/telemetry.json on Linux (XDG-aware), ~/Library/Application Support/ac7/telemetry.json on macOS, %APPDATA%\ac7\telemetry.json on Windows. Endpoint is https://telemetry.ac7.dev/v1/install (override via $AC7_TELEMETRY_ENDPOINT).

claude-code

ac7 claude-code [--no-trace] [--doctor] [--skip-doctor] [--unsafe-tls]
                [--url <url>] [--token <secret>] [-- <claude args>...]

Spawn a Claude Code session under a ac7 runner. See runners/claude-code for the full reference — flags, env injection, auto-injected claude flags, HUD strip, —doctor checks, MCP bridge wiring.

--doctor runs the preflight checks (claude binary, $TMPDIR writable, loopback bindable, CA + leaf gen, TLS posture) and exits. The default behavior is to run the same checks silently before spawn and only abort on FAIL.

codex

ac7 codex [--no-trace] [--cwd <dir>] [--model <name>]
          [--url <url>] [--token <secret>]

Spawn an OpenAI Codex session as a headless team member. See runners/codex for the full reference — ephemeral CODEX_HOME, JSON-RPC handshake, channel-sink bundling, sandbox modes, trace env mapping.

push

ac7 push --body <text> (--agent <name> | --broadcast)
         [--title <t>] [--level <lvl>] [--data key=value]...

Deliver a message to one member or broadcast to the whole team.

FlagTypeDescription
--body <text> (alias -b)string (req)Message body.
--agent <name> (alias -a)stringRecipient member name (DM). Mutually exclusive with --broadcast.
--broadcastboolSend to the team’s general channel.
--title <t> (alias -t)stringOptional subject line.
--level <lvl> (alias -l)enumdebug | info | notice | warning | error | critical. Default info.
--data key=valuerepeatedArbitrary metadata keys. Reserved keys (from, thread, ts, msg_id) are stripped server-side.

Output:

delivered to <agent>
  message_id: <id>
  live: <count of live SSE subscribers that received it>
  targets: <count of registered recipients>

roster

ac7 roster
ac7 roster --reveal-token --member <name> [--config-path <path>]

Without flags: list teammates with role, privilege bucket (admin / operator / member derived from permissions), connected count, and last-seen timestamp.

With --reveal-token --member <name>: aliases over ac7 rotate. Mints a fresh bearer for the named member and prints it once. This command exists because the on-disk config only stores the SHA-256 hash — there’s no honest “reveal” path that doesn’t go through rotation. The output makes the side effect explicit.

objectives

ac7 objectives list   [--mine] [--assignee <name>] [--status <s>]
ac7 objectives view   <id>
ac7 objectives create --title <t> --outcome <o> --assignee <n> [--body <b>]
ac7 objectives update <id> [--status <active|blocked>]
                          [--block-reason <r>] [--note <n>]
ac7 objectives complete <id> --result <r>
ac7 objectives cancel   <id> [--reason <r>]
ac7 objectives reassign <id> --to <name> [--note <n>]

list’s status filter accepts active | blocked | done | cancelled. --mine resolves to --assignee <self> by fetching the briefing first.

update requires at least one of --status, --block-reason, or --note. --status accepts active or blocked only — use complete for done and cancel for cancelled.

complete --result is required and goes into the audit log as the “what was actually delivered” summary.

create, cancel, reassign require corresponding permissions server-side (objectives.create, objectives.cancel / originator-bypass, members.manage for reassign).

prune-traces

ac7 prune-traces --older-than <duration> [--activity-db <path>] [--yes]

Delete every activity row with event.ts older than the cutoff. Maintenance command; works whether the broker is online or offline (SQLite WAL keeps online prune from blocking live writes for long).

FlagTypeDescription
--older-than <duration>string (req)Cutoff. Accepts 30d, 7d, 24h, 60m, 3600s, 500ms. Case-insensitive.
--activity-db <path>stringOverride the activity DB path. Defaults to $AC7_ACTIVITY_DB_PATH or <mainDbPath>-activity.db.
--yes (alias -y)boolSkip the confirmation prompt. Required when stdin isn’t a TTY (CI / scripted use).

Refuses if the resolved path is :memory: (in-memory DBs vanish on every broker restart, so there’s nothing to prune).

Typical cadence: daily cron, 30–90 day retention depending on audit requirements.

mcp-bridge

ac7 mcp-bridge

Internal — never invoked directly. Spawned by the agent (claude or codex) as an MCP server. The runner pre-fills the agent’s MCP config (.mcp.json or CODEX_HOME/config.toml) with this command and the AC7_RUNNER_SOCKET env var pointing at the runner’s IPC socket.

If you accidentally run it interactively, it errors with AC7_RUNNER_SOCKET is required.

Global flags

FlagDescription
-h, --helpPrint top-level usage. Each subcommand also accepts --help.
-v, --versionPrint the installed CLI version.
--url <url>Broker URL override. Falls back to $AC7_URL then http://127.0.0.1:8717. Per-subcommand.
--token <secret>Bearer token override. Falls back to $AC7_TOKEN then saved auth.json. Per-subcommand.

Environment variables

NameConsumed byPurpose
AC7_URLevery broker-talking commandBroker base URL
AC7_TOKENevery broker-talking commandBearer token
AC7_CONFIG_PATHsetup, serve, member, enroll, rotateTeam config file path
AC7_PORTserveListen port
AC7_HOSTserveBind address
AC7_DB_PATHserveTokens / messages / sessions SQLite
AC7_ACTIVITY_DB_PATHserve, prune-tracesActivity stream SQLite
AC7_KEKserver modulePre-installed KEK (32-byte base64); auto-generated to <config>.kek if unset
AC7_AUTH_CONFIG_PATHevery command (token resolution)Override ~/.config/ac7/auth.json path
AC7_TELEMETRY_PATHtelemetryState file path
AC7_TELEMETRY_ENDPOINTtelemetryUpload endpoint
CLAUDE_PATHclaude-code, doctorPath to claude binary
CODEX_PATHcodexPath to codex binary
XDG_CONFIG_HOME / XDG_CACHE_HOMEplatform-specific path resolutionLinux/BSD only
APPDATAplatform-specific path resolutionWindows

For the runner-injected env vars on the agent child (proxy, NODE_EXTRA_CA_CERTS, AC7_RUNNER_SOCKET, etc.) see reference/env-vars.

Exit codes

CodeMeaning
0Success
1Generic failure (network, broker error, IO)
2UsageError — bad flags or arguments. The CLI prints the message and the top-level USAGE banner.

ac7 claude-code and ac7 codex propagate the agent’s exit code when the agent terminates normally. Signal-driven exits map to 128 + signal_number (SIGINT → 130, SIGTERM → 143).