Skip to main content
Exhaustive reference for the ncl admin CLI. For a task-oriented walkthrough, see The ncl admin CLI.

Invocation

bin/ncl resolves the project root and execs the client via tsx — symlink it onto your PATH (ln -s "$(pwd)/bin/ncl" /usr/local/bin/ncl) or run pnpm ncl ... from the checkout. The client sends one request frame over the Unix socket at data/ncl.sock and exits. Conventions that apply to every command:
  • Positional IDs only work without dashes. Positional words join into the command name; the dispatcher trims one trailing dash-segment as the target ID. ncl groups get abc123 works, but a real UUID (four hyphens) does not — always pass --id <uuid>.
  • Kebab and snake case are interchangeable. --agent-group-id and --agent_group_id are the same flag; hyphens normalize to underscores.
  • list accepts column filters and --limit. Any non-generated column is a filter (ncl sessions list --container-status running), except a column literally named id — the list handler always skips the id arg, so ncl users list --id ... is silently ignored (affects users). Filters combine with AND. Default --limit is 200 (minimum 1). No ordering or offset flags.
  • get, update, delete require --id — the value of the resource’s ID column.
  • create validates enums, applies defaults, auto-generates the UUID primary key and *_at timestamps.
  • update requires at least one updatable column flag, validates enums, and returns the full updated row.
  • --json prints the raw response frame; default output is an aligned table for lists, pretty JSON otherwise, error (<code>): <message> on failure.
  • Exit codes: 0 success, 1 command error, 2 transport error (host unreachable, missing command).
  • Error codes in response frames: unknown-command, invalid-args, forbidden, approval-pending, handler-error, transport-error (plus reserved permission-denied, not-found).
  • Help is introspected: ncl help lists resources and verbs; ncl <resource> help shows its fields, enums, and defaults.

Access control

Each operation has an access level: open or approval. Host callers (your terminal) bypass approval entirely — every command runs inline. When a containerized agent calls an approval operation, the command is not executed; an approval card goes to an admin, the call returns approval-pending, and on approval the host executes it and notifies the agent. Across the resources the rule is near-uniform: reads (list, get, config get) are open and mutations are approval — except tasks, where every verb is open: an agent scheduling its own future work is scoped to its own group and doesn’t gate on an admin. Agent callers are also subject to the group’s cli_scope (set via ncl groups config update --cli-scope):

approvals

In-flight approval cards waiting for an admin response. Read-only; rows are deleted after approve/reject/expiry. Columns (all usable as list filters): approval_id, session_id, request_id, action (e.g. install_packages, add_mcp_server, onecli_credential), payload (JSON), created_at, agent_group_id, channel_type, platform_id, platform_message_id, expires_at, status (pending | approved | rejected | expired | awaiting_reason), approver_user_id, title, options_json.

destinations

Per-agent routing entries and ACL — each row lets an agent send to a target (channel or another agent) under a local name. Local names are unique per agent. No get, create, update, or delete; mutations go through add/remove, which also project the change into every active session’s inbound.db so running containers pick it up without restart. --target-id is a messaging_groups.id when --target-type channel, an agent_groups.id when agent.

dropped-messages

Log of messages dropped by the router or access gate, aggregated by (channel_type, platform_id) with a running count. List-only. Columns: channel_type, platform_id, user_id, sender_name, reason (no_agent_wired | no_agent_engaged | unknown_sender_strict | unknown_sender_request_approval), messaging_group_id, agent_group_id, message_count, first_seen, last_seen.

groups

Agent groups — logical agent identities with their own workspace folder, history, and container image.

members

Membership rows granting unprivileged users access to an agent group. Admins/owners are implicitly members and need no row. Checked by the router when a wiring’s sender_scope is known.

messaging-groups

One chat or channel on one platform. Identity is the unique (channel_type, platform_id) pair.

policies

Per-message approval gate on an agent-to-agent connection. A row forces every message from one agent to another to be approved by a human before delivery — without un-wiring the destination. No row = free flow. Directed and per-pair: gate both directions with two policies. Operator-only — agents can’t manage their own gates, and self-messages (--from equal to --to) are never gated. set upserts — re-running with a different --approver replaces it. Only the named --approver (or an owner) can approve a held message. Removing a policy returns the edge to free flow. Columns: from_agent_group_id, to_agent_group_id, approver, created_at.

roles

Privilege grants. owner is always global; admin can be global (agent_group_id null) or scoped to one group. Admin at a group implies membership.

sessions

The runtime unit — one (agent_group, messaging_group, thread) combination mapped to a container. Created automatically by the router; read-only in the CLI. Columns: id, agent_group_id, messaging_group_id (null for agent-shared sessions), thread_id (per-thread mode only), agent_provider (null = inherit from group), status (active | closed), container_status (running | idle | stoppedidle is reserved and currently unused), last_active, created_at.

tasks

Scheduled tasks — the CLI home for what used to be the agent’s schedule_task MCP tools. Each task is a series: the live pending/paused row is the next run, and completed rows are its run history. Tasks are created from the host or by an agent inside its own task session, and each runs in an isolated per-task session that the host garbage-collects once the series is spent. Ids are t-<hex>, or <slug>-<hex> when you pass --name. run fires a task now without changing its schedule (a safe test that neither consumes a one-shot nor advances a recurring series — unlike update --process-after now). append-log adds a note to a task’s run log (tasks/<id>.md); every run already auto-logs its final text, so it’s for additive mid-run notes only.

user-dms

DM route cache mapping (user, channel_type) to the messaging group used for cold DMs (approvals, pairing). Populated lazily; list-only. Columns: user_id, channel_type, messaging_group_id, resolved_at.

users

Messaging-platform identities — one row per sender per channel (no cross-channel linking yet). No delete.

wirings

Connects a messaging group to an agent group — which agent handles which chat. Many-to-many in both directions.
Last modified on July 16, 2026