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 — every command reads or mutates the host’s central database live, no restart needed. 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. A trailing positional becomes the target ID only if the ID contains no dashes — the dispatcher trims a single dash-segment, so ncl groups get abc123 works, but a real NanoClaw ID (a UUID, four hyphens) does not resolve. In practice, always pass --id <uuid>. 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, and members 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.
global — full access to every resource.
Regardless of scope, 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. 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 <name> to stamp the new group from a template.
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, and --cli-scope. 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):
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). 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 July 16, 2026