@cordn/cli
v0.9.0
Published
Persistent MLS client for Cordn: interactive commands, encrypted state, and durable agent inbox/outbox queues.
Downloads
423
Maintainers
Readme
@cordn/cli
Persistent MLS client for Cordn. It supports an interactive REPL, one-shot commands, encrypted restart-safe state, live group watches, and durable filesystem queues for local agents and automations.
Install
npm install --global @cordn/cli
cordn --version
cordn --helpNode.js 20.12 or newer is required.
Bundled documentation
The package includes version-matched offline documentation. These commands do not require state, network access, or coordinator configuration:
cordn docs
cordn docs quickstart
cordn docs commands
cordn docs agent
cordn docs daemon
cordn docs queues
cordn docs securityHosted default
A fresh client uses:
coordinator: 92753cbe63e943d0c4a0c61d745437892af6e98f179ce04a7a863aad4e00b1a5
relays:
wss://relay.contextvm.org
wss://relay2.contextvm.org
wss://relay.primal.netOverride these with --server-pubkey and one or more --relay options. Persistent snapshots remember the coordinator and relays used to create them. Local development can continue deriving the coordinator public key from CORDN_SERVER_PRIVATE_KEY and reading comma-separated CORDN_RELAY_URLS. --transport-encryption required (or CORDN_TRANSPORT_ENCRYPTION=required) gift-wraps coordinator requests so relays cannot read their metadata; see cordn docs security.
Quickstart
STATE="${XDG_STATE_HOME:-$HOME/.local/state}/cordn/session.json"
cordn --state-file "$STATE" --command status
cordn --state-file "$STATE" --command "gen-kp main"
cordn --state-file "$STATE" --command "create-group office"
cordn --state-file "$STATE" --command "send-to office hello"
cordn --state-file "$STATE" --command "messages office"The first command generates a client identity and creates:
$STATE— AES-256-GCM encrypted identity, MLS state, cursors, and history$STATE.key— the hex-encoded 32-byte snapshot encryption key
Back up both files securely. Only one process may use a snapshot at a time.
Interactive mode
cordn --state-file "$STATE"Useful commands include:
status
whoami
gen-kp [alias] [--last-resort] [--local-only]
publish-kp <alias> [--coordinator <pubkey>]
key-packages
available-kps
create-group <alias> [keyPackageAlias] [--watch]
groups
group-info [groupAlias]
use <groupAlias> [--watch]
add-member <groupAlias> <stablePubkeyOrKeyPackageRef>
remove-member <groupAlias> <stablePubkey>
fetch-welcomes
welcomes
accept-welcome <welcomeIdOrKeyPackageReference> [groupAlias] [--watch]
send <message...>
send-to <groupAlias> <message...>
sync [groupAlias]
sync-all
watch-all
messages [groupAlias]
issues [groupAlias]
exit | quitRun cordn docs commands for the complete version-matched command reference.
After selecting a group with use, plain text sends a message and an empty line synchronizes it. whoami prints the private identity key; prefer status unless explicit export is intended.
One-shot mode
cordn --state-file "$STATE" --command "groups"
cordn --state-file "$STATE" --command "sync office"
cordn --state-file "$STATE" --command "send-to office hello"Use send-to rather than send: selected-group context does not survive between processes.
Persistent daemon
Bootstrap the identity and publish a KeyPackage before starting a long-running agent:
ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/cordn/agent"
mkdir -p "$ROOT/inbox" "$ROOT/outbox"
chmod 700 "$ROOT" "$ROOT/inbox" "$ROOT/outbox"
cordn \
--state-file "$ROOT/session.json" \
--daemon \
--group-alias office \
--outbox-dir "$ROOT/outbox" \
--inbox-dir "$ROOT/inbox"The daemon fetches matching Welcomes, accepts them using local KeyPackage material, catches groups up before watching, writes decrypted inbound messages to the inbox, and processes ordered JSON outbox jobs. It must be the sole writer of its snapshot.
Both queue directions are at-least-once. Producers must write a non-.json temporary file and atomically rename it into the outbox. Consumers must claim inbox records and deduplicate by groupAlias + cursor + id.
Run cordn docs queues for exact schemas and crash behavior.
Encrypted media
Pass --media-dir <path> to enable send-media and save-media. Media is encrypted client-side using an MLS exporter-derived key and stored as an opaque content-addressed blob. Multiple local clients must share the same media directory for filesystem-backed exchange; a network content store is required across hosts.
See the encrypted-media specification.
Library usage
The same persistent client the executable is built on is importable from Node
(the package is Node-only; it is not browser-safe like @cordn/core):
import { openPersistentSession } from "@cordn/cli";
const opened = await openPersistentSession({
stateFile: "./state/session.json",
});
const { session } = opened;
await session.generateKeyPackage("bot", { lastResort: true });
await session.publishKeyPackage("bot");
session.onGroupEvent((event) => {
if (event.type === "messages-ingested")
console.log(event.groupAlias, event.received);
});
await session.watchAllGroups();
await opened.persist();
// ...
await opened.close();openPersistentSession provides exactly what the daemon relies on: an exclusive
state lock, snapshot restore, identity check, coordinator/relay precedence, and
a serialized durable-write queue (persist, flush, close). Omit stateFile
for an ephemeral session. transportEncryption: "required" gift-wraps coordinator
requests, and coordinators (keyed by server pubkey, each with its own relays)
makes further coordinators reachable next to the default one; neither is saved
in the snapshot, so pass them on every open. Everything else (CliSession, snapshot helpers,
queue helpers, defaults, error classes) is exported from the same barrel.
When consuming the package from a source checkout (for example link: to
packages/cli), the dev-time exports point at .ts sources: run on Node >= 22.18
(type stripping) and set rewriteRelativeImportExtensions: true in the consumer
tsconfig. The published package ships compiled dist/lib/** with declarations.
Wiring an agent
The fastest integration is to hand your agent the bundled agent guide —
docs/AGENT.md (or cordn docs agent from an installed CLI) —
and let it wire itself: it documents both integration paths (one-shot CLI
commands, filesystem queues, and the library above), the safety rules, and the
trust boundary in agent-readable form.
Development from the monorepo
pnpm install
pnpm run client:cli -- --state-file /tmp/cordn-session.json
pnpm --filter @cordn/cli run build # dist/cli.js (bin) + dist/lib/** (library)
pnpm --filter @cordn/cli run pack:checkThe reference sync implementation follows fetch-first-then-subscribe semantics, independent per-group cursors, one inbound ingestion path for catch-up and live records, ciphertext-based self-echo reconciliation, and inbound confirmation before pending epoch operations are finalized.
License
MIT
