Skip to main content
ncl is the admin CLI for a running NanoClaw host. It sends one request frame over a Unix socket at data/ncl.sock and prints the response — commands inspect and manage the running host’s state, including central configuration and per-session mailboxes. The socket is chmod 0600, so only the user that started the host can connect. Don’t confuse it with the CLI channel, which is a chat adapter for talking to an agent from the terminal — ncl is for operating the host itself. This page is the working tour. The ncl CLI reference has every operation, flag, and enum for all thirteen resources — including the operator-only policies gate for agent-to-agent messages.

Invoking ncl

Setup symlinks bin/ncl into ~/.local/bin, so after install you can run ncl from anywhere. From the project root, pnpm ncl works too:
The general shape is:
Positional words join into the command name: the dispatcher matches the longest registered command, and everything after it is the target ID — dashes intact, so ncl groups get abc123 and dashed ids like ncl tasks cancel task-374f-… both resolve. --id <uuid> works everywhere too. Flags map to columns; hyphens and underscores are interchangeable (--agent-group-id = --agent_group_id). Run ncl help to list every resource, ncl <resource> help for its verbs, columns, and enums, and ncl <resource> help <verb> (or --help on any command) for a single verb’s flags.

Output and errors

By default ncl prints a human format: a small aligned table for list results, pretty-printed JSON for single objects, and error (<code>): <message> on failure. Pass --json to get the raw response frame instead — use that for scripting. Exit codes: 0 success, 1 command error, 2 transport error. If the host isn’t running, ncl can’t reach the socket and tells you so:

Agents can run ncl too

The same command surface is exposed to agents inside containers. What an agent may do is controlled per group by cli_scope in its container config:
  • disabled — all ncl commands are rejected.
  • group (default) — the agent only sees the groups, sessions, destinations, members, and tasks resources, pinned to its own group: IDs are auto-filled, rows from other groups are filtered out, and it cannot change its own cli_scope. It also gets wirings get/wirings update for the current conversation’s wiring only — the id is force-filled, and updates may only change engage_mode/engage_pattern.
  • global — full access to every resource.
Across most resources, mutating verbs (create, update, delete, grant, restart, …) are approval-gated for agents: the agent gets approval-pending and an admin receives a card showing the exact command. The exception is tasks — every tasks verb, including create and cancel, is open to agents, since a task is scoped to the agent’s own group. From the host socket, the same verbs run immediately — the socket file permissions are the auth boundary. Change the scope with:

Working with resources

Representative commands per resource — ncl <resource> help shows the full set.

groups

Agent groups: the agent identities with their own folder, history, and container image. Beyond CRUD, groups carries the container-config and lifecycle verbs.
create also takes --template <ref> to stamp the new group from a template — a ref relative to your local templates/ directory, e.g. <category>/<template>. When a group already carries that template’s plugin, the same command shows an in-place update plan instead; --yes applies it and --new forces a second agent. restart kills and respawns the group’s containers (--rebuild rebuilds the image first — required after package changes; --message <text> leaves an on-wake instruction). delete cascades through sessions, wirings, roles, memberships, and destinations in one transaction. Config subcommands edit the group’s container_configs row — changes apply on the next restart:
config update accepts --provider, --model, --effort, --image-tag, --assistant-name, --max-messages-per-prompt, --cli-scope, and --timezone. add-mcp-server also takes --url in place of --command/--args for a remote Streamable HTTP server, with optional --headers as a JSON object. Plugin-owned MCP servers cannot be replaced or removed through these config commands: edit the plugin and restamp it instead. config add-mount and remove-mount are operator-only — they run from the host socket, never from inside a container — and mounts still pass the allowlist check at spawn.

messaging-groups and wirings

A messaging group is one chat on one platform (unique channel_type + platform_id); a wiring connects it to an agent group and sets engagement rules. List commands accept column filters and --limit:
Wiring columns: engage_mode (mention | mention-sticky | pattern), engage_pattern, sender_scope (all | known), ignored_message_policy (drop | accumulate), session_mode (shared | per-thread | agent-shared), threads (honor platform thread ids for this wiring; unset inherits the channel’s default — you can turn threads off on a threaded platform, never on for one that has none), and priority (fanout order when several agents are wired to one chat; higher first). Engagement flags you omit on create default from the channel adapter’s declaration. messaging-groups send injects a message into a chat as if a sender posted it, waking the wired agent — the host uses it to send a welcome on first wire:

users, roles, and members

Users are per-channel identities (tg:6037840640, discord:1234...). Roles grant privilege (owner is always global; admin can be global or scoped to one group with --group). Members let unprivileged users talk to a group when its wiring has sender_scope known.
roles revoke and members remove take the same flags.

sessions

Read-only view of the runtime units — one row per (agent group, messaging group, thread) with its container status (running | stopped; idle is reserved and currently unused):
history merges inbound and outbound messages into a chronological transcript. Group-scoped agents can only read sessions owned by their own agent group. Human output truncates message text to 200 characters; add --json for the raw rows and full text.

tasks

Scheduled and recurring work, run by the host in isolated per-task sessions. Ids are stable series handles — t-<hex>, or <slug>-<hex> when created with --name:
create needs --group and --prompt; --recurrence takes a cron expression, one-shot tasks use --process-after, and --script adds a pre-run bash gate. The full verb set (run, resume, update, cancel, delete, append-log) is in the reference; the workflow tour is in Scheduled tasks.

dropped-messages

Read-only log of messages the router dropped, aggregated per sender and chat with a reason (no_agent_wired, no_agent_engaged, unknown_sender_strict, unknown_sender_request_approval, unknown_sender_decline_notify). Your first stop when a message got no reply:

approvals

Read-only view of in-flight approval cards waiting for an admin response:

destinations and user-dms

Destinations are per-agent send ACLs — each row lets an agent address a channel or another agent by a local name. user-dms is the read-only DM-route cache the host uses to cold-DM users.
Last modified on September 4, 2026