> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myme.so/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

> @withmarfa/cli — the `my` command-line client for a Marfa server

The Marfa CLI is a Commander-based terminal client published on npm as `@withmarfa/cli`. Wraps the TypeScript SDK; every operation the SDK exposes is reachable as a `my <resource> <verb>` subcommand. Human-readable output by default; auto-switches to JSON when stdout is a pipe or redirected file.

## Install

```bash theme={null}
npm install -g @withmarfa/cli
# or: pnpm add -g @withmarfa/cli
# or: yarn global add @withmarfa/cli
```

Installs the `my` binary on your `PATH`. Confirm with `my --help`.

## Authenticate

Two paths, both work; OAuth wins when present.

**Interactive (recommended for humans)** — `my auth login` runs the OAuth Device Authorization Grant. The CLI prints a URL and code; approve in a browser; tokens persist at `~/.marfa/<instance>.json` and refresh transparently.

```bash theme={null}
my --url https://marfa.example.com auth login
my auth whoami
my items list           # uses the stored OAuth tokens
my auth logout
```

**API key (recommended for scripts and CI)** — set `MARFA_API_URL` and `MARFA_API_KEY`, or pass `--url` / `--key` per call.

```bash theme={null}
export MARFA_API_URL=https://marfa.example.com
export MARFA_API_KEY=marfa_k1_...
my items list
```

OAuth tokens take precedence when a stored session matches the resolved URL — an explicit `my auth login` should beat a stale env var. To force the API-key path: `my auth logout`, unset `MARFA_API_KEY`, or pass `--key` explicitly.

## Command surface

```
my [--url <url>] [--key <key>] [--instance <name>] [--json] [--verbose]
  auth         login, logout, whoami, delete-account
  profile      show, update, avatar {set, clear}
  items        create, get, list, update, delete, restore, transition,
               search, versions, edges, backrefs, bulk, action
  metadata     get, tag, untag, set, merge, list-tags, extensions {get, set}
  edges        create, list, update, delete, types {list, create, delete}, bulk
  blobs        upload, download
  types        list, describe, register, update, delete
  keys         create, update, list, revoke
  apps         register
  webhooks     create, list, get, update, delete, deliveries
  tenant       config {get, set}
  connections  list, get, configure, pause, resume, install, uninstall,
               logs, activity, preview-event, verify, replay-dlq
  platform     tenants {list, show, suspend, unsuspend, quotas, metrics},
               keys {list},
               account-deletion {purge-now}
  export       [--output <file.json>]
  health
```

`my --help` and `my <subcommand> --help` are the canonical reference for flags. The sections below walk the more substantial surfaces.

## Examples

### Items and edges

```bash theme={null}
# Plain create
my items create --type core.note --prop title="Hello" --prop body="World"

# Atomic item + outbound edges in one transaction
my items create --type core.highlight --prop text="..." \
  --edge references:<doc-id> --edge about:<topic-id>

# Natural-key upsert — re-running with the same --source + --source-id
# updates the existing row in place. Human mode prints "Created." or
# "Updated (natural-key match)." based on the server's 200/201.
my items create --type core.note --prop title="Hello" \
  --source my-script --source-id intro-001

# Filter by edge / backref / state / tier
my items list --type core.note --tier library --edge in-thread=<root-id>
my items list --backref about=<topic-id>
```

### Scoped API keys

`my keys create` exposes the full permission-map surface. Permission flags use `=` between key and level (the `:` separator collides with the edge-create grammar).

```bash theme={null}
my keys create --label app-cred --source acme-app --role member \
  --type-perm core.note=write \
  --type-perm core.task=read \
  --extension-perm app.acme=write \
  --edge-perm about=write

my keys update <key-id> --type-perm core.note=read
```

`--is-platform` is silently coerced to false unless the calling credential is itself platform-flagged — the server enforces this.

### Metadata extensions

The metadata layer's `extensions` map is a JSON sidecar keyed by namespace. Reserved namespaces (e.g. `connection.runtime`) have constrained write semantics; the CLI is the surface, not the gate.

```bash theme={null}
# Set an app-scoped namespace from inline JSON
my metadata extensions set <item-id> app.acme --data '{"cursor":"abc"}'

# Or from a file / stdin
my metadata extensions set <item-id> connection.runtime --file runtime.json
echo '{"k":1}' | my metadata extensions set <item-id> app.foo

# Read back
my metadata extensions get <item-id>
my metadata extensions get <item-id> --namespace app.acme

# Distinct tag enumeration with usage counts
my metadata list-tags
```

### Connections

`my connections` is the operator-facing surface for `system.connection` items of kind `integration`.

```bash theme={null}
my connections list                              # all connections in the tenant
my connections list --integration acme.gdrive    # filter by integration
my connections get <connection-id>               # hydrates connection.runtime when present
my connections configure <connection-id> --extension connection.runtime --data '{"cursor":"..."}'
my connections pause <connection-id>             # archived
my connections resume <connection-id>            # active
my connections logs <connection-id> --severity error
my connections preview-event --item <id> --event-type item.created [--connection <id>]
my connections uninstall <connection-id>         # orchestrated teardown
```

`install` calls the platform's `POST /connections/install` orchestration — the admin-only JSON sibling of the browser consent flow. Returns the connection id, seed credential id, and an activity id.

`uninstall` calls the platform's `POST /connections/:id/uninstall` orchestration: revokes runtime credentials, deletes upstream OAuth tokens, revokes leased tokens, disables inbound webhook subscriptions, transitions state to `revoked`, emits `system.activity`, audits.

`preview-event` is a debug verb. Given an item id and an event type, it renders the queue envelopes the bridge would emit to each subscribed connector — without invoking any handler. Each envelope carries a `dispatch_reason` (`ok`, `self_event`, `cross_tenant`, `hop_budget_exceeded`, `subscription_inactive`) so operators can see why a subscriber would or wouldn't receive a given event. Pure server-side; safe to run repeatedly while debugging fanout.

`verify <id> --event <json>` synchronously dispatches a sample event through `runtime-control` into the connector via service binding. Operator-debug; requires a platform credential. Reads the runtime-control endpoint from `MARFA_RUNTIME_CONTROL_URL` or `--runtime-control-url`.

`replay-dlq <id> [--message-ids id1,id2] [--yes]` re-enqueues DLQ messages back onto their main queues for a connection. Destructive (handlers re-run upstream); interactive confirmation unless `--yes` is passed (and `--yes` is required in non-TTY contexts). `my connections logs <id> --dlq` reads the same DLQ surface.

### Tenant config

```bash theme={null}
my tenant config get
my tenant config set --file ./tenant-config.json
```

The `set` body is a full `TenantConfig` JSON object. Plumbing for the schema-enforcement levers (TSC42 §5: `strict_mode`, `source_allowlist`, `source_filter`) and feed-tier retention. Editing tenant config is rare enough that per-field flags would invent a parallel grammar that drifts from the wire shape; raw JSON is the call.

### Webhook deliveries

```bash theme={null}
my webhooks list
my webhooks deliveries <webhook-id>           # recent attempts
my webhooks deliveries <webhook-id> --limit 200
```

### Account deletion

`my auth delete-account` initiates the hosted-mode account-deletion flow. The CLI verb is initiation-only — the server emails a confirmation link; clicking it transitions the account to `pending_deletion`. See [Account lifecycle](/self-hosting/account-lifecycle) for the full state machine.

```bash theme={null}
my auth delete-account            # interactive: prints flow summary, prompts y/N
my auth delete-account --yes      # non-interactive (required outside a TTY)
```

### Profile

`my profile` reads and updates the calling user's own profile — name, handle, avatar. The avatar surface is two sub-verbs (`set` uploads an image, `clear` reverts to the deterministic placeholder).

```bash theme={null}
my profile show
my profile update --name "Alex"
my profile avatar set ./avatar.png
my profile avatar clear
```

### Platform (operator)

`my platform` is the cross-tenant operator surface, gated on platform credentials. The two trees:

* `platform tenants` — list, inspect, suspend / unsuspend, set quotas, per-tenant metrics.
* `platform account-deletion` — force the next pending-delete purger sweep without waiting for the 1-hour cadence.

```bash theme={null}
my platform tenants list
my platform tenants show <tenant-id>
my platform tenants metrics <tenant-id>
my platform tenants suspend <tenant-id> [--yes]
my platform tenants unsuspend <tenant-id> [--yes]
my platform tenants quotas <tenant-id> --items 10000 --webhooks 50 \
  --blobs 5000 --storage-gb 25 --rate-per-minute 600 [--yes]

my platform keys list <tenant-id>

my platform account-deletion purge-now [--yes]
```

Suspend / unsuspend / quota writes prompt for confirmation unless `--yes` is passed; the prompt is required in non-TTY contexts. `purge-now` is the operator escape hatch when an account just crossed its grace window and you want the cascade now rather than at the next 1-hour tick.

## Output modes

Human-readable tables in an interactive terminal; JSON when stdout is a pipe, a redirected file, or `--json` is set explicitly. There's no `--human` flag — scripts that consume output get JSON, full stop.

```bash theme={null}
my items list                  # table in a TTY
my items list | jq '.[0]'      # JSON automatically
my items list --json           # force JSON in a TTY
my items list > items.json     # JSON automatically (non-TTY)
```

## Multi-instance config

`--instance <name>` (or `MARFA_INSTANCE`) selects the config-file slot under `~/.marfa/<instance>.json`. Use it to keep separate sessions per server (e.g. `production` + `staging`):

```bash theme={null}
my --instance production --url https://marfa.example.com auth login
my --instance staging    --url https://staging.example.com auth login

my --instance production items list
my --instance staging    items list
```

The default instance name is `default`.

## Repo

Source at [`withmarfa/cli`](https://github.com/withmarfa/cli). Commander-based; consumes `@withmarfa/sdk` as a regular npm dep. Build and code rules in the repo's `CLAUDE.md`.
