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 abc123works, but a real UUID (four hyphens) does not — always pass--id <uuid>. - Kebab and snake case are interchangeable.
--agent-group-idand--agent_group_idare the same flag; hyphens normalize to underscores. listaccepts column filters and--limit. Any non-generated column is a filter (ncl sessions list --container-status running), except a column literally namedid— the list handler always skips theidarg, soncl users list --id ...is silently ignored (affects users). Filters combine with AND. Default--limitis 200 (minimum 1). No ordering or offset flags.get,update,deleterequire--id— the value of the resource’s ID column.createvalidates enums, applies defaults, auto-generates the UUID primary key and*_attimestamps.updaterequires at least one updatable column flag, validates enums, and returns the full updated row.--jsonprints the raw response frame; default output is an aligned table for lists, pretty JSON otherwise,error (<code>): <message>on failure.- Exit codes:
0success,1command error,2transport error (host unreachable, missing command). - Error codes in response frames:
unknown-command,invalid-args,forbidden,approval-pending,handler-error,transport-error(plus reservedpermission-denied,not-found). - Help is introspected:
ncl helplists resources and verbs;ncl <resource> helpshows 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. Noget, 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’ssender_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 | stopped — idle is reserved and currently unused), last_active, created_at.
tasks
Scheduled tasks — the CLI home for what used to be the agent’sschedule_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.