meroctl CLI Reference
meroctl is the operator CLI. It connects to a running merod node over its
RPC server and manages applications, namespaces, groups, contexts, blobs, and
method calls.
Global arguments
Section titled “Global arguments”| Flag | Type / values | Notes |
|---|---|---|
--home <PATH> |
path | Directory for config and data. Env: CALIMERO_HOME. |
--node <ALIAS> |
string | Use a pre-configured node alias. Conflicts with --api. |
--api <URL> |
URL | API endpoint URL of the node to target. |
--output-format <FORMAT> |
enum | Output format (e.g. JSON) — used by commands such as network status. |
When neither --node nor --api is given, meroctl uses the active node (set
with node use), falling back to http://127.0.0.1:2528.
Aliases
Section titled “Aliases”Almost every command accepts an alias wherever it accepts an id. An alias is
a human-readable name (max 50 characters) that the node resolves server-side to
a ContextId, an ApplicationId, a member PublicKey, or a DeviceId. Aliases
live on the node, not in the CLI: they are created, looked up, and deleted
through the
admin API alias endpoints, so
every client talking to the same node sees the same aliases.
Scoping is not uniform across the four target types:
| Alias target | Scope | Created with |
|---|---|---|
ContextId |
Node-global | context alias add, or context create --name |
ApplicationId |
Node-global | admin API alias/create/application |
DeviceId |
Node-global | account alias add |
Context, application, and device aliases are unique across the whole node.
A reserved default alias exists for contexts. Commands that take --context
fall back to default when the flag is omitted, and context use <CONTEXT>
just points the default alias at a chosen target.
Command groups
Section titled “Command groups”| Group | Purpose |
|---|---|
app |
Manage applications (install, list, versions, watch). |
context |
Manage contexts, identities, aliases, and sync. |
group |
Manage groups: members, contexts, metadata, upgrades, settings. |
namespace |
Manage namespaces (root groups / application instances). Alias ns. |
blob |
Upload, download, inspect, and delete blobs. |
call |
Call a method on a context (mutation or view). |
dev |
Developer workflow: build, install, create context, watch. |
network |
Inspect libp2p networking state. |
peers |
Return the number of connected peers. |
tee |
TEE fleet-node commands. |
node |
Manage node connections used by meroctl (local config). |
| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
get <APP_ID> |
Fetch application details. | APP_ID |
install |
Install a signed .mpk application bundle. |
PACKAGE@VERSION, --path/-p, --watch/-w |
list (ls) |
List installed applications. | — |
uninstall <APP_ID> |
Uninstall an application. | APP_ID |
versions <APP_ID> |
List locally installed versions of an app. | APP_ID |
watch |
Watch a .mpk bundle and update all contexts using the app. |
--path/-p, --current-app-id |
list-packages |
List the packages installed on this node. | — |
list-versions <package> |
List this node’s installed versions of a package. | package |
get-latest-version <package> |
Highest installed version of a package. | package |
meroctl --node node1 app install com.example.myapp@1.0.0meroctl --node node1 app install --path ./my-app.mpkmeroctl --node node1 app lsPositional PACKAGE@VERSION installs a published version from the one application
source the node is configured with (its [registry]
section); --path installs a local signed bundle. The two are mutually exclusive,
and --watch applies to a --path install only.
A .mpk bundle is the only thing either form installs - there is no raw-.wasm
path and no way to name a URL. A node in dht mode has no registry to ask, so
PACKAGE@VERSION reports the coordinates as unpublished there; use --path, or let
governance deliver the application.
list-packages, list-versions and get-latest-version all read this node’s own
installed rows, not the registry. Because a bundle’s application id is version-stable,
a package occupies one row, so list-versions reports the version this node currently
holds.
context
Section titled “context”| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
list (ls) |
List all contexts. | — |
create |
Create a new context. | --application-id/-a, --group-id (required), --params/-p, --watch/-w <path>, --seed/-s, --name (context alias), --service, --identity-secret, --group-name |
get <CONTEXT> |
Fetch context details: info, client-keys, or storage. |
CONTEXT, subcommand |
delete (del) <CONTEXT> |
Delete a context. | CONTEXT |
update |
Update the application in a context. | --context/-c, --application-id/-a, --path, --watch/-w |
watch (ws) |
Stream context events, optionally run a command. | --context, -x/--exec, -n/--count |
sync |
Explicitly request a sync. | --context/-c, --all |
identity |
Manage context identities (see below). | subcommand |
alias |
Manage context aliases: add, remove, get, list. |
subcommand |
use <CONTEXT> |
Set the default context. | CONTEXT, --force |
context identity
Section titled “context identity”| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
list (ls) |
List identities in a context. | --context, --owned |
generate (new) |
Generate a new identity keypair. | — |
meroctl --node node1 context create --application-id <app_id> --group-id <group_id>meroctl --node node1 group members set-caps <group_id> <identity> --can-create-context| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
get <GROUP_ID> |
Get information about a group. | GROUP_ID |
delete (del) <GROUP_ID> |
Delete a group. | GROUP_ID |
members |
Manage group members (see below). | subcommand |
contexts |
List (list/ls) or detach contexts in a group. |
subcommand |
metadata |
Show / update a group’s metadata record. | subcommand |
member-metadata |
Show / update a member’s metadata record. | subcommand |
context-metadata |
Show / update a registered context’s metadata. | subcommand |
reparent <GROUP_ID> <NEW_PARENT_ID> |
Atomically move a group to a new parent. | GROUP_ID, NEW_PARENT_ID |
subgroups <GROUP_ID> |
List direct subgroups of a group. | GROUP_ID |
upgrade |
trigger, status, or retry a group upgrade. |
subcommand |
sync <GROUP_ID> |
Sync a group from local state. | GROUP_ID |
join-context <CONTEXT_ID> |
Join a context via group membership. | CONTEXT_ID |
leave-context <CONTEXT_ID> |
Locally opt out of a context (no DAG op). | CONTEXT_ID |
leave <GROUP_ID> |
Voluntarily leave a group (publishes MemberLeft). | GROUP_ID |
settings |
Manage group-level default settings (see below). | subcommand |
group members
Section titled “group members”| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
list (ls) <GROUP_ID> |
List all members of a group. | GROUP_ID |
add <GROUP_ID> <IDENTITY> |
Add a member to a group. | --role |
remove <GROUP_ID> <IDENTITY...> |
Remove members from a group. | identities |
set-role <GROUP_ID> <IDENTITY> <ROLE> |
Update a member’s role. | — |
set-capabilities (set-caps) |
Set capabilities for a member (admin-only). | GROUP_ID, IDENTITY, --can-* flags |
get-capabilities (get-caps) |
Get a member’s capabilities. | GROUP_ID, IDENTITY |
check-access |
Check if an identity can join a context. | GROUP_ID, identity |
group settings
Section titled “group settings”| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
get <GROUP_ID> |
Get current default settings. | GROUP_ID |
set-default-capabilities (set-default-caps) |
Set default capabilities for new members. | GROUP_ID, --can-* flags |
set-subgroup-visibility (set-subgroup-vis) |
Set subgroup visibility (open/restricted). | GROUP_ID, --mode |
meroctl --node node1 group members list <group_id>meroctl --node node1 group reparent <group_id> <new_parent_id>namespace
Section titled “namespace”Alias: ns. Namespaces are root groups (application instances).
| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
create |
Create a new namespace. | --application-id, --name, --bytecode-id (alias: --app-key) |
get <NAMESPACE_ID> |
Get details about a namespace. | NAMESPACE_ID |
delete (del) <NAMESPACE_ID> |
Delete a namespace. | NAMESPACE_ID |
invite <NAMESPACE_ID> |
Create an invitation for a namespace. | NAMESPACE_ID, --expiration-timestamp, --recursive, --admitter, --admitter-addr (see below) |
join <NAMESPACE_ID> <INVITATION> |
Join a namespace using an invitation. | NAMESPACE_ID, invitation JSON |
leave <NAMESPACE_ID> |
Leave a namespace (cascades through descendants). | NAMESPACE_ID |
groups <NAMESPACE_ID> |
List direct groups under a namespace. | NAMESPACE_ID |
create-group <NAMESPACE_ID> |
Create a child group in a namespace. | NAMESPACE_ID, --alias |
list (ls) |
List all namespaces. | — |
identity (id) <NAMESPACE_ID> |
Get this node’s identity for a namespace. | NAMESPACE_ID |
meroctl --node node1 namespace create --application-id <app_id>meroctl --node node2 namespace join <namespace_id> '<invitation_json>'Invitation admitters and addresses
Section titled “Invitation admitters and addresses”namespace invite takes two optional flags that decide who may admit a joiner
and where the joiner should look for them. Both are repeatable.
| flag | value | what it does |
|---|---|---|
--admitter |
account, 64 hex | Narrows who may admit this invitation. Omit and the node uses the group’s admins plus its TEE nodes. Signed, so it cannot be redirected afterwards. |
--admitter-addr |
/ip4/…/tcp/…/p2p/<peer-id> |
Where a joiner can dial an admitter. Unsigned, and must include the /p2p/ suffix. |
You usually need neither. The node names the admitters itself and fills in addresses from what it already has on file. Reach for them when:
--admitter— you want a narrower set than every admin and TEE node.--admitter-addr— the node’s address for an admitter is stale or missing (it has not seen that peer within the cache’s 24-hour window).
# Ordinary case: let the node decide who admits and where they are.meroctl namespace invite $NS
# Narrow admission to one account, and say where it is.meroctl namespace invite $NS \ --admitter 8f2c...e1 \ --admitter-addr /ip4/203.0.113.7/tcp/2528/p2p/12D3KooWExampleAddresses are unsigned, deliberately. A wrong one costs a failed dial and
nothing more: libp2p authenticates the peer id on connect, and the signed
--admitter list still decides who may answer. See
Direct Admission for why an address travels
unsigned while the admitter list cannot.
| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
list (ls) |
List all blobs. | — |
upload |
Upload a blob from a file. | --file, --context-id |
download |
Download a blob to a file. | --blob-id, --output, --context-id |
info |
Get information about a blob. | --blob-id |
delete (rm) |
Delete a blob by its ID. | --blob-id |
meroctl --node node1 blob upload --file /path/to/filemeroctl --node node1 blob download --blob-id <blob_id> --output /path/to/outputCall a method on a context. Whether a call mutates state or runs as a read-only
view is determined by the method’s own declaration (#[app::view]), not by a
CLI flag; the call executes under the context’s default identity.
| Arg / flag | Type / values | Default | Purpose |
|---|---|---|---|
<METHOD> |
string | — | Method to call (omit with --interactive). |
--context/-c <CONTEXT> |
context alias/id | default |
Context to call the method on. |
--args <JSON> |
JSON | {} |
JSON arguments to pass to the method. |
--id <ID> |
string | — | JSON-RPC call id. |
--interactive/-i |
bool | false |
Open a persistent WebSocket shell and run many calls. |
--timeout <SECONDS> |
u64 | 120 |
Per-call timeout in the interactive shell (0 disables). |
meroctl --node <NODE> call <METHOD> --context <CONTEXT_ID> --args '<ARGS_JSON>'meroctl --node <NODE> call -i --context <CONTEXT_ID>| Subcommand | Purpose | Key args |
|---|---|---|
start <path> |
Build, install, create/reuse a context, optionally watch. | --group-id (required), --watch/-w, --new, --params/-p, --seed/-s, --no-build |
<path> may be a signed .mpk bundle, or a project directory with a
Cargo.toml (which is bundled with cargo mero bundle --dev --no-icon into
dist/dev.mpk).
meroctl --node node1 dev start ./my-app --group-id <group_id> --watchnetwork
Section titled “network”| Subcommand | Purpose |
|---|---|
status |
Dump swarm connectivity: listen/external addresses, relay reservations, rendezvous registrations, DCUtR upgrades, AutoNAT. |
meroctl --node node1 network statusmeroctl --output-format json --node node1 network statusNo subcommand — returns the number of connected peers.
meroctl --node node1 peers| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
fleet-join <GROUP_ID> |
Announce this node as a TEE fleet member and auto-join contexts in the group once admitted. | GROUP_ID |
meroctl --node node1 tee fleet-join <GROUP_ID>node (connection management)
Section titled “node (connection management)”These commands manage meroctl’s local connection config — they do not call
a node. A node may be a local home directory or a remote URL.
| Subcommand (aliases) | Purpose | Key args |
|---|---|---|
add (connect) <NAME> <LOCATION> |
Add or connect to a node. | LOCATION (path or URL), --access-token, --refresh-token |
remove (rm, disconnect) <NAME> |
Remove a node connection. | NAME |
use <NAME> |
Set a node as active (default for commands). | NAME |
list (ls) |
List all configured nodes. | — |
identity (id) |
Display the node’s peer ID (local nodes only). | — |
meroctl node add node1 /path/to/homemeroctl node add node3 http://public.node.commeroctl node use node1meroctl node lsFor a remote node that needs authentication, login starts automatically; pass
--access-token (and optionally --refresh-token) to skip it with explicit
JWTs.
meroctl’s own config (nodes.toml)
Section titled “meroctl’s own config (nodes.toml)”The node commands above read and write a single client-side config file at
~/.config/calimero/meroctl/nodes.toml (under your platform config dir). This
is meroctl’s own state — it is never sent to a node. It records:
- Each configured node connection under its connection name, as either a
local node (a
pathto a node home) or a remote node (aurl). active_node— the connection used when neither--nodenor--apiis given.- Per-node JWT access and refresh tokens, saved here after a login so subsequent commands reuse them.
Because it holds bearer tokens, the file is written with owner-only permissions
(0600) and re-written atomically with those permissions on every update. Treat
it as secret material:
active_node = "node1"
[nodes.node1]path = "/home/alice/.calimero"
[nodes.node1.jwt_tokens]access_token = "<jwt>"refresh_token = "<jwt>"
[nodes.prod]url = "https://node.example.com/"End-to-end session
Section titled “End-to-end session”A complete run from an installed application bundle to reading back state. Each
command below adds --output-format json so the ids you need to copy forward
are machine-readable; drop it to get the default human-readable table.
-
Install the application. The node returns an
applicationId:Terminal window meroctl --node node1 app install \--path ./dist/com.example.kv-store-0.1.0.mpk \--output-format json{"data":{"applicationId":"<app-id>"}} -
Create a namespace bound to that application id. A namespace is a root group; its id is the
--group-ida context attaches to:Terminal window meroctl --node node1 namespace create --application-id <app-id> \--output-format json{"data":{"namespaceId":"<namespace-id>"}} -
Create a context from the application id and the namespace id, giving it a memorable alias with
--name:Terminal window meroctl --node node1 context create \--application-id <app-id> \--group-id <namespace-id> \--name my-kv \--output-format json{"data":{"contextId":"<context-id>","memberPublicKey":"<public-key>"}} -
Call a mutating method. Whether a call mutates or only reads is decided by the method’s own
#[app::view]declaration, not a CLI flag —sethere writes a key. Refer to the context by its alias:Terminal window meroctl --node node1 call set \--context my-kv \--args '{"key": "hello", "value": "world"}' \--output-format jsonsetreturns(), so the result isnull:{"jsonrpc":"2.0","id":null,"result":{"output":null}} -
Read it back with a view method.
getis declared#[app::view], so the samecallcommand runs it read-only:Terminal window meroctl --node node1 call get \--context my-kv \--args '{"key": "hello"}' \--output-format json{"jsonrpc":"2.0","id":null,"result":{"output":"world"}}
The value you stored comes back under result.output.