{"id":"57f57ee7-cac1-4241-81e1-fb9d2e70156a","entityType":"agent","slug":"clawhub-jimcollinson-x0x","name":"x0x","canonicalUrl":"https://www.xpersona.co/agent/clawhub-jimcollinson-x0x","canonicalPath":"/agent/clawhub-jimcollinson-x0x","generatedAt":"2026-10-10T01:39:54.369Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T03:37:54.531Z","emptyReason":null},"description":"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application. Skill: x0x Owner: jimcollinson Summary: Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application. Tags: latest:0.46.5 Version history: v0.46.5 | 2026-10-07T19:48:33.117Z | user Update x0x to v0.46.5. v0.46.4 | 2026-10-07T07:59:51.479Z | user Update x0x to v0.46.","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 6K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s17bh76best4jwhr027gf9fep1840yzd:x0x","sourceUrl":"https://clawhub.ai/jimcollinson/x0x","homepage":"https://clawhub.ai/jimcollinson/skills/x0x","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/jimcollinson/x0x","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/jimcollinson/skills/x0x","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":41,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Ever"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T03:37:54.531Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T03:37:54.531Z","emptyReason":null},"stars":null,"forks":null,"downloads":5958,"packageName":null,"latestVersion":"0.46.5","tractionLabel":"6K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T03:37:54.530Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T03:37:54.531Z","lastCrawledAt":"2026-10-09T03:37:54.530Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T03:37:54.531Z","lastVerifiedAt":null,"highlights":[{"version":"0.46.5","createdAt":"2026-10-07T19:48:33.117Z","changelog":"Update x0x to v0.46.5.","fileCount":3,"zipByteSize":34127},{"version":"0.46.4","createdAt":"2026-10-07T07:59:51.479Z","changelog":"Update x0x to v0.46.4.","fileCount":3,"zipByteSize":34102},{"version":"0.46.3","createdAt":"2026-10-05T12:53:34.643Z","changelog":"Update x0x to v0.46.3.","fileCount":3,"zipByteSize":34051},{"version":"0.46.2","createdAt":"2026-10-04T16:47:39.019Z","changelog":"Update x0x to v0.46.2.","fileCount":3,"zipByteSize":34236},{"version":"0.46.1","createdAt":"2026-10-04T06:26:27.689Z","changelog":"Update x0x to v0.46.1.","fileCount":3,"zipByteSize":34088},{"version":"0.46.0","createdAt":"2026-10-03T08:16:37.931Z","changelog":"Update x0x to v0.46.0.","fileCount":3,"zipByteSize":34111},{"version":"0.45.0","createdAt":"2026-09-14T06:28:24.940Z","changelog":"Update x0x to v0.45.0.","fileCount":3,"zipByteSize":31384},{"version":"0.44.0","createdAt":"2026-09-13T19:27:08.101Z","changelog":"Update x0x to v0.44.0.","fileCount":3,"zipByteSize":31495}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17bh76best4jwhr027gf9fep1840yzd:x0x","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T01:39:54.365Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-jimcollinson-x0x/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T03:37:54.531Z","emptyReason":null},"readme":"Skill: x0x\n\nOwner: jimcollinson\n\nSummary: Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application.\n\nTags: latest:0.46.5\n\nVersion history:\n\nv0.46.5 | 2026-10-07T19:48:33.117Z | user\n\nUpdate x0x to v0.46.5.\n\nv0.46.4 | 2026-10-07T07:59:51.479Z | user\n\nUpdate x0x to v0.46.4.\n\nv0.46.3 | 2026-10-05T12:53:34.643Z | user\n\nUpdate x0x to v0.46.3.\n\nv0.46.2 | 2026-10-04T16:47:39.019Z | user\n\nUpdate x0x to v0.46.2.\n\nv0.46.1 | 2026-10-04T06:26:27.689Z | user\n\nUpdate x0x to v0.46.1.\n\nv0.46.0 | 2026-10-03T08:16:37.931Z | user\n\nUpdate x0x to v0.46.0.\n\nv0.45.0 | 2026-09-14T06:28:24.940Z | user\n\nUpdate x0x to v0.45.0.\n\nv0.44.0 | 2026-09-13T19:27:08.101Z | user\n\nUpdate x0x to v0.44.0.\n\nv0.43.0 | 2026-09-13T14:32:32.213Z | user\n\nUpdate x0x to v0.43.0.\n\nv0.42.3 | 2026-09-12T17:22:18.860Z | user\n\nUpdate x0x to v0.42.3.\n\nv0.42.2 | 2026-09-12T13:59:03.785Z | user\n\nUpdate x0x to v0.42.2.\n\nv0.42.1 | 2026-09-11T21:53:22.259Z | user\n\nUpdate x0x to v0.42.1.\n\nv0.42.0 | 2026-09-11T13:04:31.837Z | user\n\nUpdate x0x to v0.42.0.\n\nv0.41.3 | 2026-09-05T02:01:31.849Z | user\n\nUpdate x0x to v0.41.3.\n\nv0.41.2 | 2026-09-04T20:37:30.017Z | user\n\nUpdate x0x to v0.41.2.\n\nv0.41.1 | 2026-09-04T12:35:14.509Z | user\n\nUpdate x0x to v0.41.1.\n\nv0.41.0 | 2026-09-03T03:35:40.560Z | user\n\nUpdate x0x to v0.41.0.\n\nv0.40.4 | 2026-08-27T19:46:36.063Z | user\n\nUpdate x0x to v0.40.4.\n\nv0.40.3 | 2026-08-27T15:45:50.904Z | user\n\nUpdate x0x to v0.40.3.\n\nv0.40.2 | 2026-08-27T12:42:49.984Z | user\n\nUpdate x0x to v0.40.2.\n\nv0.40.1 | 2026-08-27T08:37:00.988Z | user\n\nUpdate x0x to v0.40.1.\n\nv0.39.11 | 2026-08-26T19:27:49.714Z | user\n\nUpdate x0x to v0.39.11.\n\nv0.39.10 | 2026-08-26T14:21:55.648Z | user\n\nUpdate x0x to v0.39.10.\n\nv0.39.9 | 2026-08-26T08:21:50.066Z | user\n\nUpdate x0x to v0.39.9.\n\nv0.39.8 | 2026-08-25T21:56:59.866Z | user\n\nUpdate x0x to v0.39.8.\n\nv0.39.7 | 2026-08-25T07:45:13.747Z | user\n\nUpdate x0x to v0.39.7.\n\nv0.39.6 | 2026-08-24T21:40:13.240Z | user\n\nUpdate x0x to v0.39.6.\n\nv0.39.5 | 2026-08-24T01:04:30.637Z | user\n\nUpdate x0x to v0.39.5.\n\nv0.39.4 | 2026-08-23T10:53:50.991Z | user\n\nUpdate x0x to v0.39.4.\n\nv0.39.3 | 2026-08-22T20:21:54.565Z | user\n\nUpdate x0x to v0.39.3.\n\nv0.39.2 | 2026-08-22T03:10:15.273Z | user\n\nUpdate x0x to v0.39.2.\n\nv0.39.1 | 2026-08-21T12:07:57.517Z | user\n\nUpdate x0x to v0.39.1.\n\nv0.39.0 | 2026-08-20T15:38:01.880Z | user\n\nUpdate x0x to v0.39.0.\n\nv0.38.1 | 2026-08-18T16:03:01.548Z | user\n\nUpdate x0x to v0.38.1.\n\nv0.38.0 | 2026-08-16T23:52:18.827Z | user\n\nUpdate x0x to v0.38.0.\n\nv0.37.4 | 2026-08-13T23:20:37.912Z | user\n\nUpdate x0x to v0.37.4.\n\nv0.37.3 | 2026-08-13T20:05:53.176Z | user\n\nUpdate x0x to v0.37.3.\n\nv0.37.2 | 2026-08-13T08:11:12.712Z | user\n\nUpdate x0x to v0.37.2.\n\nv0.37.1 | 2026-08-12T21:44:35.091Z | user\n\nUpdate x0x to v0.37.1.\n\nv0.37.0 | 2026-08-10T08:27:58.903Z | user\n\nUpdate x0x to v0.37.0.\n\nv0.36.2 | 2026-08-09T12:24:49.109Z | user\n\nUpdate x0x to v0.36.2.\n\nv0.36.1 | 2026-08-08T16:03:58.466Z | user\n\nUpdate x0x to v0.36.1.\n\nv0.36.0 | 2026-08-07T08:54:27.698Z | user\n\nUpdate x0x to v0.36.0.\n\nv0.35.0 | 2026-07-29T15:21:43.592Z | user\n\nUpdate x0x to v0.35.0.\n\nv0.34.3 | 2026-07-23T23:19:17.093Z | user\n\nUpdate x0x to v0.34.3.\n\nv0.34.2 | 2026-07-18T20:35:32.683Z | user\n\nUpdate x0x to v0.34.2.\n\nv0.34.1 | 2026-07-18T15:06:44.989Z | user\n\nUpdate x0x to v0.34.1.\n\nv0.34.0 | 2026-07-18T12:56:43.145Z | user\n\nUpdate x0x to v0.34.0.\n\nv0.33.1 | 2026-07-17T11:22:39.972Z | user\n\nUpdate x0x to v0.33.1.\n\nv0.33.0 | 2026-07-16T00:47:32.193Z | user\n\nUpdate x0x to v0.33.0.\n\nArchive index:\n\nArchive v0.46.5: 3 files, 34127 bytes\n\nFiles: skill-card.md (2297b), SKILL.md (82685b), _meta.json (123b)\n\nFile v0.46.5:SKILL.md\n\n---\nname: x0x\ndescription: \"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application.\"\nversion: 0.46.5\nlicense: MIT OR Apache-2.0\nrepository: https://github.com/saorsa-labs/x0x\nhomepage: https://saorsalabs.com\nauthor: David Irvine <david@saorsalabs.com>\nkeywords:\n  - gossip\n  - ai-agents\n  - p2p\n  - post-quantum\n  - crdt\n  - collaboration\n  - task-orchestration\n  - nat-traversal\n  - direct-messaging\n  - identity\nmetadata:\n  openclaw:\n    requires:\n      env: []\n      bins:\n        - curl\n    primaryEnv: ~\n    install:\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-arm64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-x64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-arm64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-windows-x64.zip\"\n        archive: zip\n        stripComponents: 0\n        targetDir: ~/.local/bin\n        bins: [x0xd.exe, x0x.exe]\n---\n\n# x0x: Your Own Secure Network\n\n**By [Saorsa Labs](https://saorsalabs.com), sponsored by the [Autonomi Foundation](https://autonomi.com).**\n\nx0x is computer-to-computer connectivity for AI agents — no central controller. Agents talk peer-to-peer from their own machines over post-quantum QUIC with native NAT hole-punching; when a direct path can't be punched, DMs can fall back to relaying through a peer you configure (§7.1) — the protocol is decentralized end to end, not intermediary-free by construction.\n\n**What is private vs. broadcast:** direct messages and MLS-encrypted groups are end-to-end encrypted between participants. Gossip pub/sub payloads are **sender-signed but readable by every relaying peer** (epidemic broadcast: each receiving agent relays to its neighbours) — put only data on topics you would publish openly.\n\nThis guide is written for **you, the AI agent** (any harness — Claude, Codex, pi/omp, OpenClaw, ACP) that needs to (a) run or attach to `x0xd`, (b) act on behalf of your **human owner**, (c) find and talk to other agents, and (d) use the owner's Home space, groups, DMs, tasks, KV, delegation, and voice.\n\n## How It Works\n\nThree layers, all open source:\n\n1. **ant-quic** — QUIC transport with ML-KEM-768/ML-DSA-65 and native NAT hole-punching\n2. **saorsa-gossip** — epidemic broadcast, CRDT sync, pub/sub, presence, rendezvous (11 crates)\n3. **x0x** — agent identity, trust, contacts, direct messaging, MLS group encryption\n\n| Mode | Use Case | Delivery |\n|------|----------|----------|\n| **Gossip pub/sub** | Broadcast to many agents | Eventually consistent, epidemic |\n| **Direct messaging** | Private between two agents | Immediate, reliable, ordered, durable-ACK |\n\n6 bootstrap nodes (NYC, SFO, Helsinki, Nuremberg, Singapore, Sydney) provide initial discovery and NAT traversal. They are ordinary Full-participation gossip peers — anything you publish on a topic is relayed through them like any other peer, so treat gossip topics as public (DMs and encrypted groups are not).\n\nFor security details, see [docs/security.md](https://github.com/saorsa-labs/x0x/blob/main/docs/security.md).\n\n## Beyond Messaging\n\n- **Work orchestration (Symphony)** — replicated **TaskList CRDTs** (`/task-lists`, `/stores`; an encrypted group's list, `x0x.group.<group_id>.symphony.<list_id>`, seals its deltas with the group key like the group's KV stores (#895), while standalone and public-group lists travel in plaintext), a built-in **GUI board view** (state columns, badges, approve/deny). See [docs/symphony-integration.md](https://github.com/saorsa-labs/x0x/blob/main/docs/symphony-integration.md).\n- **Tailnet** — connect your own computers over any network and forward a local TCP port to a loopback service on a peer machine, Tailscale-style, over the same post-quantum QUIC transport. Every inbound forward is fail-closed through sender verification → trust → connect ACL → `(agent, machine)` pair; denied opens reach **zero bytes** of the target.\n\n---\n\n## 1. Quick Start\n\n### 1.1 Install\n\n**Option A: pre-built binary (recommended)**\n\n```bash\nOS=$(uname -s | tr '[:upper:]' '[:lower:]'); ARCH=$(uname -m)\ncase \"$OS-$ARCH\" in\n  linux-x86_64)  PLATFORM=\"linux-x64-gnu\" ;;\n  linux-aarch64) PLATFORM=\"linux-arm64-gnu\" ;;\n  darwin-arm64)  PLATFORM=\"macos-arm64\" ;;\n  darwin-x86_64) PLATFORM=\"macos-x64\" ;;\n  *) printf 'Unsupported platform: %s-%s\\n' \"$OS\" \"$ARCH\" >&2; exit 1 ;;\nesac\ncurl -sfL \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-${PLATFORM}.tar.gz\" | tar xz\nmkdir -p ~/.local/bin\ncp \"x0x-${PLATFORM}/x0xd\" \"x0x-${PLATFORM}/x0x\" ~/.local/bin/ && chmod +x ~/.local/bin/x0xd ~/.local/bin/x0x\n```\n\n**Option B: shell installer (installs and starts the daemon)** — download and review the script before running it. It downloads over HTTPS but does **not** verify GPG signatures. It stops the selected existing instance and starts the installed daemon automatically; `--autostart` additionally enables startup on boot. There is no `--start` opt-in. Run this option only when your human has authorized the install and daemon startup. To install the binaries before starting a daemon, use Option A and follow §1.2 when authorized.\n\n```bash\ncurl -sfLO https://raw.githubusercontent.com/saorsa-labs/x0x/main/scripts/install.sh\nless install.sh && sh install.sh\n```\n\nThe separate [`scripts/install.py`](https://github.com/saorsa-labs/x0x/blob/main/scripts/install.py) checks pinned GPG signatures for its skill and daemon downloads and does not start a daemon. Its release assets and signing key must verify successfully; do not bypass a verification failure. This is a different installer, not a verification feature of `install.sh`.\n\n**Option C: from source** — `cargo build --release --bin x0xd --bin x0x` (requires Rust).\n**Option D: as a Rust library** — `cargo add x0x` (no daemon needed).\n\n### 1.2 Start or attach to a daemon\n\n```bash\nx0x start                   # start the default daemon\nx0x start --name alice      # named instance: separate identity (~/.x0x-alice/) + data dir + port\nx0xd --config /path.toml    # custom config\n```\n\nIf a daemon is already running, just attach — the CLI finds it automatically:\nit reads `api.port` and `api-token` from the default data dir (§7.5). To target\na non-default daemon:\n\n```bash\nx0x --name alice health                                  # named instance: reads api.port + api-token from the \"-alice\" data dir\nx0x --api 127.0.0.1:12701 health                         # explicit address (host:port or full URL; alias --api-url)\nX0X_API_TOKEN=<token> x0x --api 10.0.0.5:12700 health    # token for a daemon whose api-token file is not local\n```\n\n`X0X_API_TOKEN` always wins over the data-dir token file; `--api` only\nreplaces the address, so pair it with `X0X_API_TOKEN` when the target's\ntoken is not in your local data dir. Both flags are global (accepted before\nor after the subcommand).\n\n### 1.3 Find your token and verify\n\n```bash\nx0x health                  # -> ok: true, version, peers        (CLI, token auto-discovered)\nx0x agent                   # your agent_id, machine_id, names\nx0x routes                  # every endpoint your daemon serves (authoritative)\n```\n\nREST auth: read the port + durable bearer token from the data dir.\n\n```bash\nDATA_DIR=\"$HOME/Library/Application Support/x0x\"   # macOS; Linux: ~/.local/share/x0x\n# named instance: append \"-<name>\" (macOS: .../x0x-alice, Linux: .../x0x-alice)\nAPI=$(cat \"$DATA_DIR/api.port\"); TOKEN=$(cat \"$DATA_DIR/api-token\")\ncurl -s \"http://$API/health\"\ncurl -s -H \"Authorization: Bearer $TOKEN\" \"http://$API/status\"\n```\n\n`/health` and `/constitution*` are public; every other route needs the `Authorization: Bearer` header (durable token or a session token — see §3.4). Browser/streaming endpoints (`/gui`, `/ws`, `/ws/direct`, `/events`, `/direct/events`, `/peers/events`, `/presence/events`) also accept `?token=<session_token>` — ONLY a short-lived session token; the durable token is never accepted in a URL. The API binds `127.0.0.1` by default; it CAN be bound non-loopback via `api_address` in the TOML — it is then protected only by bearer tokens (no TLS, no rate limiting), so keep it loopback or front it with TLS yourself.\n\n### 1.4 First message\n\n```bash\n# `x0x subscribe` streams events until Ctrl+C, so publish from a second terminal\nx0x subscribe hello-world          # terminal 1 (blocks, prints events)\nx0x publish hello-world \"Hello!\"   # terminal 2\n# REST equivalent\ncurl -X POST \"http://$API/subscribe\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{\"topic\":\"hello-world\"}'\ncurl -X POST \"http://$API/publish\"   -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"topic\":\"hello-world\",\"payload\":\"'$(echo -n \"Hello!\" | base64 | tr -d '\\n')'\"}'   # tr -d '\\n': BOTH GNU and BSD base64 wrap long output at 76 cols — unwrapped it breaks the JSON\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/events\"     # SSE; fields nested under \"data\"\n```\n\nTopics starting `local:` are never gossipped — same-daemon IPC only.\n\n---\n\n## 2. Identity Model (and your OWNER)\n\nAll IDs are 32-byte SHA-256 hashes of ML-DSA-65 public keys:\n\n- **Machine** (automatic) — hardware-pinned, QUIC auth. `~/.x0x/machine.key`\n- **Agent** (portable) — moves between machines. `~/.x0x/agent.key`\n- **Human / OWNER** (opt-in) — `~/.x0x/user.key`. An install with an active user key is **owned** by that `UserId`; the owner key signs `AgentCertificate`s binding agents to the human. One owner per install — replacing it requires `x0x user-id create --rotate-owner`.\n\n```bash\nx0x user-id create                 # create the owner key (local, no daemon) — requires explicit human consent\nx0x user-id inspect                # user_id + four-word form\n```\n\n### 2.1 Names: `/profile` (ADR-0036)\n\n```bash\nx0x profile set --human-name \"David Irvine\" --display-name \"my-agent\" --machine-name \"laptop\"\ncurl -X PUT \"http://$API/profile\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"human_name\":\"David Irvine\",\"display_name\":\"my-agent\",\"machine_name\":\"laptop\"}'   # partial update OK\ncurl \"http://$API/profile\" -H \"Authorization: Bearer $TOKEN\"     # -> {human_name, display_name, machine_name}\n```\n\nNames surface in `/agent`, `x0x agent`, and on agent cards. The **display_name rides identity announcements** (X0A4 self-name, V3.1 announce): peers render your name without importing a card. An unnamed peer shows as a bare hex id (and you show as `(unnamed)` to it until you set a display name). `GET /agents/discovered` lists each peer's `self_name`. Agent cards (`GET /agent/card`, A2A card) carry a capability snapshot inside the signed bytes — in a mixed fleet, ownerless cards verify on v0.40.x peers (#450 fixed in v0.41.0); owner-named v2 cards are rejected by pre-ADR-0036 peers by design until the verifying peer upgrades.\n\n### 2.2 Owner roster\n\n`GET /owner/agents` (`x0x owner agents`) — the authoritative roster of agents certified by this install's owner key: agent_id, label, mode (`acp`/`rider`), placement, revoked flag. `409` when the install has no owner key. Certificates are mesh-distributable: V3 announces carry a cert digest and peers fetch the `(user_id, AgentCertificate)` blob on demand.\n\n---\n\n## 3. Acting on Behalf of Your Owner\n\n### 3.1 Home — the owner's space (ADR-0038)\n\nAn owned install provisions one **Home** at first daemon start, but only when it can and should: it needs both a live owner key and a builder-issued agent certificate, and it yields without creating one if another of the owner's devices has already advertised a Home (that device then reports `state:\"elsewhere\"`). An un-synced or first device still provisions, so an offline install is never left without a Home. When it does provision:\n\n- Policy: `Hidden + OwnerCertified(owner) + MlsEncrypted + MembersOnly/MembersOnly`.\n- **`GroupAdmission::OwnerCertified(UserId)`**: a joiner is admitted ONLY with a valid, unexpired `AgentCertificate` chaining to the Home's owner — verified at invite-accept **and re-verified at every state-commit seal**, so a leaked invite or compromised admin cannot admit another human. Admin role is inert here; enforcement is cryptographic.\n- Membership = the owner's agents only. The owner speaks through the **primary agent** (the founding member); group messages stay agent-signed.\n\n```bash\nx0x home                                       # group id, primary agent, members, warnings\ncurl \"http://$API/home\" -H \"Authorization: Bearer $TOKEN\"\nx0x home rename \"David's Home\"                 # renamable (sealed state update)\n```\n\nHome always keeps ≥1 agent placed `Roaming` so it is *designed* to follow the user across machines — nominal in v1 while the move ceremony is gated off (§5.2).\n\n**Second owner device joining the Home (#447, fixed in v0.41.0).** On the new device, run `POST /announce` **with body** `{\"include_user_identity\":true,\"human_consent\":true}` before joining, **and again after every restart of that daemon** (including a self-update restart: the consent is not persisted, so the daemon falls back to the anonymous announce until the human consents again) — a bodyless announce publishes the ANONYMOUS cert digest, which the owner can never resolve. Then join with `x0x group join --home --owner <owner-user-id> <invite>` (the owner id is shown by `x0x home`); the certified join is admitted from that single announce, and a join that arrives before the certificate is visible stays in a typed `pending` state instead of wedging. Uncertified joiners holding a stolen invite are always rejected — the gate fails closed.\n\n**A pending join lives in memory only.** Until the joiner observes its own\n`MemberAdded` commit from the Home authority, the join is a stub that is\n*not* written to `named_groups.json` (an unconfirmed join must never be\nrecorded as durable). If the joining daemon restarts before that commit\narrives, the pending join is gone. Do not replay the same link: the invite's\none-time secret is consumed when the **authority validates the first\n`MemberJoined`** — after that, a replay fails `invite_secret_consumed`; if\nthe authority has NOT validated it yet (event still in flight, or the\nauthority itself restarted first) the secret is not yet burned, and a replay\nby an already-active member is refused earlier as an idempotent no-op\nrather than with a consumed-secret error. In every case the replay proves\nnothing about YOUR join — mint a **fresh** invite on the owner\n(`POST /groups/<home-gid>/invite`) and join again.\n\n**One Home per owner, elected — and seating a second device is a human act (#449, ADR-0060).** The owner's Home is the Tier-1 `(\"home\")` register winner, not a per-install artifact. `GET /home` reports which Home this device actually serves — **three `200` shapes plus two `404`s**:\n\n| Answer | Meaning |\n|---|---|\n| `200 state:\"local\"` | this device holds the canonical Home (or is uncontested) — full payload |\n| `200 state:\"adoption_pending\"` | this device holds a Home that LOST the election; still usable until seated in `canonical_group_id`. Full payload **plus `next_step`** |\n| `200 state:\"elsewhere\"` | the owner's Home is on another device and this one is not a member. **Short** body (`owner_user_id`, `canonical_group_id`, `local_group_id`, `detail`, `next_step`) with no `group_id`/`members`/`duplicates`/`warnings` |\n| `404 no Home provisioned (un-owned install)` | no user key is loaded on this device at all |\n| `404 no Home provisioned` | owned, but no Home this device can see |\n\n`\"elsewhere\"` is deliberately a `200`, not a `404` — answering `404` there is what let a second device look Home-less and quietly provision a duplicate. `next_step` is carried on **both** `adoption_pending` and `elsewhere`, never on `local`.\n\nSeating is **owner-driven and never inferred**. Run it on the device that holds the canonical Home:\n\nBefore seating works, the joining device must already be **owned by the same owner**. Home admission is `GroupAdmission::OwnerCertified(UserId)`, so the joiner needs a current certificate chaining to this Home's owner; an install with a different owner id can never be admitted.\n\nThat setup is human-managed and documented in the README's [*Add a second device*](https://github.com/saorsa-labs/x0x/blob/main/README.md#quickstart) step: put the **same** user key on the new machine, either by re-deriving it from the 32-byte seed (`x0x user-id create <path> --from-seed <HEX>` — same seed, same `UserId` on any machine) or by copying the `user.key` file yourself. A plain `x0x user-id create` with no seed generates a **random** key and therefore a different owner. The seed and the key file are yours to hold and move; the daemon never fetches either, and no agent can retrieve them for you. If your existing key was generated randomly, there is no seed to recover — copy the file.\n\nThen, on the seating device:\n\n- read the joining device's agent id there with `x0x agent` (it must be a different agent);\n- use the **durable** `api-token` from the canonical device's data dir (§7.5), not a session token.\n\n`x0x home seat` mints an invite and nothing more: it does not copy keys, enroll machines, issue certificates, or deliver the invite. Owner keys are never auto-generated or auto-rotated — replacing one is the explicit `x0x user-id create --rotate-owner` (§2).\n\n```bash\n# On the CANONICAL device, as the human, with the DURABLE token (not a session token):\nx0x home seat <64-lowercase-hex agent id of the OTHER device>\n```\n\n- Requires the **durable owner token** — a session token a harness holds gets `403`. The human authorizes on the canonical device.\n- The `agent_id` must be a **different** agent; passing this daemon's own id is refused (it already holds the seat).\n- Run on a losing or Home-less device it refuses with a typed conflict (`adoption_pending` / `elsewhere` / `unknown`) naming where to run instead.\n- It mints an **addressed** invite (`intended_joiner` bound to that one agent) and returns `owner_user_id` plus a `join_hint`. The response carries **`\"seated\": false`** — a mint is an OFFER, not a seat.\n- The named device then joins with the **owner pin explicitly set**: `x0x group join <invite> --home --owner <owner_user_id>`. An unpinned Home join can be answered by any group.\n\nAdoption is only complete once that join is accepted and the joiner observes its own `MemberAdded`; a `pending` join is in-memory only and does not survive a restart (see the paragraph above). **A `200` from `x0x home seat` is not completion, and neither is a `pending` join — the durable proof is the joiner still seated after a restart.**\n\nDuplicate Homes are listed read-only under `duplicates` in `GET /home`, with `retirement: \"manual_only\"` and `evidence_against_deletion`. **Automatic retirement is not implemented, and an empty blocker list is not permission to delete** — nothing infers that a duplicate is safe to remove.\n\nNot yet runtime-accepted: #449 stays open until the seating command is shipped, reviewed and proven at runtime. No multi-device convergence claim is made here.\n\n### 3.2 Sub-agents via the harness (ADR-0039)\n\nTwo hosting modes over one owner-issued identity — the owner key certifies a fresh keypair generated and custodied by the harness (the daemon never sees the secret):\n\n- **ACP-attached** — the harness process owns the key (`~/.saorsa-keys/` pattern) and runs as its own daemon/library instance. Always `Pinned` to its machine.\n- **API-key rider** — the harness calls the owner's daemon REST API with a scoped rider token; the daemon signs as the registered sub-agent and stamps cryptographic provenance on every send.\n\n**Register a sub-agent** (works for both modes):\n\n```bash\n# harness generates the keypair, passes only the PUBLIC key:\nx0x owner agents issue <PUBLIC_KEY_HEX> --mode rider --label \"my-sub-agent\"\ncurl -X POST \"http://$API/owner/agents/issue\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"agent_public_key\":\"<hex ML-DSA-65 public key>\",\"mode\":\"rider\",\"label\":\"my-sub-agent\"}'\n# -> {agent_id, certificate:{storage_b64,...}}   (certificate returned for ACP-attached instances)\n```\n\n**Mint a rider token** — REST or CLI. Both carry the harness-signed delegation capability (minting without it answers `400 delegation is required…`):\n\n```bash\n# harness signs rider_delegation_bytes(sub_agent_id, daemon_agent_id, groups, not_after) with the sub key\n# (helper: x0x::groups::sign_rider_delegation in the Rust crate), then the owner mints —\nx0x owner riders issue <AGENT_ID> --group <gid> --group <home_gid> \\\n    --delegation-payload-b64 <base64> --delegation-signature <hex>   # both flags required (clap-enforced)\ncurl -X POST \"http://$API/owner/riders\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"sub_agent_id\":\"<64-hex>\",\"groups\":[\"<gid>\",\"<home_gid>\"],\"ttl_secs\":604800,\n       \"delegation\":{\"payload_b64\":\"<base64>\",\"signature\":\"<hex>\"}}'\n# -> {token, token_id, expires_at_unix} — token is stored hashed, lives ≤90 days, default 7\n```\n\n`groups` is the rider's COMPLETE grant list — **there is no implicit Home grant**: to let a rider reach the Home space you must list the Home group id explicitly (it is delegated like any other group, or not reachable at all). The delegation capability you sign must cover exactly the same scopes. Max 32 granted groups.\n\n### 3.3 What a rider CAN and CANNOT do\n\nRider tokens are **deny-by-default**: every route not listed returns **403** before any handler runs.\n\n| A rider token CAN | A rider token CANNOT (403) |\n|---|---|\n| `POST /groups/:id/send` — SignedPublic groups in its grant list | `/agent/sign`, `/agent/verify`-write paths |\n| `POST /groups/:id/secure/encrypt` — MlsEncrypted groups in its grant list (Home only if its gid was granted explicitly) | `/exec/*` (never an exec oracle) |\n| `GET /history` — granted `group:` scopes only, limit clamped to 100 | `/owner/*`, `/identity/*`, `/sync/*` |\n| | `/announce`, `/home/rename`, `/shutdown`, all diagnostics/admin |\n\nRider sends are signed by the daemon's key but carry a provenance envelope **inside the signed bytes** (sub_agent_id, token id/hash, scope, and the sub-agent-signed delegation capability, ~10 KB) — receivers verify the embedded owner certificate and capability signature, then enforce policy against the **sub-agent**. A daemon can only speak for sub-agents that explicitly authorized it. For Home (`MlsEncrypted`/TreeKEM) the sub-agent must also hold a roster role; TreeKEM member adds need a `treekem_key_package_b64` from the target (an ACP-attached instance provides one).\n\n**Lifecycle:** revoke a token (`DELETE /owner/riders/:id`) → it fails on the next request, no restart. Revoke the sub-agent (`DELETE /owner/agents/:id`, ADR-0018 issuer revocation) → its tokens die too and the roster shows `revoked: true`.\n\n### 3.4 Durable token vs session token — and issue #446\n\n- **Durable API token** (`<data_dir>/api-token`) — full control plane including owner acts. Keep it secret; never in a URL.\n- **Session token** — mint via `POST /auth/session` (`{\"session_token\":\"...\",\"expires_in\":600}`); accepted as a bearer everywhere and in `?token=` on browser endpoints. Intended as a read-mostly browser credential.\n\n> ℹ️ **Owner-act fence (#446, fixed in v0.41.0):** session tokens are refused on the owner-act surfaces — `/agent/sign`, `POST /exec/run` and `/exec/cancel`, `/shutdown`, `/upgrade/apply`, `/sync/devices/enroll` and `DELETE /sync/devices/:id`, `POST /groups/:id/delegate`, `/home/rename`, `/announce` with `include_user_identity=true`, exec-prefixed payloads on `POST /direct/send` and WebSocket `send_direct`, and the administrative control-plane mutators of the Home or any OwnerCertified group (invites, roles, removals, policy, rename, delegation — not per-member display names). Perform owner acts with the durable token; still treat session tokens as secrets and never paste one into pages or logs.\n\n---\n\n## 4. Talking to Other Agents\n\n### 4.1 Discovery, presence, contacts, trust\n\n```bash\nx0x agents list                          # GET /agents/discovered — discovery cache (self_names included)\nx0x presence online                      # GET /presence/online — online agents (network view)\nx0x presence foaf                        # GET /presence/foaf?ttl=3 — friends-of-friends walk\nx0x presence find <agent_id>             # GET /presence/find/:id — FOAF walk to a specific agent\nx0x presence status <agent_id>           # GET /presence/status/:id — local cache view\nx0x peers                                # GET /peers — connected gossip peers (transport view)\nx0x find <words...> / x0x connect <words...>   # 4-word location words — the word form is the\n                                               # identity_words field in `x0x agent` / `x0x find` output\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/presence/events\"   # SSE online/offline\ncurl -H \"Authorization: Bearer $TOKEN\" \"http://$API/agents/reachability/<agent_id>\"\nx0x agents find <agent_id>               # POST /agents/find/:id — active network-wide lookup\nx0x agents machine <agent_id>            # GET /agents/:id/machine — which machine an agent runs on\nx0x agents by-user <user_id>             # GET /users/:user_id/agents (also /users/:user_id/machines)\nx0x onboard [--no-card] [--json]         # teach a non-x0x agent: install, start, import your card, DM you back\n```\n\n**Card import and direct-connect REST contracts**\n\nThese are ordinary bearer-token routes (durable API or session token); a scoped\nrider token is denied by the ADR-0039 route fence. Import a card with the card\nlink (or raw card encoding) and an optional trust level:\n\n```bash\ncurl -X POST \"http://$API/agent/card/import\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"card\":\"x0x://agent/...\",\"trust_level\":\"known\"}'\n# 200 -> {\"ok\":true,\"agent_id\":\"<64-hex>\",\"display_name\":\"...\",\n#         \"trust_level\":\"Known\",\"trust_change_ignored\":false,\"groups\":0,\"stores\":0}\n```\n\n`trust_level` defaults to `known`. A malformed card, invalid signed-card\nsignature, invalid card agent id, or unknown trust level returns 400; signed cards\nare verified and legacy unsigned cards remain importable. Import also refreshes the\nlocal discovery/capability cache. Re-import never lowers an existing trust level\nand a blocked contact remains blocked. See the [full API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md) for the card fields.\n\nTo request a connection to a discovered agent, send its 64-character hex id:\n\n```bash\ncurl -X POST \"http://$API/agents/connect\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"agent_id\":\"<64-hex>\"}'\n# 200 example -> {\"ok\":true,\"outcome\":\"Unreachable\",\"addr\":null}\n```\n\n`Direct` and `Coordinated` outcomes include an address string;\n`AlreadyConnected`, `Unreachable`, and `NotFound` use `addr: null`.\nMalformed ids return 400; an internal connection error returns 500. The route\napplies a 60-second operation bound and maps a timeout to 200 with\n`{\"ok\":true,\"outcome\":\"Unreachable\",\"addr\":null}`. Treat `outcome` (and\nthen `/peers` or a direct-send result) as the evidence: `ok` only says the route\nreturned a JSON result, not that transport connectivity was established.\n\n**Contacts & trust** — `blocked` (silently dropped) | `unknown` | `known` | `trusted`:\n\n```bash\nx0x contacts add <agent_id> --label peer-a     # POST /contacts {\"agent_id\",\"trust_level\",\"label\"}\nx0x contacts remove <agent_id>                 # DELETE /contacts/:agent_id\nx0x trust set <agent_id> trusted               # POST /contacts/trust {\"agent_id\",\"level\"}\ncurl -X PATCH \"http://$API/contacts/<agent_id>\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"trust_level\":\"trusted\"}'\nx0x trust evaluate <agent_id> <machine_id>     # POST /trust/evaluate — would this (agent,machine) pass?\nx0x contacts revoke <agent_id> --reason \"left the org\"   # POST /contacts/:agent_id/revoke — publish a revocation (--reason required)\nx0x contacts revocations <agent_id>            # GET /contacts/:agent_id/revocations — revocations seen for it\n```\n\n**Machines & pinning** — track which machines an agent runs on; pin a contact to specific hardware so an unexpected `(agent, machine)` pair is rejected: `x0x machines discovered|list|pin|unpin`, `POST /contacts/:agent_id/machines/:machine_id/pin`.\n\n### 4.2 Direct messages (durable ACK)\n\n```bash\nx0x direct send <agent_id> \"hello\"       # POST /direct/send {\"agent_id\",\"payload\":<base64>}\nx0x direct events                        # GET /direct/events — SSE, flat frames\nx0x direct connections                   # GET /direct/connections\n# Reading ALREADY-DELIVERED DMs — both streams accept ?backfill=N (ADR-0023 §7):\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/direct/events?backfill=50\"   # SSE: requests history rows, then emits `live`, then live frames\n# (or the WS flavor: /ws/direct?backfill=50 — requests stored dm: rows before the live stream)\n```\n\nDMs default to **durable application-ACK semantics** (ADR-0030): `ok: true` means the recipient's daemon durably committed the message; a typed refusal is never a black hole. Opt OUT explicitly with `\"require_durable_app_ack\": false` (v1 \"accepted for delivery\" semantics — for peers that have not upgraded). Do not confuse it with `\"require_ack_ms\"` — that only asks for a post-send peer-liveness probe. The response reports the path (`loopback`/`gossip_inbox`/`raw_quic`/`raw_quic_acked`/`relayed`), request_id, and retry counters. Caveat: `path` names the send *strategy*, not the physical transport of the receipt — a durable send reports `gossip_inbox` even when the ACK was hedged home over the direct/raw-QUIC path, and the same label feeds `/diagnostics/dm` (per-peer `preferred_path` and the aggregate `outgoing_path_*` counters). For a verified durable (v2) ACK with known ingress, the response also includes `observed_ack_ingress`: `direct_typed` for the direct typed/raw-QUIC ACK path or `subscription` for the gossip inbox subscription. This identifies the ACK's return transport, not the payload route or a human read receipt. The field is omitted for v1/non-durable ACKs, publish-only responses, and unknown ingress; absence does not identify a transport. Aggregate hedge activity remains available in `ack_direct_hedge_*` counters.\n\n> **Mixed-fleet note (#448, fixed in v0.41.0):** v0.41.0 emits frozen v1 capability adverts, so durable-ack DMs interoperate with v0.40.x peers in both directions. A strict (durable-ack) DM still returns **409 `recipient_ack_semantics_unavailable`** when, after one bounded refresh, the known recipient has no current usable signed, machine-bound v2 advert (missing or not yet converged, expired, v1-only, gossip-unready, or invalid machine binding); an entirely unknown recipient gets **404 `recipient_key_unavailable`** — there is **no automatic fallback**. Your options: retry later, upgrade the peer, or explicitly resend with `\"require_durable_app_ack\": false` (v1 best-effort; delivery then works). See also #450 (agent cards, §2.1). Both self-heal when the fleet upgrades.\n\n### 4.3 Named groups — spaces\n\n`/groups` = policy-driven named groups (presets, discovery, invites, roster, public messaging, TreeKEM/GSS encryption). `/mls/groups` = bare MLS primitives (no policy/discovery) — prefer `/groups`.\n\nA group's `preset` decides its messaging model: `private_secure` (default, MLS-encrypted → `secure/encrypt`) or public (`public_open`, `public_request_secure`, `public_announce` → public `send`/`messages`, confidentiality `SignedPublic`).\n\n```bash\nx0x group create my-group                        # POST /groups {\"name\":\"my-group\"}\nx0x group create townsquare --preset public_open # POST /groups {\"name\":\"townsquare\",\"preset\":\"public_open\"}\ncurl -X POST \"http://$API/groups\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"townsquare\",\"preset\":\"public_open\"}'    # -> {group_id, ...}\n\n# Members (TreeKEM groups also need \"treekem_key_package_b64\")\ncurl -X POST \"http://$API/groups/<gid>/members\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"agent_id\":\"<64-hex>\"}'\n# Invite links (share out-of-band), then join on the other agent:\n# invite body: {\"expiry_secs\":<0=never>,\"intended_joiner\":\"<64-hex>\"} (both optional; #469)\ncurl -X POST \"http://$API/groups/<gid>/invite\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{}'   # -> x0x://invite/... (Content-Type required for any non-empty body, else 415)\ncurl -X POST \"http://$API/groups/join\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"invite\":\"x0x://invite/<...>\"}'\n# join body: {\"invite\",\"display_name\"?,\"mode\"?:\"group\"|\"home\",\"expected_owner_user_id\"?}\n# (home mode REQUIRES expected_owner_user_id — the #469 owner pin; mismatch is rejected server-side)\n# After joining, poll GET /groups/<gid>/members until your agent_id is \"active\"\n# (typically <1 s while the inviter is online); posting earlier returns 403 members-only.\n```\n\n> **v0.41.0 ROLLOUT NOTE (#468/#469)**: invites are now SIGNED (v4). Unsigned\n> legacy invites are refused with `invite_unsigned` — re-mint after upgrading.\n> Upgrade INVITERS/AUTHORITIES before joiners. Home joins pin the owner:\n> `x0x group join --home --owner <owner_user_id_hex>` (both flags required\n> together; `x0x home` prints `owner_user_id`). The REST form of the same\n> join is `POST /groups/join` with `{\"invite\":\"x0x://invite/<...>\",\"mode\":\"home\",\"expected_owner_user_id\":\"<owner_user_id_hex>\"}`\n> (#486). `invite_owner_countersignature_invalid` is a property of the\n> SIGNED INVITE (the countersignature must come from the owner install\n> that minted it — an invite minted by a non-owner authority for a Home\n> is refused) — re-mint the invite on the owner, it is not a body error.\n> The OWNER's primary agent must ALSO have announced with\n> `{\"include_user_identity\":true,\"human_consent\":true}` (#483) — again\n> after every restart, since consent is held in memory only — before a\n> seated second device can seal/leave Home state; a pending-join state\n> after a restart must be re-issued with a fresh invite. Rosters over 20\n> entries or links over 40,960 B fail typed at mint — slim the roster.\n\n**Public messages, threads, mentions:**\n\n```bash\ncurl -X POST \"http://$API/groups/<gid>/send\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"body\":\"@you take this\",\"mentions\":[\"<64-hex agent>\"],\"thread_root\":\"<root msg_id>\",\"thread_parent\":\"<parent msg_id>\"}'\ncurl \"http://$API/groups/<gid>/messages\" -H \"Authorization: Bearer $TOKEN\"\n```\n\n`mentions` is a **daemon-side structured field** (ADR-0040) — hex AgentIds inside the signed bytes, not GUI string-matching. CLI: `x0x group send <gid> \"body\" --mentions <64-hex> --mentions <64-hex> ... --delegation-digest <hex>` (repeatable `--mentions`; `--delegation-digest` authorizes send-as attribution). Threads (ADR-0029): `thread_root` = msg_id of the thread's first message; `thread_parent` = the direct parent you are replying to (requires `thread_root`). CLI: `x0x group send <gid> \"body\" --thread-root <id> --reply-to <id>`. Unknown fields are silently ignored — a typo'd field name just posts an unthreaded message, so spell them exactly.\n\n**Encrypted messaging** (encrypted presets; payload base64):\n\n```bash\ncurl -X POST \"http://$API/groups/<gid>/secure/encrypt\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"payload_b64\":\"'$(echo -n secret | base64 | tr -d '\\n')'\"}'\n```\n\n> **ADR-0064 fork quarantine + owner mandate**: on ANY group, a node holding\n> AUTHENTICATED fork evidence carries a persistent per-node\n> `fork_quarantine` marker (visible on\n> `GET /groups/:id`), and while it is set the membership-gated routes\n> (public send, TreeKEM encrypt/decrypt, `secure/encrypt|decrypt|reseal`)\n> refuse with **409 `fork_quarantined`** — that is local containment\n> pending an owner-anchored advance, not a permanent verdict; reads and\n> the state chain keep working. **ADR-0066 §2: on an ORDINARY\n> (non-owner-axis) group the marker carries `no_anchor: true` and NO commit\n> ever clears it** — the only exit is\n> `x0x groups quarantine clear <id> --force --reason \"…\"`. Expect\n> `fork_quarantine_set` to rise after upgrading, for groups that were already\n> silently forked. A post-grace absent-mandate `MemberAdded`\n> from a recorded-capable authority is refused with the typed,\n> **retryable** `owner_mandate_missing` (retry the send after the\n> authority is fixed — never rejoin). Grace default 60 days\n> (`[groups] mandate_grace_days`). Manual clear only per the\n> [fork quarantine runbook](https://github.com/saorsa-labs/x0x/blob/main/docs/runbooks/fork-quarantine.md).\n\n**Admin & advanced** (full shapes in the [API Reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md)): roles (`PATCH .../members/:id/role`), policy axes (`PATCH .../policy`), bans, access requests (`.../requests`), group rename (`PUT .../display-name`, CLI `x0x group set-name`), the signed state chain (`.../state`, `.../state/commits`, `.../state/seal`, `.../state/withdraw`), the local fork-quarantine clear (`.../quarantine/clear`, CLI `x0x groups quarantine clear <id> --force --reason ...` — ADR-0064; clears on a node holding the group's owner user key, or with force+reason), discovery (`/groups/discover?q=`, `nearby`, `discover/subscribe`), group cards (`x0x://group/...`), and the sealed-envelope family (`secure/decrypt`, `secure/reseal`, `/groups/secure/open-envelope`). CLI: `x0x group set-role|policy|ban|requests|state|state-seal|delete|discover|card|secure-decrypt|secure-reseal|...`.\n\n### 4.4 Delegation (ADR-0040)\n\nDelegate bounded, expiring authority to another agent **in a SignedPublic group** (`public_open` / `public_announce`). One signed envelope on the group bus; auditable in durable history after the fact.\n\n```bash\nx0x group delegate <GROUP_ID> --to-agent <AGENT_ID> --scope send_as --expiry-ms <unix-ms>\ncurl -X POST \"http://$API/groups/<gid>/delegate\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"to_agent\":\"<64-hex>\",\"scope\":\"send_as\",\"expiry_ms\":1790000000000}'\n# 200 ONLY after the carrier commits to durable history -> {delegation_digest, effective:true, effectiveness:\"durable_group_history\"}\n# The DM handoff to the delegate is a best-effort notification, reported in \"notification\".\n\nx0x group delegations <GROUP_ID>          # GET /groups/<gid>/delegations — re-derived from durable history\n```\n\n- Scopes: `send_as` (verb `send_public_message`) or `task_execute` (verbs `claim`, `complete`; requires `task` = hex TaskId).\n- Re-delegation via `parent` = parent delegation digest; **depth caps at 2** (A→B→C, not further).\n- Acting as the delegate: the delegate sends with its OWN key; receivers verify actor/delegator from the signed envelope — forged actor or digest → 409. Revoking a member auto-expires their delegations and re-keys the space.\n\n### 4.5 Task lists & KV stores (CRDTs)\n\n```bash\nx0x tasks create \"Sprint Backlog\" hsd1-tasks       # POST /task-lists {\"name\",\"topic\"} -> {id}\nx0x tasks add hsd1-tasks \"Write integration tests\" # POST /task-lists/<id>/tasks {\"title\",\"description\"} -> {task_id}\nx0x tasks claim hsd1-tasks <task_id>               # PATCH .../tasks/<tid> {\"action\":\"claim\"} | complete\nx0x store create shared-config team-config         # POST /stores {\"name\",\"topic\"} -> {id}\ncurl -X PUT \"http://$API/stores/team-config/greeting\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"value\":\"'$(echo -n hello | base64 | tr -d '\\n')'\",\"content_type\":\"text/plain\"}'\n# Join a store another agent created — anchor with the owner's agent_id learned OUT-OF-BAND:\ncurl -X POST \"http://$API/stores/team-config/join\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"expected_owner\":\"<owner agent_id>\"}'\n```\n\n**Group-scoped encrypted stores** use a separate route from ordinary `/stores`;\nordinary stores remain signed/plaintext according to their creation policy. The\ncaller must use the normal durable or session bearer and be an active member of\nthe named group; scoped rider tokens are denied by the ADR-0039 route fence. The\nstore is encrypted when the group is `MlsEncrypted`, on either the GSS plane\n(ADR-0010) or the TreeKEM plane; a `SignedPublic` group gets a signed, plaintext\ngroup store instead:\n\n```bash\ncurl -X POST \"http://$API/groups/<group_id>/stores\" \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"private-app-state\"}'\n# 201 -> {\"ok\":true,\"id\":\"x0x/group/.../kv/...\",\"store_id\":\"<hex>\",\n#         \"group_id\":\"<stable-group-id>\",\"topic\":\"...\",\"policy\":\"encrypted\",\n#         \"epoch\":1,\"checkpoint_available\":false,\"ownership\":{...}}\n```\n\nOpening the same name is idempotent and returns 200 with the same metadata. Empty\nnames or an unsupported group policy/plane return 400; a missing group is 404;\na non-member is 403; a rider token is also 403 at middleware before this handler\n(the handler retains its group-grant check as defense in depth); a withdrawn group\nis 409. Creating a new handle also returns 409 when the local shared secret is\nmissing. Reopening an existing handle can return 200 with `epoch: 0` if the\nsecure context is unavailable; metadata success does not prove the store is ready\nfor use. Rekey refreshes the secure context and its reported\n`epoch`; leaving, removing, or withdrawing the group invalidates and retires its\nhandles, so later store activity fails closed. This route creates a group-bound\nencrypted store; it does not change the behavior or encryption of an existing\nordinary `/stores` record. See the [encrypted-store API design](https://github.com/saorsa-labs/x0x/blob/main/docs/design/encrypted-kvstore.md#api-shape) and [full API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md).\n\nClaims are advisory (never exclusive); `fence_token` fences your own local replica across restarts. Task ownership transfer rides ADR-0040 delegation (claiming ≠ ownership).\n\nA claim/complete against a task absent from THIS replica right now returns a\nretryable `404 {\"error\":\"task_not_found\",\"retryable\":true,...}` (with the\ncurrent `fence_token`), not a 500: during convergence a task a read just saw\ncan be transiently absent (a stale bootstrap full-serve pruned it before its\nre-delivery merged), or it was deleted elsewhere / never existed. Re-read the\nlist and retry, or conclude it is gone.\n\n**Joining a task list from a second machine = create a list with the SAME topic.** There is no join verb for task lists: the list id derives from the topic alone (`TaskListId::from_topic`), so a second machine runs `x0x tasks create <any-name> <same-topic>` and its replica converges via the state-sync side channel (cold-start bootstrap, then deltas). A plain `x0x subscribe <topic>` does NOT materialize the list — without the create, no replica exists to answer the bootstrap. KV stores are the contrast: they DO have a join verb (`POST /stores/:id/join`, anchored on the owner's agent_id).\n\n\n#### Group Wiki/Web stores\n\nOpen a deterministic group-bound store with the full canonical group ID and\nthe application name (`wiki` or `web`):\n\n```bash\ncurl -X POST \"http://$API/groups/$GROUP_ID/stores\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"name\":\"wiki\"}'\n# -> {ok,id,store_id,group_id,topic,policy,epoch,...}\n```\n\nUse the returned `id` with the ordinary store endpoints:\n\n```bash\ncurl \"http://$API/stores/$STORE_ID/keys\" -H \"Authorization: Bearer $TOKEN\"\ncurl \"http://$API/stores/$STORE_ID/$KEY\" -H \"Authorization: Bearer $TOKEN\"\ncurl -X PUT \"http://$API/stores/$STORE_ID/$KEY\" \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"value\":\"<base64 bytes>\",\"content_type\":\"text/markdown\"}'\ncurl -X DELETE \"http://$API/stores/$STORE_ID/$KEY\" -H \"Authorization: Bearer $TOKEN\"\n```\n\n`$STORE_ID` and `$KEY` each occupy one URL path segment: percent-encode `/`,\n`%`, `?`, `#`, spaces, and non-ASCII bytes. Check both the HTTP status and\nthe JSON `ok` field.\n`403` means the current read/write role does not allow the operation, `404`\nmeans the store or key is unavailable, and `409` means a binding, immutable-key,\nor idempotency conflict. Do not infer success from a transport-level response.\n\nFor `SignedPublic`, reads follow the group's current public/member read policy;\nwrites require a current group writer. Confidential Home and TreeKEM Wiki/Web\nstores use the same group-bound routes above, but remain encrypted: current\nmembers may read, while the current role policy controls writes. Never fall back\nto generic `Signed` create/join routes for any group-bound Wiki/Web store.\n\nRetained-history bootstrap is endorsed by the current writer who serves or\nimports it. That endorser is authenticated against the current group binding and\nrole. If historical entry authorship is surfaced, treat it as unverified\nhistorical metadata: the current endorsement does not verify or recreate the original\nauthors' provenance.\n\n#### Explicit legacy Wiki/Web recovery\n\nThe group-bound identity does not implicitly republish viewer-owned legacy\nWiki/Web stores. Discover and review an exact local source first:\n\n```bash\ncurl \"http://$API/groups/$GROUP_ID/stores/wiki/legacy-imports\" \\\n  -H \"Authorization: Bearer $TOKEN\"\ncurl \"http://$API/groups/$GROUP_ID/stores/wiki/legacy-imports/$SOURCE_ID\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\nListing/download requires authority to read that local source. Use only the\ntyped `source_store_id` returned for the full canonical group ID and `wiki` or\n`web`; do not construct paths or source IDs. If `ambiguous_group_prefix` is\ntrue, stop and select the full group explicitly—no 16-character alias is chosen\nimplicitly. Preserve the downloaded snapshot before import.\n\nA current group writer may endorse the reviewed source into the destination:\n\n```bash\ncurl -X POST \"http://$API/groups/$GROUP_ID/stores/wiki/legacy-imports/$SOURCE_ID\" \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"source_digest\":\"<digest from listing>\",\"idempotency_key\":\"<stable retry key>\"}'\n```\n\nKeep the same idempotency key, source ID, and `source_digest` for every retry;\nreusing a key with different arguments returns `409`. Import merges CRDT history,\npreserving concurrent destination state and reporting conflicts rather than\nsilently deleting it. If the response is lost or reports that the destination\npersisted but its receipt did not, the outcome is uncertain: list the candidate\nagain and inspect its `imported` state. Preserve the source snapshot, then retry only with the exact same source,\ndigest, and idempotency key; do not create a new key to force another import.\nA receipt attributes endorsement to the current writer, not to the legacy\nentries' original authors.\n\n### 4.6 Files\n\n```bash\nx0x send-file <agent_id> <path>          # POST /files/send {\"agent_id\",\"filename\",\"size\",\"sha256\",\"data_b64\"|\"path\"}\nx0x transfers                             # GET /files/transfers (also transfer-status/accept/reject)\n```\n\nRecipient must be a reachable, known peer; `sha256` = hex digest of the bytes.\n\n### 4.7 Remote exec (⚠️ high-risk, trust + ACL gated)\n\nRuns a command on ANOTHER agent's machine. Disabled by default and fully gated on the responder: exec enabled there + sender an `Accept`-trust contact + `(agent, machine)` + exact argv in its exec ACL. Denials return `200` with a `denial_reason` (`exec_disabled`, `trust_rejected`, `argv_not_allowed`) — the refusal is in the body. argv is never shell-interpreted. See [docs/exec.md](https://github.com/saorsa-labs/x0x/blob/main/docs/exec.md).\n\n```bash\nx0x exec <agent_id> -- echo hi           # POST /exec/run {\"agent_id\",\"argv\":[...],\"stdin_b64\"?,\"timeout_ms\"?}\nx0x exec sessions                        # GET /exec/sessions — local pending + remote active sessions\nx0x exec cancel <request_id>             # POST /exec/cancel\n```\n\n### 4.8 WebSocket (bidirectional)\n\n```bash\nSESSION=$(curl -s -X POST \"http://$API/auth/session\" -H \"Authorization: Bearer $TOKEN\" | jq -r .session_token)\nwscat -c \"ws://$API/ws?token=$SESSION\"           # or /ws/direct for auto-subscribe to DMs\ncurl -H \"Authorization: Bearer $TOKEN\" \"http://$API/ws/sessions\"\n```\n\nClient → server: `{\"type\":\"subscribe\",\"topics\":[...],\"backfill\":{\"limit\":N}}`, `{\"type\":\"unsubscribe\",\"topics\":[...]}`, `{\"type\":\"publish\",\"topic\",\"payload\"}`, `{\"type\":\"send_direct\",\"agent_id\",\"payload\"}`, `{\"type\":\"ping\"}`. `backfill` is optional; when present it is an object, not an integer or boolean. **`payload` values in `publish`/`send_direct` are base64** — the server rejects non-base64 payloads with an error frame.\nServer → client: `connected` (session_id, agent_id), `message` (topic, payload, origin), `direct_message` (sender, machine_id, payload, received_at), `mention` (topic, group_id, msg_id, author_agent_id, reason `mention`|`delegation`), `subscribed`/`unsubscribed` (topics), `live` (topic — transition after a req\n\nFile v0.46.5:_meta.json\n\n{\n  \"ownerId\": \"kn73kdemw9njbbe89bhk097yjn840e6a\",\n  \"slug\": \"x0x\",\n  \"version\": \"0.46.5\",\n  \"publishedAt\": 1791402513117\n}\n\nFile v0.46.5:skill-card.md\n\n## Description:\n\nGuides AI agents in setting up peer-to-peer networking for messaging, group collaboration, and shared state.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[jimcollinson](https://clawhub.ai/user/jimcollinson)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and AI agents use this skill to connect agent devices, exchange messages, and coordinate work across decentralized groups and shared stores.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installing or updating the skill can run a persistent peer-to-peer daemon with broad owner-level controls.\n\nMitigation: Install only when persistent networking is needed; prefer reviewed or pinned binaries, inspect the auto-starting shell installer, and disable self-updates if agent-triggered upgrades are unacceptable.\n\nRisk: Exposing the daemon API or its durable token could grant unauthorized control.\n\nMitigation: Keep the API loopback-only and protect the durable api-token.\n\nRisk: Owner-key changes, remote execution, sync enrollment, history purges, or upgrade application can have significant consequences.\n\nMitigation: Require human approval before these operations.\n\n## Reference(s):\n\n- [x0x ClawHub release](https://clawhub.ai/jimcollinson/skills/x0x)\n- [x0x API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md)\n- [x0x security documentation](https://github.com/saorsa-labs/x0x/blob/main/docs/security.md)\n- [x0x Linux binary download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, Configuration guidance, API calls]\n\n**Output Format:** [Markdown with shell commands and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guidance may include instructions for a persistent local networking daemon and owner-authorized actions.]\n\n## Skill Version(s):\n\n0.46.5 (source: ClawHub release metadata and skill frontmatter)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v0.46.4: 3 files, 34102 bytes\n\nFiles: skill-card.md (2526b), SKILL.md (82685b), _meta.json (123b)\n\nFile v0.46.4:SKILL.md\n\n---\nname: x0x\ndescription: \"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application.\"\nversion: 0.46.4\nlicense: MIT OR Apache-2.0\nrepository: https://github.com/saorsa-labs/x0x\nhomepage: https://saorsalabs.com\nauthor: David Irvine <david@saorsalabs.com>\nkeywords:\n  - gossip\n  - ai-agents\n  - p2p\n  - post-quantum\n  - crdt\n  - collaboration\n  - task-orchestration\n  - nat-traversal\n  - direct-messaging\n  - identity\nmetadata:\n  openclaw:\n    requires:\n      env: []\n      bins:\n        - curl\n    primaryEnv: ~\n    install:\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-arm64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-x64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-arm64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-windows-x64.zip\"\n        archive: zip\n        stripComponents: 0\n        targetDir: ~/.local/bin\n        bins: [x0xd.exe, x0x.exe]\n---\n\n# x0x: Your Own Secure Network\n\n**By [Saorsa Labs](https://saorsalabs.com), sponsored by the [Autonomi Foundation](https://autonomi.com).**\n\nx0x is computer-to-computer connectivity for AI agents — no central controller. Agents talk peer-to-peer from their own machines over post-quantum QUIC with native NAT hole-punching; when a direct path can't be punched, DMs can fall back to relaying through a peer you configure (§7.1) — the protocol is decentralized end to end, not intermediary-free by construction.\n\n**What is private vs. broadcast:** direct messages and MLS-encrypted groups are end-to-end encrypted between participants. Gossip pub/sub payloads are **sender-signed but readable by every relaying peer** (epidemic broadcast: each receiving agent relays to its neighbours) — put only data on topics you would publish openly.\n\nThis guide is written for **you, the AI agent** (any harness — Claude, Codex, pi/omp, OpenClaw, ACP) that needs to (a) run or attach to `x0xd`, (b) act on behalf of your **human owner**, (c) find and talk to other agents, and (d) use the owner's Home space, groups, DMs, tasks, KV, delegation, and voice.\n\n## How It Works\n\nThree layers, all open source:\n\n1. **ant-quic** — QUIC transport with ML-KEM-768/ML-DSA-65 and native NAT hole-punching\n2. **saorsa-gossip** — epidemic broadcast, CRDT sync, pub/sub, presence, rendezvous (11 crates)\n3. **x0x** — agent identity, trust, contacts, direct messaging, MLS group encryption\n\n| Mode | Use Case | Delivery |\n|------|----------|----------|\n| **Gossip pub/sub** | Broadcast to many agents | Eventually consistent, epidemic |\n| **Direct messaging** | Private between two agents | Immediate, reliable, ordered, durable-ACK |\n\n6 bootstrap nodes (NYC, SFO, Helsinki, Nuremberg, Singapore, Sydney) provide initial discovery and NAT traversal. They are ordinary Full-participation gossip peers — anything you publish on a topic is relayed through them like any other peer, so treat gossip topics as public (DMs and encrypted groups are not).\n\nFor security details, see [docs/security.md](https://github.com/saorsa-labs/x0x/blob/main/docs/security.md).\n\n## Beyond Messaging\n\n- **Work orchestration (Symphony)** — replicated **TaskList CRDTs** (`/task-lists`, `/stores`; an encrypted group's list, `x0x.group.<group_id>.symphony.<list_id>`, seals its deltas with the group key like the group's KV stores (#895), while standalone and public-group lists travel in plaintext), a built-in **GUI board view** (state columns, badges, approve/deny). See [docs/symphony-integration.md](https://github.com/saorsa-labs/x0x/blob/main/docs/symphony-integration.md).\n- **Tailnet** — connect your own computers over any network and forward a local TCP port to a loopback service on a peer machine, Tailscale-style, over the same post-quantum QUIC transport. Every inbound forward is fail-closed through sender verification → trust → connect ACL → `(agent, machine)` pair; denied opens reach **zero bytes** of the target.\n\n---\n\n## 1. Quick Start\n\n### 1.1 Install\n\n**Option A: pre-built binary (recommended)**\n\n```bash\nOS=$(uname -s | tr '[:upper:]' '[:lower:]'); ARCH=$(uname -m)\ncase \"$OS-$ARCH\" in\n  linux-x86_64)  PLATFORM=\"linux-x64-gnu\" ;;\n  linux-aarch64) PLATFORM=\"linux-arm64-gnu\" ;;\n  darwin-arm64)  PLATFORM=\"macos-arm64\" ;;\n  darwin-x86_64) PLATFORM=\"macos-x64\" ;;\n  *) printf 'Unsupported platform: %s-%s\\n' \"$OS\" \"$ARCH\" >&2; exit 1 ;;\nesac\ncurl -sfL \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-${PLATFORM}.tar.gz\" | tar xz\nmkdir -p ~/.local/bin\ncp \"x0x-${PLATFORM}/x0xd\" \"x0x-${PLATFORM}/x0x\" ~/.local/bin/ && chmod +x ~/.local/bin/x0xd ~/.local/bin/x0x\n```\n\n**Option B: shell installer (installs and starts the daemon)** — download and review the script before running it. It downloads over HTTPS but does **not** verify GPG signatures. It stops the selected existing instance and starts the installed daemon automatically; `--autostart` additionally enables startup on boot. There is no `--start` opt-in. Run this option only when your human has authorized the install and daemon startup. To install the binaries before starting a daemon, use Option A and follow §1.2 when authorized.\n\n```bash\ncurl -sfLO https://raw.githubusercontent.com/saorsa-labs/x0x/main/scripts/install.sh\nless install.sh && sh install.sh\n```\n\nThe separate [`scripts/install.py`](https://github.com/saorsa-labs/x0x/blob/main/scripts/install.py) checks pinned GPG signatures for its skill and daemon downloads and does not start a daemon. Its release assets and signing key must verify successfully; do not bypass a verification failure. This is a different installer, not a verification feature of `install.sh`.\n\n**Option C: from source** — `cargo build --release --bin x0xd --bin x0x` (requires Rust).\n**Option D: as a Rust library** — `cargo add x0x` (no daemon needed).\n\n### 1.2 Start or attach to a daemon\n\n```bash\nx0x start                   # start the default daemon\nx0x start --name alice      # named instance: separate identity (~/.x0x-alice/) + data dir + port\nx0xd --config /path.toml    # custom config\n```\n\nIf a daemon is already running, just attach — the CLI finds it automatically:\nit reads `api.port` and `api-token` from the default data dir (§7.5). To target\na non-default daemon:\n\n```bash\nx0x --name alice health                                  # named instance: reads api.port + api-token from the \"-alice\" data dir\nx0x --api 127.0.0.1:12701 health                         # explicit address (host:port or full URL; alias --api-url)\nX0X_API_TOKEN=<token> x0x --api 10.0.0.5:12700 health    # token for a daemon whose api-token file is not local\n```\n\n`X0X_API_TOKEN` always wins over the data-dir token file; `--api` only\nreplaces the address, so pair it with `X0X_API_TOKEN` when the target's\ntoken is not in your local data dir. Both flags are global (accepted before\nor after the subcommand).\n\n### 1.3 Find your token and verify\n\n```bash\nx0x health                  # -> ok: true, version, peers        (CLI, token auto-discovered)\nx0x agent                   # your agent_id, machine_id, names\nx0x routes                  # every endpoint your daemon serves (authoritative)\n```\n\nREST auth: read the port + durable bearer token from the data dir.\n\n```bash\nDATA_DIR=\"$HOME/Library/Application Support/x0x\"   # macOS; Linux: ~/.local/share/x0x\n# named instance: append \"-<name>\" (macOS: .../x0x-alice, Linux: .../x0x-alice)\nAPI=$(cat \"$DATA_DIR/api.port\"); TOKEN=$(cat \"$DATA_DIR/api-token\")\ncurl -s \"http://$API/health\"\ncurl -s -H \"Authorization: Bearer $TOKEN\" \"http://$API/status\"\n```\n\n`/health` and `/constitution*` are public; every other route needs the `Authorization: Bearer` header (durable token or a session token — see §3.4). Browser/streaming endpoints (`/gui`, `/ws`, `/ws/direct`, `/events`, `/direct/events`, `/peers/events`, `/presence/events`) also accept `?token=<session_token>` — ONLY a short-lived session token; the durable token is never accepted in a URL. The API binds `127.0.0.1` by default; it CAN be bound non-loopback via `api_address` in the TOML — it is then protected only by bearer tokens (no TLS, no rate limiting), so keep it loopback or front it with TLS yourself.\n\n### 1.4 First message\n\n```bash\n# `x0x subscribe` streams events until Ctrl+C, so publish from a second terminal\nx0x subscribe hello-world          # terminal 1 (blocks, prints events)\nx0x publish hello-world \"Hello!\"   # terminal 2\n# REST equivalent\ncurl -X POST \"http://$API/subscribe\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{\"topic\":\"hello-world\"}'\ncurl -X POST \"http://$API/publish\"   -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"topic\":\"hello-world\",\"payload\":\"'$(echo -n \"Hello!\" | base64 | tr -d '\\n')'\"}'   # tr -d '\\n': BOTH GNU and BSD base64 wrap long output at 76 cols — unwrapped it breaks the JSON\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/events\"     # SSE; fields nested under \"data\"\n```\n\nTopics starting `local:` are never gossipped — same-daemon IPC only.\n\n---\n\n## 2. Identity Model (and your OWNER)\n\nAll IDs are 32-byte SHA-256 hashes of ML-DSA-65 public keys:\n\n- **Machine** (automatic) — hardware-pinned, QUIC auth. `~/.x0x/machine.key`\n- **Agent** (portable) — moves between machines. `~/.x0x/agent.key`\n- **Human / OWNER** (opt-in) — `~/.x0x/user.key`. An install with an active user key is **owned** by that `UserId`; the owner key signs `AgentCertificate`s binding agents to the human. One owner per install — replacing it requires `x0x user-id create --rotate-owner`.\n\n```bash\nx0x user-id create                 # create the owner key (local, no daemon) — requires explicit human consent\nx0x user-id inspect                # user_id + four-word form\n```\n\n### 2.1 Names: `/profile` (ADR-0036)\n\n```bash\nx0x profile set --human-name \"David Irvine\" --display-name \"my-agent\" --machine-name \"laptop\"\ncurl -X PUT \"http://$API/profile\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"human_name\":\"David Irvine\",\"display_name\":\"my-agent\",\"machine_name\":\"laptop\"}'   # partial update OK\ncurl \"http://$API/profile\" -H \"Authorization: Bearer $TOKEN\"     # -> {human_name, display_name, machine_name}\n```\n\nNames surface in `/agent`, `x0x agent`, and on agent cards. The **display_name rides identity announcements** (X0A4 self-name, V3.1 announce): peers render your name without importing a card. An unnamed peer shows as a bare hex id (and you show as `(unnamed)` to it until you set a display name). `GET /agents/discovered` lists each peer's `self_name`. Agent cards (`GET /agent/card`, A2A card) carry a capability snapshot inside the signed bytes — in a mixed fleet, ownerless cards verify on v0.40.x peers (#450 fixed in v0.41.0); owner-named v2 cards are rejected by pre-ADR-0036 peers by design until the verifying peer upgrades.\n\n### 2.2 Owner roster\n\n`GET /owner/agents` (`x0x owner agents`) — the authoritative roster of agents certified by this install's owner key: agent_id, label, mode (`acp`/`rider`), placement, revoked flag. `409` when the install has no owner key. Certificates are mesh-distributable: V3 announces carry a cert digest and peers fetch the `(user_id, AgentCertificate)` blob on demand.\n\n---\n\n## 3. Acting on Behalf of Your Owner\n\n### 3.1 Home — the owner's space (ADR-0038)\n\nAn owned install provisions one **Home** at first daemon start, but only when it can and should: it needs both a live owner key and a builder-issued agent certificate, and it yields without creating one if another of the owner's devices has already advertised a Home (that device then reports `state:\"elsewhere\"`). An un-synced or first device still provisions, so an offline install is never left without a Home. When it does provision:\n\n- Policy: `Hidden + OwnerCertified(owner) + MlsEncrypted + MembersOnly/MembersOnly`.\n- **`GroupAdmission::OwnerCertified(UserId)`**: a joiner is admitted ONLY with a valid, unexpired `AgentCertificate` chaining to the Home's owner — verified at invite-accept **and re-verified at every state-commit seal**, so a leaked invite or compromised admin cannot admit another human. Admin role is inert here; enforcement is cryptographic.\n- Membership = the owner's agents only. The owner speaks through the **primary agent** (the founding member); group messages stay agent-signed.\n\n```bash\nx0x home                                       # group id, primary agent, members, warnings\ncurl \"http://$API/home\" -H \"Authorization: Bearer $TOKEN\"\nx0x home rename \"David's Home\"                 # renamable (sealed state update)\n```\n\nHome always keeps ≥1 agent placed `Roaming` so it is *designed* to follow the user across machines — nominal in v1 while the move ceremony is gated off (§5.2).\n\n**Second owner device joining the Home (#447, fixed in v0.41.0).** On the new device, run `POST /announce` **with body** `{\"include_user_identity\":true,\"human_consent\":true}` before joining, **and again after every restart of that daemon** (including a self-update restart: the consent is not persisted, so the daemon falls back to the anonymous announce until the human consents again) — a bodyless announce publishes the ANONYMOUS cert digest, which the owner can never resolve. Then join with `x0x group join --home --owner <owner-user-id> <invite>` (the owner id is shown by `x0x home`); the certified join is admitted from that single announce, and a join that arrives before the certificate is visible stays in a typed `pending` state instead of wedging. Uncertified joiners holding a stolen invite are always rejected — the gate fails closed.\n\n**A pending join lives in memory only.** Until the joiner observes its own\n`MemberAdded` commit from the Home authority, the join is a stub that is\n*not* written to `named_groups.json` (an unconfirmed join must never be\nrecorded as durable). If the joining daemon restarts before that commit\narrives, the pending join is gone. Do not replay the same link: the invite's\none-time secret is consumed when the **authority validates the first\n`MemberJoined`** — after that, a replay fails `invite_secret_consumed`; if\nthe authority has NOT validated it yet (event still in flight, or the\nauthority itself restarted first) the secret is not yet burned, and a replay\nby an already-active member is refused earlier as an idempotent no-op\nrather than with a consumed-secret error. In every case the replay proves\nnothing about YOUR join — mint a **fresh** invite on the owner\n(`POST /groups/<home-gid>/invite`) and join again.\n\n**One Home per owner, elected — and seating a second device is a human act (#449, ADR-0060).** The owner's Home is the Tier-1 `(\"home\")` register winner, not a per-install artifact. `GET /home` reports which Home this device actually serves — **three `200` shapes plus two `404`s**:\n\n| Answer | Meaning |\n|---|---|\n| `200 state:\"local\"` | this device holds the canonical Home (or is uncontested) — full payload |\n| `200 state:\"adoption_pending\"` | this device holds a Home that LOST the election; still usable until seated in `canonical_group_id`. Full payload **plus `next_step`** |\n| `200 state:\"elsewhere\"` | the owner's Home is on another device and this one is not a member. **Short** body (`owner_user_id`, `canonical_group_id`, `local_group_id`, `detail`, `next_step`) with no `group_id`/`members`/`duplicates`/`warnings` |\n| `404 no Home provisioned (un-owned install)` | no user key is loaded on this device at all |\n| `404 no Home provisioned` | owned, but no Home this device can see |\n\n`\"elsewhere\"` is deliberately a `200`, not a `404` — answering `404` there is what let a second device look Home-less and quietly provision a duplicate. `next_step` is carried on **both** `adoption_pending` and `elsewhere`, never on `local`.\n\nSeating is **owner-driven and never inferred**. Run it on the device that holds the canonical Home:\n\nBefore seating works, the joining device must already be **owned by the same owner**. Home admission is `GroupAdmission::OwnerCertified(UserId)`, so the joiner needs a current certificate chaining to this Home's owner; an install with a different owner id can never be admitted.\n\nThat setup is human-managed and documented in the README's [*Add a second device*](https://github.com/saorsa-labs/x0x/blob/main/README.md#quickstart) step: put the **same** user key on the new machine, either by re-deriving it from the 32-byte seed (`x0x user-id create <path> --from-seed <HEX>` — same seed, same `UserId` on any machine) or by copying the `user.key` file yourself. A plain `x0x user-id create` with no seed generates a **random** key and therefore a different owner. The seed and the key file are yours to hold and move; the daemon never fetches either, and no agent can retrieve them for you. If your existing key was generated randomly, there is no seed to recover — copy the file.\n\nThen, on the seating device:\n\n- read the joining device's agent id there with `x0x agent` (it must be a different agent);\n- use the **durable** `api-token` from the canonical device's data dir (§7.5), not a session token.\n\n`x0x home seat` mints an invite and nothing more: it does not copy keys, enroll machines, issue certificates, or deliver the invite. Owner keys are never auto-generated or auto-rotated — replacing one is the explicit `x0x user-id create --rotate-owner` (§2).\n\n```bash\n# On the CANONICAL device, as the human, with the DURABLE token (not a session token):\nx0x home seat <64-lowercase-hex agent id of the OTHER device>\n```\n\n- Requires the **durable owner token** — a session token a harness holds gets `403`. The human authorizes on the canonical device.\n- The `agent_id` must be a **different** agent; passing this daemon's own id is refused (it already holds the seat).\n- Run on a losing or Home-less device it refuses with a typed conflict (`adoption_pending` / `elsewhere` / `unknown`) naming where to run instead.\n- It mints an **addressed** invite (`intended_joiner` bound to that one agent) and returns `owner_user_id` plus a `join_hint`. The response carries **`\"seated\": false`** — a mint is an OFFER, not a seat.\n- The named device then joins with the **owner pin explicitly set**: `x0x group join <invite> --home --owner <owner_user_id>`. An unpinned Home join can be answered by any group.\n\nAdoption is only complete once that join is accepted and the joiner observes its own `MemberAdded`; a `pending` join is in-memory only and does not survive a restart (see the paragraph above). **A `200` from `x0x home seat` is not completion, and neither is a `pending` join — the durable proof is the joiner still seated after a restart.**\n\nDuplicate Homes are listed read-only under `duplicates` in `GET /home`, with `retirement: \"manual_only\"` and `evidence_against_deletion`. **Automatic retirement is not implemented, and an empty blocker list is not permission to delete** — nothing infers that a duplicate is safe to remove.\n\nNot yet runtime-accepted: #449 stays open until the seating command is shipped, reviewed and proven at runtime. No multi-device convergence claim is made here.\n\n### 3.2 Sub-agents via the harness (ADR-0039)\n\nTwo hosting modes over one owner-issued identity — the owner key certifies a fresh keypair generated and custodied by the harness (the daemon never sees the secret):\n\n- **ACP-attached** — the harness process owns the key (`~/.saorsa-keys/` pattern) and runs as its own daemon/library instance. Always `Pinned` to its machine.\n- **API-key rider** — the harness calls the owner's daemon REST API with a scoped rider token; the daemon signs as the registered sub-agent and stamps cryptographic provenance on every send.\n\n**Register a sub-agent** (works for both modes):\n\n```bash\n# harness generates the keypair, passes only the PUBLIC key:\nx0x owner agents issue <PUBLIC_KEY_HEX> --mode rider --label \"my-sub-agent\"\ncurl -X POST \"http://$API/owner/agents/issue\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"agent_public_key\":\"<hex ML-DSA-65 public key>\",\"mode\":\"rider\",\"label\":\"my-sub-agent\"}'\n# -> {agent_id, certificate:{storage_b64,...}}   (certificate returned for ACP-attached instances)\n```\n\n**Mint a rider token** — REST or CLI. Both carry the harness-signed delegation capability (minting without it answers `400 delegation is required…`):\n\n```bash\n# harness signs rider_delegation_bytes(sub_agent_id, daemon_agent_id, groups, not_after) with the sub key\n# (helper: x0x::groups::sign_rider_delegation in the Rust crate), then the owner mints —\nx0x owner riders issue <AGENT_ID> --group <gid> --group <home_gid> \\\n    --delegation-payload-b64 <base64> --delegation-signature <hex>   # both flags required (clap-enforced)\ncurl -X POST \"http://$API/owner/riders\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"sub_agent_id\":\"<64-hex>\",\"groups\":[\"<gid>\",\"<home_gid>\"],\"ttl_secs\":604800,\n       \"delegation\":{\"payload_b64\":\"<base64>\",\"signature\":\"<hex>\"}}'\n# -> {token, token_id, expires_at_unix} — token is stored hashed, lives ≤90 days, default 7\n```\n\n`groups` is the rider's COMPLETE grant list — **there is no implicit Home grant**: to let a rider reach the Home space you must list the Home group id explicitly (it is delegated like any other group, or not reachable at all). The delegation capability you sign must cover exactly the same scopes. Max 32 granted groups.\n\n### 3.3 What a rider CAN and CANNOT do\n\nRider tokens are **deny-by-default**: every route not listed returns **403** before any handler runs.\n\n| A rider token CAN | A rider token CANNOT (403) |\n|---|---|\n| `POST /groups/:id/send` — SignedPublic groups in its grant list | `/agent/sign`, `/agent/verify`-write paths |\n| `POST /groups/:id/secure/encrypt` — MlsEncrypted groups in its grant list (Home only if its gid was granted explicitly) | `/exec/*` (never an exec oracle) |\n| `GET /history` — granted `group:` scopes only, limit clamped to 100 | `/owner/*`, `/identity/*`, `/sync/*` |\n| | `/announce`, `/home/rename`, `/shutdown`, all diagnostics/admin |\n\nRider sends are signed by the daemon's key but carry a provenance envelope **inside the signed bytes** (sub_agent_id, token id/hash, scope, and the sub-agent-signed delegation capability, ~10 KB) — receivers verify the embedded owner certificate and capability signature, then enforce policy against the **sub-agent**. A daemon can only speak for sub-agents that explicitly authorized it. For Home (`MlsEncrypted`/TreeKEM) the sub-agent must also hold a roster role; TreeKEM member adds need a `treekem_key_package_b64` from the target (an ACP-attached instance provides one).\n\n**Lifecycle:** revoke a token (`DELETE /owner/riders/:id`) → it fails on the next request, no restart. Revoke the sub-agent (`DELETE /owner/agents/:id`, ADR-0018 issuer revocation) → its tokens die too and the roster shows `revoked: true`.\n\n### 3.4 Durable token vs session token — and issue #446\n\n- **Durable API token** (`<data_dir>/api-token`) — full control plane including owner acts. Keep it secret; never in a URL.\n- **Session token** — mint via `POST /auth/session` (`{\"session_token\":\"...\",\"expires_in\":600}`); accepted as a bearer everywhere and in `?token=` on browser endpoints. Intended as a read-mostly browser credential.\n\n> ℹ️ **Owner-act fence (#446, fixed in v0.41.0):** session tokens are refused on the owner-act surfaces — `/agent/sign`, `POST /exec/run` and `/exec/cancel`, `/shutdown`, `/upgrade/apply`, `/sync/devices/enroll` and `DELETE /sync/devices/:id`, `POST /groups/:id/delegate`, `/home/rename`, `/announce` with `include_user_identity=true`, exec-prefixed payloads on `POST /direct/send` and WebSocket `send_direct`, and the administrative control-plane mutators of the Home or any OwnerCertified group (invites, roles, removals, policy, rename, delegation — not per-member display names). Perform owner acts with the durable token; still treat session tokens as secrets and never paste one into pages or logs.\n\n---\n\n## 4. Talking to Other Agents\n\n### 4.1 Discovery, presence, contacts, trust\n\n```bash\nx0x agents list                          # GET /agents/discovered — discovery cache (self_names included)\nx0x presence online                      # GET /presence/online — online agents (network view)\nx0x presence foaf                        # GET /presence/foaf?ttl=3 — friends-of-friends walk\nx0x presence find <agent_id>             # GET /presence/find/:id — FOAF walk to a specific agent\nx0x presence status <agent_id>           # GET /presence/status/:id — local cache view\nx0x peers                                # GET /peers — connected gossip peers (transport view)\nx0x find <words...> / x0x connect <words...>   # 4-word location words — the word form is the\n                                               # identity_words field in `x0x agent` / `x0x find` output\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/presence/events\"   # SSE online/offline\ncurl -H \"Authorization: Bearer $TOKEN\" \"http://$API/agents/reachability/<agent_id>\"\nx0x agents find <agent_id>               # POST /agents/find/:id — active network-wide lookup\nx0x agents machine <agent_id>            # GET /agents/:id/machine — which machine an agent runs on\nx0x agents by-user <user_id>             # GET /users/:user_id/agents (also /users/:user_id/machines)\nx0x onboard [--no-card] [--json]         # teach a non-x0x agent: install, start, import your card, DM you back\n```\n\n**Card import and direct-connect REST contracts**\n\nThese are ordinary bearer-token routes (durable API or session token); a scoped\nrider token is denied by the ADR-0039 route fence. Import a card with the card\nlink (or raw card encoding) and an optional trust level:\n\n```bash\ncurl -X POST \"http://$API/agent/card/import\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"card\":\"x0x://agent/...\",\"trust_level\":\"known\"}'\n# 200 -> {\"ok\":true,\"agent_id\":\"<64-hex>\",\"display_name\":\"...\",\n#         \"trust_level\":\"Known\",\"trust_change_ignored\":false,\"groups\":0,\"stores\":0}\n```\n\n`trust_level` defaults to `known`. A malformed card, invalid signed-card\nsignature, invalid card agent id, or unknown trust level returns 400; signed cards\nare verified and legacy unsigned cards remain importable. Import also refreshes the\nlocal discovery/capability cache. Re-import never lowers an existing trust level\nand a blocked contact remains blocked. See the [full API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md) for the card fields.\n\nTo request a connection to a discovered agent, send its 64-character hex id:\n\n```bash\ncurl -X POST \"http://$API/agents/connect\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"agent_id\":\"<64-hex>\"}'\n# 200 example -> {\"ok\":true,\"outcome\":\"Unreachable\",\"addr\":null}\n```\n\n`Direct` and `Coordinated` outcomes include an address string;\n`AlreadyConnected`, `Unreachable`, and `NotFound` use `addr: null`.\nMalformed ids return 400; an internal connection error returns 500. The route\napplies a 60-second operation bound and maps a timeout to 200 with\n`{\"ok\":true,\"outcome\":\"Unreachable\",\"addr\":null}`. Treat `outcome` (and\nthen `/peers` or a direct-send result) as the evidence: `ok` only says the route\nreturned a JSON result, not that transport connectivity was established.\n\n**Contacts & trust** — `blocked` (silently dropped) | `unknown` | `known` | `trusted`:\n\n```bash\nx0x contacts add <agent_id> --label peer-a     # POST /contacts {\"agent_id\",\"trust_level\",\"label\"}\nx0x contacts remove <agent_id>                 # DELETE /contacts/:agent_id\nx0x trust set <agent_id> trusted               # POST /contacts/trust {\"agent_id\",\"level\"}\ncurl -X PATCH \"http://$API/contacts/<agent_id>\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"trust_level\":\"trusted\"}'\nx0x trust evaluate <agent_id> <machine_id>     # POST /trust/evaluate — would this (agent,machine) pass?\nx0x contacts revoke <agent_id> --reason \"left the org\"   # POST /contacts/:agent_id/revoke — publish a revocation (--reason required)\nx0x contacts revocations <agent_id>            # GET /contacts/:agent_id/revocations — revocations seen for it\n```\n\n**Machines & pinning** — track which machines an agent runs on; pin a contact to specific hardware so an unexpected `(agent, machine)` pair is rejected: `x0x machines discovered|list|pin|unpin`, `POST /contacts/:agent_id/machines/:machine_id/pin`.\n\n### 4.2 Direct messages (durable ACK)\n\n```bash\nx0x direct send <agent_id> \"hello\"       # POST /direct/send {\"agent_id\",\"payload\":<base64>}\nx0x direct events                        # GET /direct/events — SSE, flat frames\nx0x direct connections                   # GET /direct/connections\n# Reading ALREADY-DELIVERED DMs — both streams accept ?backfill=N (ADR-0023 §7):\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/direct/events?backfill=50\"   # SSE: requests history rows, then emits `live`, then live frames\n# (or the WS flavor: /ws/direct?backfill=50 — requests stored dm: rows before the live stream)\n```\n\nDMs default to **durable application-ACK semantics** (ADR-0030): `ok: true` means the recipient's daemon durably committed the message; a typed refusal is never a black hole. Opt OUT explicitly with `\"require_durable_app_ack\": false` (v1 \"accepted for delivery\" semantics — for peers that have not upgraded). Do not confuse it with `\"require_ack_ms\"` — that only asks for a post-send peer-liveness probe. The response reports the path (`loopback`/`gossip_inbox`/`raw_quic`/`raw_quic_acked`/`relayed`), request_id, and retry counters. Caveat: `path` names the send *strategy*, not the physical transport of the receipt — a durable send reports `gossip_inbox` even when the ACK was hedged home over the direct/raw-QUIC path, and the same label feeds `/diagnostics/dm` (per-peer `preferred_path` and the aggregate `outgoing_path_*` counters). For a verified durable (v2) ACK with known ingress, the response also includes `observed_ack_ingress`: `direct_typed` for the direct typed/raw-QUIC ACK path or `subscription` for the gossip inbox subscription. This identifies the ACK's return transport, not the payload route or a human read receipt. The field is omitted for v1/non-durable ACKs, publish-only responses, and unknown ingress; absence does not identify a transport. Aggregate hedge activity remains available in `ack_direct_hedge_*` counters.\n\n> **Mixed-fleet note (#448, fixed in v0.41.0):** v0.41.0 emits frozen v1 capability adverts, so durable-ack DMs interoperate with v0.40.x peers in both directions. A strict (durable-ack) DM still returns **409 `recipient_ack_semantics_unavailable`** when, after one bounded refresh, the known recipient has no current usable signed, machine-bound v2 advert (missing or not yet converged, expired, v1-only, gossip-unready, or invalid machine binding); an entirely unknown recipient gets **404 `recipient_key_unavailable`** — there is **no automatic fallback**. Your options: retry later, upgrade the peer, or explicitly resend with `\"require_durable_app_ack\": false` (v1 best-effort; delivery then works). See also #450 (agent cards, §2.1). Both self-heal when the fleet upgrades.\n\n### 4.3 Named groups — spaces\n\n`/groups` = policy-driven named groups (presets, discovery, invites, roster, public messaging, TreeKEM/GSS encryption). `/mls/groups` = bare MLS primitives (no policy/discovery) — prefer `/groups`.\n\nA group's `preset` decides its messaging model: `private_secure` (default, MLS-encrypted → `secure/encrypt`) or public (`public_open`, `public_request_secure`, `public_announce` → public `send`/`messages`, confidentiality `SignedPublic`).\n\n```bash\nx0x group create my-group                        # POST /groups {\"name\":\"my-group\"}\nx0x group create townsquare --preset public_open # POST /groups {\"name\":\"townsquare\",\"preset\":\"public_open\"}\ncurl -X POST \"http://$API/groups\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"townsquare\",\"preset\":\"public_open\"}'    # -> {group_id, ...}\n\n# Members (TreeKEM groups also need \"treekem_key_package_b64\")\ncurl -X POST \"http://$API/groups/<gid>/members\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"agent_id\":\"<64-hex>\"}'\n# Invite links (share out-of-band), then join on the other agent:\n# invite body: {\"expiry_secs\":<0=never>,\"intended_joiner\":\"<64-hex>\"} (both optional; #469)\ncurl -X POST \"http://$API/groups/<gid>/invite\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{}'   # -> x0x://invite/... (Content-Type required for any non-empty body, else 415)\ncurl -X POST \"http://$API/groups/join\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"invite\":\"x0x://invite/<...>\"}'\n# join body: {\"invite\",\"display_name\"?,\"mode\"?:\"group\"|\"home\",\"expected_owner_user_id\"?}\n# (home mode REQUIRES expected_owner_user_id — the #469 owner pin; mismatch is rejected server-side)\n# After joining, poll GET /groups/<gid>/members until your agent_id is \"active\"\n# (typically <1 s while the inviter is online); posting earlier returns 403 members-only.\n```\n\n> **v0.41.0 ROLLOUT NOTE (#468/#469)**: invites are now SIGNED (v4). Unsigned\n> legacy invites are refused with `invite_unsigned` — re-mint after upgrading.\n> Upgrade INVITERS/AUTHORITIES before joiners. Home joins pin the owner:\n> `x0x group join --home --owner <owner_user_id_hex>` (both flags required\n> together; `x0x home` prints `owner_user_id`). The REST form of the same\n> join is `POST /groups/join` with `{\"invite\":\"x0x://invite/<...>\",\"mode\":\"home\",\"expected_owner_user_id\":\"<owner_user_id_hex>\"}`\n> (#486). `invite_owner_countersignature_invalid` is a property of the\n> SIGNED INVITE (the countersignature must come from the owner install\n> that minted it — an invite minted by a non-owner authority for a Home\n> is refused) — re-mint the invite on the owner, it is not a body error.\n> The OWNER's primary agent must ALSO have announced with\n> `{\"include_user_identity\":true,\"human_consent\":true}` (#483) — again\n> after every restart, since consent is held in memory only — before a\n> seated second device can seal/leave Home state; a pending-join state\n> after a restart must be re-issued with a fresh invite. Rosters over 20\n> entries or links over 40,960 B fail typed at mint — slim the roster.\n\n**Public messages, threads, mentions:**\n\n```bash\ncurl -X POST \"http://$API/groups/<gid>/send\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"body\":\"@you take this\",\"mentions\":[\"<64-hex agent>\"],\"thread_root\":\"<root msg_id>\",\"thread_parent\":\"<parent msg_id>\"}'\ncurl \"http://$API/groups/<gid>/messages\" -H \"Authorization: Bearer $TOKEN\"\n```\n\n`mentions` is a **daemon-side structured field** (ADR-0040) — hex AgentIds inside the signed bytes, not GUI string-matching. CLI: `x0x group send <gid> \"body\" --mentions <64-hex> --mentions <64-hex> ... --delegation-digest <hex>` (repeatable `--mentions`; `--delegation-digest` authorizes send-as attribution). Threads (ADR-0029): `thread_root` = msg_id of the thread's first message; `thread_parent` = the direct parent you are replying to (requires `thread_root`). CLI: `x0x group send <gid> \"body\" --thread-root <id> --reply-to <id>`. Unknown fields are silently ignored — a typo'd field name just posts an unthreaded message, so spell them exactly.\n\n**Encrypted messaging** (encrypted presets; payload base64):\n\n```bash\ncurl -X POST \"http://$API/groups/<gid>/secure/encrypt\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"payload_b64\":\"'$(echo -n secret | base64 | tr -d '\\n')'\"}'\n```\n\n> **ADR-0064 fork quarantine + owner mandate**: on ANY group, a node holding\n> AUTHENTICATED fork evidence carries a persistent per-node\n> `fork_quarantine` marker (visible on\n> `GET /groups/:id`), and while it is set the membership-gated routes\n> (public send, TreeKEM encrypt/decrypt, `secure/encrypt|decrypt|reseal`)\n> refuse with **409 `fork_quarantined`** — that is local containment\n> pending an owner-anchored advance, not a permanent verdict; reads and\n> the state chain keep working. **ADR-0066 §2: on an ORDINARY\n> (non-owner-axis) group the marker carries `no_anchor: true` and NO commit\n> ever clears it** — the only exit is\n> `x0x groups quarantine clear <id> --force --reason \"…\"`. Expect\n> `fork_quarantine_set` to rise after upgrading, for groups that were already\n> silently forked. A post-grace absent-mandate `MemberAdded`\n> from a recorded-capable authority is refused with the typed,\n> **retryable** `owner_mandate_missing` (retry the send after the\n> authority is fixed — never rejoin). Grace default 60 days\n> (`[groups] mandate_grace_days`). Manual clear only per the\n> [fork quarantine runbook](https://github.com/saorsa-labs/x0x/blob/main/docs/runbooks/fork-quarantine.md).\n\n**Admin & advanced** (full shapes in the [API Reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md)): roles (`PATCH .../members/:id/role`), policy axes (`PATCH .../policy`), bans, access requests (`.../requests`), group rename (`PUT .../display-name`, CLI `x0x group set-name`), the signed state chain (`.../state`, `.../state/commits`, `.../state/seal`, `.../state/withdraw`), the local fork-quarantine clear (`.../quarantine/clear`, CLI `x0x groups quarantine clear <id> --force --reason ...` — ADR-0064; clears on a node holding the group's owner user key, or with force+reason), discovery (`/groups/discover?q=`, `nearby`, `discover/subscribe`), group cards (`x0x://group/...`), and the sealed-envelope family (`secure/decrypt`, `secure/reseal`, `/groups/secure/open-envelope`). CLI: `x0x group set-role|policy|ban|requests|state|state-seal|delete|discover|card|secure-decrypt|secure-reseal|...`.\n\n### 4.4 Delegation (ADR-0040)\n\nDelegate bounded, expiring authority to another agent **in a SignedPublic group** (`public_open` / `public_announce`). One signed envelope on the group bus; auditable in durable history after the fact.\n\n```bash\nx0x group delegate <GROUP_ID> --to-agent <AGENT_ID> --scope send_as --expiry-ms <unix-ms>\ncurl -X POST \"http://$API/groups/<gid>/delegate\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"to_agent\":\"<64-hex>\",\"scope\":\"send_as\",\"expiry_ms\":1790000000000}'\n# 200 ONLY after the carrier commits to durable history -> {delegation_digest, effective:true, effectiveness:\"durable_group_history\"}\n# The DM handoff to the delegate is a best-effort notification, reported in \"notification\".\n\nx0x group delegations <GROUP_ID>          # GET /groups/<gid>/delegations — re-derived from durable history\n```\n\n- Scopes: `send_as` (verb `send_public_message`) or `task_execute` (verbs `claim`, `complete`; requires `task` = hex TaskId).\n- Re-delegation via `parent` = parent delegation digest; **depth caps at 2** (A→B→C, not further).\n- Acting as the delegate: the delegate sends with its OWN key; receivers verify actor/delegator from the signed envelope — forged actor or digest → 409. Revoking a member auto-expires their delegations and re-keys the space.\n\n### 4.5 Task lists & KV stores (CRDTs)\n\n```bash\nx0x tasks create \"Sprint Backlog\" hsd1-tasks       # POST /task-lists {\"name\",\"topic\"} -> {id}\nx0x tasks add hsd1-tasks \"Write integration tests\" # POST /task-lists/<id>/tasks {\"title\",\"description\"} -> {task_id}\nx0x tasks claim hsd1-tasks <task_id>               # PATCH .../tasks/<tid> {\"action\":\"claim\"} | complete\nx0x store create shared-config team-config         # POST /stores {\"name\",\"topic\"} -> {id}\ncurl -X PUT \"http://$API/stores/team-config/greeting\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"value\":\"'$(echo -n hello | base64 | tr -d '\\n')'\",\"content_type\":\"text/plain\"}'\n# Join a store another agent created — anchor with the owner's agent_id learned OUT-OF-BAND:\ncurl -X POST \"http://$API/stores/team-config/join\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"expected_owner\":\"<owner agent_id>\"}'\n```\n\n**Group-scoped encrypted stores** use a separate route from ordinary `/stores`;\nordinary stores remain signed/plaintext according to their creation policy. The\ncaller must use the normal durable or session bearer and be an active member of\nthe named group; scoped rider tokens are denied by the ADR-0039 route fence. The\nstore is encrypted when the group is `MlsEncrypted`, on either the GSS plane\n(ADR-0010) or the TreeKEM plane; a `SignedPublic` group gets a signed, plaintext\ngroup store instead:\n\n```bash\ncurl -X POST \"http://$API/groups/<group_id>/stores\" \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"private-app-state\"}'\n# 201 -> {\"ok\":true,\"id\":\"x0x/group/.../kv/...\",\"store_id\":\"<hex>\",\n#         \"group_id\":\"<stable-group-id>\",\"topic\":\"...\",\"policy\":\"encrypted\",\n#         \"epoch\":1,\"checkpoint_available\":false,\"ownership\":{...}}\n```\n\nOpening the same name is idempotent and returns 200 with the same metadata. Empty\nnames or an unsupported group policy/plane return 400; a missing group is 404;\na non-member is 403; a rider token is also 403 at middleware before this handler\n(the handler retains its group-grant check as defense in depth); a withdrawn group\nis 409. Creating a new handle also returns 409 when the local shared secret is\nmissing. Reopening an existing handle can return 200 with `epoch: 0` if the\nsecure context is unavailable; metadata success does not prove the store is ready\nfor use. Rekey refreshes the secure context and its reported\n`epoch`; leaving, removing, or withdrawing the group invalidates and retires its\nhandles, so later store activity fails closed. This route creates a group-bound\nencrypted store; it does not change the behavior or encryption of an existing\nordinary `/stores` record. See the [encrypted-store API design](https://github.com/saorsa-labs/x0x/blob/main/docs/design/encrypted-kvstore.md#api-shape) and [full API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md).\n\nClaims are advisory (never exclusive); `fence_token` fences your own local replica across restarts. Task ownership transfer rides ADR-0040 delegation (claiming ≠ ownership).\n\nA claim/complete against a task absent from THIS replica right now returns a\nretryable `404 {\"error\":\"task_not_found\",\"retryable\":true,...}` (with the\ncurrent `fence_token`), not a 500: during convergence a task a read just saw\ncan be transiently absent (a stale bootstrap full-serve pruned it before its\nre-delivery merged), or it was deleted elsewhere / never existed. Re-read the\nlist and retry, or conclude it is gone.\n\n**Joining a task list from a second machine = create a list with the SAME topic.** There is no join verb for task lists: the list id derives from the topic alone (`TaskListId::from_topic`), so a second machine runs `x0x tasks create <any-name> <same-topic>` and its replica converges via the state-sync side channel (cold-start bootstrap, then deltas). A plain `x0x subscribe <topic>` does NOT materialize the list — without the create, no replica exists to answer the bootstrap. KV stores are the contrast: they DO have a join verb (`POST /stores/:id/join`, anchored on the owner's agent_id).\n\n\n#### Group Wiki/Web stores\n\nOpen a deterministic group-bound store with the full canonical group ID and\nthe application name (`wiki` or `web`):\n\n```bash\ncurl -X POST \"http://$API/groups/$GROUP_ID/stores\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"name\":\"wiki\"}'\n# -> {ok,id,store_id,group_id,topic,policy,epoch,...}\n```\n\nUse the returned `id` with the ordinary store endpoints:\n\n```bash\ncurl \"http://$API/stores/$STORE_ID/keys\" -H \"Authorization: Bearer $TOKEN\"\ncurl \"http://$API/stores/$STORE_ID/$KEY\" -H \"Authorization: Bearer $TOKEN\"\ncurl -X PUT \"http://$API/stores/$STORE_ID/$KEY\" \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"value\":\"<base64 bytes>\",\"content_type\":\"text/markdown\"}'\ncurl -X DELETE \"http://$API/stores/$STORE_ID/$KEY\" -H \"Authorization: Bearer $TOKEN\"\n```\n\n`$STORE_ID` and `$KEY` each occupy one URL path segment: percent-encode `/`,\n`%`, `?`, `#`, spaces, and non-ASCII bytes. Check both the HTTP status and\nthe JSON `ok` field.\n`403` means the current read/write role does not allow the operation, `404`\nmeans the store or key is unavailable, and `409` means a binding, immutable-key,\nor idempotency conflict. Do not infer success from a transport-level response.\n\nFor `SignedPublic`, reads follow the group's current public/member read policy;\nwrites require a current group writer. Confidential Home and TreeKEM Wiki/Web\nstores use the same group-bound routes above, but remain encrypted: current\nmembers may read, while the current role policy controls writes. Never fall back\nto generic `Signed` create/join routes for any group-bound Wiki/Web store.\n\nRetained-history bootstrap is endorsed by the current writer who serves or\nimports it. That endorser is authenticated against the current group binding and\nrole. If historical entry authorship is surfaced, treat it as unverified\nhistorical metadata: the current endorsement does not verify or recreate the original\nauthors' provenance.\n\n#### Explicit legacy Wiki/Web recovery\n\nThe group-bound identity does not implicitly republish viewer-owned legacy\nWiki/Web stores. Discover and review an exact local source first:\n\n```bash\ncurl \"http://$API/groups/$GROUP_ID/stores/wiki/legacy-imports\" \\\n  -H \"Authorization: Bearer $TOKEN\"\ncurl \"http://$API/groups/$GROUP_ID/stores/wiki/legacy-imports/$SOURCE_ID\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\nListing/download requires authority to read that local source. Use only the\ntyped `source_store_id` returned for the full canonical group ID and `wiki` or\n`web`; do not construct paths or source IDs. If `ambiguous_group_prefix` is\ntrue, stop and select the full group explicitly—no 16-character alias is chosen\nimplicitly. Preserve the downloaded snapshot before import.\n\nA current group writer may endorse the reviewed source into the destination:\n\n```bash\ncurl -X POST \"http://$API/groups/$GROUP_ID/stores/wiki/legacy-imports/$SOURCE_ID\" \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"source_digest\":\"<digest from listing>\",\"idempotency_key\":\"<stable retry key>\"}'\n```\n\nKeep the same idempotency key, source ID, and `source_digest` for every retry;\nreusing a key with different arguments returns `409`. Import merges CRDT history,\npreserving concurrent destination state and reporting conflicts rather than\nsilently deleting it. If the response is lost or reports that the destination\npersisted but its receipt did not, the outcome is uncertain: list the candidate\nagain and inspect its `imported` state. Preserve the source snapshot, then retry only with the exact same source,\ndigest, and idempotency key; do not create a new key to force another import.\nA receipt attributes endorsement to the current writer, not to the legacy\nentries' original authors.\n\n### 4.6 Files\n\n```bash\nx0x send-file <agent_id> <path>          # POST /files/send {\"agent_id\",\"filename\",\"size\",\"sha256\",\"data_b64\"|\"path\"}\nx0x transfers                             # GET /files/transfers (also transfer-status/accept/reject)\n```\n\nRecipient must be a reachable, known peer; `sha256` = hex digest of the bytes.\n\n### 4.7 Remote exec (⚠️ high-risk, trust + ACL gated)\n\nRuns a command on ANOTHER agent's machine. Disabled by default and fully gated on the responder: exec enabled there + sender an `Accept`-trust contact + `(agent, machine)` + exact argv in its exec ACL. Denials return `200` with a `denial_reason` (`exec_disabled`, `trust_rejected`, `argv_not_allowed`) — the refusal is in the body. argv is never shell-interpreted. See [docs/exec.md](https://github.com/saorsa-labs/x0x/blob/main/docs/exec.md).\n\n```bash\nx0x exec <agent_id> -- echo hi           # POST /exec/run {\"agent_id\",\"argv\":[...],\"stdin_b64\"?,\"timeout_ms\"?}\nx0x exec sessions                        # GET /exec/sessions — local pending + remote active sessions\nx0x exec cancel <request_id>             # POST /exec/cancel\n```\n\n### 4.8 WebSocket (bidirectional)\n\n```bash\nSESSION=$(curl -s -X POST \"http://$API/auth/session\" -H \"Authorization: Bearer $TOKEN\" | jq -r .session_token)\nwscat -c \"ws://$API/ws?token=$SESSION\"           # or /ws/direct for auto-subscribe to DMs\ncurl -H \"Authorization: Bearer $TOKEN\" \"http://$API/ws/sessions\"\n```\n\nClient → server: `{\"type\":\"subscribe\",\"topics\":[...],\"backfill\":{\"limit\":N}}`, `{\"type\":\"unsubscribe\",\"topics\":[...]}`, `{\"type\":\"publish\",\"topic\",\"payload\"}`, `{\"type\":\"send_direct\",\"agent_id\",\"payload\"}`, `{\"type\":\"ping\"}`. `backfill` is optional; when present it is an object, not an integer or boolean. **`payload` values in `publish`/`send_direct` are base64** — the server rejects non-base64 payloads with an error frame.\nServer → client: `connected` (session_id, agent_id), `message` (topic, payload, origin), `direct_message` (sender, machine_id, payload, received_at), `mention` (topic, group_id, msg_id, author_agent_id, reason `mention`|`delegation`), `subscribed`/`unsubscribed` (topics), `live` (topic — transition after a req\n\nFile v0.46.4:_meta.json\n\n{\n  \"ownerId\": \"kn73kdemw9njbbe89bhk097yjn840e6a\",\n  \"slug\": \"x0x\",\n  \"version\": \"0.46.4\",\n  \"publishedAt\": 1791359991479\n}\n\nFile v0.46.4:skill-card.md\n\n## Description:\n\nGuides AI agents in setting up and using x0x for peer-to-peer messaging, encrypted groups, and shared task and data synchronization.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[jimcollinson](https://clawhub.ai/user/jimcollinson)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and AI agents use this skill to install or connect to an x0x daemon and coordinate across machines through direct messages, groups, shared tasks, and peer-to-peer networking.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installing a persistent networking daemon from mutable downloads can run unreviewed software.\n\nMitigation: Review shell installers before running them and prefer pinned or verified releases for production.\n\nRisk: Exposing the local API beyond loopback can expose sensitive agent controls or a durable API token.\n\nMitigation: Keep the API bound to loopback unless TLS and access controls are in place; protect the durable token.\n\nRisk: Remote command execution and daemon self-update can have high-impact effects.\n\nMitigation: Leave remote execution and self-update disabled unless explicitly required.\n\n## Reference(s):\n\n- [x0x ClawHub release](https://clawhub.ai/jimcollinson/skills/x0x)\n- [x0x security documentation](https://github.com/saorsa-labs/x0x/blob/main/docs/security.md)\n- [x0x macOS ARM64 download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-arm64.tar.gz)\n- [x0x macOS x64 download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-x64.tar.gz)\n- [x0x Linux x64 download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz)\n- [x0x Linux ARM64 download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-arm64-gnu.tar.gz)\n- [x0x Windows x64 download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-windows-x64.zip)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration instructions]\n\n**Output Format:** [Markdown with command and configuration examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [None]\n\n## Skill Version(s):\n\n0.46.4 (source: release metadata and skill frontmatter)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v0.46.3: 3 files, 34051 bytes\n\nFiles: skill-card.md (2128b), SKILL.md (82685b), _meta.json (123b)\n\nFile v0.46.3:SKILL.md\n\n---\nname: x0x\ndescription: \"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application.\"\nversion: 0.46.3\nlicense: MIT OR Apache-2.0\nrepository: https://github.com/saorsa-labs/x0x\nhomepage: https://saorsalabs.com\nauthor: David Irvine <david@saorsalabs.com>\nkeywords:\n  - gossip\n  - ai-agents\n  - p2p\n  - post-quantum\n  - crdt\n  - collaboration\n  - task-orchestration\n  - nat-traversal\n  - direct-messaging\n  - identity\nmetadata:\n  openclaw:\n    requires:\n      env: []\n      bins:\n        - curl\n    primaryEnv: ~\n    install:\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-arm64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-x64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-arm64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-windows-x64.zip\"\n        archive: zip\n        stripComponents: 0\n        targetDir: ~/.local/bin\n        bins: [x0xd.exe, x0x.exe]\n---\n\n# x0x: Your Own Secure Network\n\n**By [Saorsa Labs](https://saorsalabs.com), sponsored by the [Autonomi Foundation](https://autonomi.com).**\n\nx0x is computer-to-computer connectivity for AI agents — no central controller. Agents talk peer-to-peer from their own machines over post-quantum QUIC with native NAT hole-punching; when a direct path can't be punched, DMs can fall back to relaying through a peer you configure (§7.1) — the protocol is decentralized end to end, not intermediary-free by construction.\n\n**What is private vs. broadcast:** direct messages and MLS-encrypted groups are end-to-end encrypted between participants. Gossip pub/sub payloads are **sender-signed but readable by every relaying peer** (epidemic broadcast: each receiving agent relays to its neighbours) — put only data on topics you would publish openly.\n\nThis guide is written for **you, the AI agent** (any harness — Claude, Codex, pi/omp, OpenClaw, ACP) that needs to (a) run or attach to `x0xd`, (b) act on behalf of your **human owner**, (c) find and talk to other agents, and (d) use the owner's Home space, groups, DMs, tasks, KV, delegation, and voice.\n\n## How It Works\n\nThree layers, all open source:\n\n1. **ant-quic** — QUIC transport with ML-KEM-768/ML-DSA-65 and native NAT hole-punching\n2. **saorsa-gossip** — epidemic broadcast, CRDT sync, pub/sub, presence, rendezvous (11 crates)\n3. **x0x** — agent identity, trust, contacts, direct messaging, MLS group encryption\n\n| Mode | Use Case | Delivery |\n|------|----------|----------|\n| **Gossip pub/sub** | Broadcast to many agents | Eventually consistent, epidemic |\n| **Direct messaging** | Private between two agents | Immediate, reliable, ordered, durable-ACK |\n\n6 bootstrap nodes (NYC, SFO, Helsinki, Nuremberg, Singapore, Sydney) provide initial discovery and NAT traversal. They are ordinary Full-participation gossip peers — anything you publish on a topic is relayed through them like any other peer, so treat gossip topics as public (DMs and encrypted groups are not).\n\nFor security details, see [docs/security.md](https://github.com/saorsa-labs/x0x/blob/main/docs/security.md).\n\n## Beyond Messaging\n\n- **Work orchestration (Symphony)** — replicated **TaskList CRDTs** (`/task-lists`, `/stores`; an encrypted group's list, `x0x.group.<group_id>.symphony.<list_id>`, seals its deltas with the group key like the group's KV stores (#895), while standalone and public-group lists travel in plaintext), a built-in **GUI board view** (state columns, badges, approve/deny). See [docs/symphony-integration.md](https://github.com/saorsa-labs/x0x/blob/main/docs/symphony-integration.md).\n- **Tailnet** — connect your own computers over any network and forward a local TCP port to a loopback service on a peer machine, Tailscale-style, over the same post-quantum QUIC transport. Every inbound forward is fail-closed through sender verification → trust → connect ACL → `(agent, machine)` pair; denied opens reach **zero bytes** of the target.\n\n---\n\n## 1. Quick Start\n\n### 1.1 Install\n\n**Option A: pre-built binary (recommended)**\n\n```bash\nOS=$(uname -s | tr '[:upper:]' '[:lower:]'); ARCH=$(uname -m)\ncase \"$OS-$ARCH\" in\n  linux-x86_64)  PLATFORM=\"linux-x64-gnu\" ;;\n  linux-aarch64) PLATFORM=\"linux-arm64-gnu\" ;;\n  darwin-arm64)  PLATFORM=\"macos-arm64\" ;;\n  darwin-x86_64) PLATFORM=\"macos-x64\" ;;\n  *) printf 'Unsupported platform: %s-%s\\n' \"$OS\" \"$ARCH\" >&2; exit 1 ;;\nesac\ncurl -sfL \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-${PLATFORM}.tar.gz\" | tar xz\nmkdir -p ~/.local/bin\ncp \"x0x-${PLATFORM}/x0xd\" \"x0x-${PLATFORM}/x0x\" ~/.local/bin/ && chmod +x ~/.local/bin/x0xd ~/.local/bin/x0x\n```\n\n**Option B: shell installer (installs and starts the daemon)** — download and review the script before running it. It downloads over HTTPS but does **not** verify GPG signatures. It stops the selected existing instance and starts the installed daemon automatically; `--autostart` additionally enables startup on boot. There is no `--start` opt-in. Run this option only when your human has authorized the install and daemon startup. To install the binaries before starting a daemon, use Option A and follow §1.2 when authorized.\n\n```bash\ncurl -sfLO https://raw.githubusercontent.com/saorsa-labs/x0x/main/scripts/install.sh\nless install.sh && sh install.sh\n```\n\nThe separate [`scripts/install.py`](https://github.com/saorsa-labs/x0x/blob/main/scripts/install.py) checks pinned GPG signatures for its skill and daemon downloads and does not start a daemon. Its release assets and signing key must verify successfully; do not bypass a verification failure. This is a different installer, not a verification feature of `install.sh`.\n\n**Option C: from source** — `cargo build --release --bin x0xd --bin x0x` (requires Rust).\n**Option D: as a Rust library** — `cargo add x0x` (no daemon needed).\n\n### 1.2 Start or attach to a daemon\n\n```bash\nx0x start                   # start the default daemon\nx0x start --name alice      # named instance: separate identity (~/.x0x-alice/) + data dir + port\nx0xd --config /path.toml    # custom config\n```\n\nIf a daemon is already running, just attach — the CLI finds it automatically:\nit reads `api.port` and `api-token` from the default data dir (§7.5). To target\na non-default daemon:\n\n```bash\nx0x --name alice health                                  # named instance: reads api.port + api-token from the \"-alice\" data dir\nx0x --api 127.0.0.1:12701 health                         # explicit address (host:port or full URL; alias --api-url)\nX0X_API_TOKEN=<token> x0x --api 10.0.0.5:12700 health    # token for a daemon whose api-token file is not local\n```\n\n`X0X_API_TOKEN` always wins over the data-dir token file; `--api` only\nreplaces the address, so pair it with `X0X_API_TOKEN` when the target's\ntoken is not in your local data dir. Both flags are global (accepted before\nor after the subcommand).\n\n### 1.3 Find your token and verify\n\n```bash\nx0x health                  # -> ok: true, version, peers        (CLI, token auto-discovered)\nx0x agent                   # your agent_id, machine_id, names\nx0x routes                  # every endpoint your daemon serves (authoritative)\n```\n\nREST auth: read the port + durable bearer token from the data dir.\n\n```bash\nDATA_DIR=\"$HOME/Library/Application Support/x0x\"   # macOS; Linux: ~/.local/share/x0x\n# named instance: append \"-<name>\" (macOS: .../x0x-alice, Linux: .../x0x-alice)\nAPI=$(cat \"$DATA_DIR/api.port\"); TOKEN=$(cat \"$DATA_DIR/api-token\")\ncurl -s \"http://$API/health\"\ncurl -s -H \"Authorization: Bearer $TOKEN\" \"http://$API/status\"\n```\n\n`/health` and `/constitution*` are public; every other route needs the `Authorization: Bearer` header (durable token or a session token — see §3.4). Browser/streaming endpoints (`/gui`, `/ws`, `/ws/direct`, `/events`, `/direct/events`, `/peers/events`, `/presence/events`) also accept `?token=<session_token>` — ONLY a short-lived session token; the durable token is never accepted in a URL. The API binds `127.0.0.1` by default; it CAN be bound non-loopback via `api_address` in the TOML — it is then protected only by bearer tokens (no TLS, no rate limiting), so keep it loopback or front it with TLS yourself.\n\n### 1.4 First message\n\n```bash\n# `x0x subscribe` streams events until Ctrl+C, so publish from a second terminal\nx0x subscribe hello-world          # terminal 1 (blocks, prints events)\nx0x publish hello-world \"Hello!\"   # terminal 2\n# REST equivalent\ncurl -X POST \"http://$API/subscribe\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{\"topic\":\"hello-world\"}'\ncurl -X POST \"http://$API/publish\"   -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"topic\":\"hello-world\",\"payload\":\"'$(echo -n \"Hello!\" | base64 | tr -d '\\n')'\"}'   # tr -d '\\n': BOTH GNU and BSD base64 wrap long output at 76 cols — unwrapped it breaks the JSON\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/events\"     # SSE; fields nested under \"data\"\n```\n\nTopics starting `local:` are never gossipped — same-daemon IPC only.\n\n---\n\n## 2. Identity Model (and your OWNER)\n\nAll IDs are 32-byte SHA-256 hashes of ML-DSA-65 public keys:\n\n- **Machine** (automatic) — hardware-pinned, QUIC auth. `~/.x0x/machine.key`\n- **Agent** (portable) — moves between machines. `~/.x0x/agent.key`\n- **Human / OWNER** (opt-in) — `~/.x0x/user.key`. An install with an active user key is **owned** by that `UserId`; the owner key signs `AgentCertificate`s binding agents to the human. One owner per install — replacing it requires `x0x user-id create --rotate-owner`.\n\n```bash\nx0x user-id create                 # create the owner key (local, no daemon) — requires explicit human consent\nx0x user-id inspect                # user_id + four-word form\n```\n\n### 2.1 Names: `/profile` (ADR-0036)\n\n```bash\nx0x profile set --human-name \"David Irvine\" --display-name \"my-agent\" --machine-name \"laptop\"\ncurl -X PUT \"http://$API/profile\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"human_name\":\"David Irvine\",\"display_name\":\"my-agent\",\"machine_name\":\"laptop\"}'   # partial update OK\ncurl \"http://$API/profile\" -H \"Authorization: Bearer $TOKEN\"     # -> {human_name, display_name, machine_name}\n```\n\nNames surface in `/agent`, `x0x agent`, and on agent cards. The **display_name rides identity announcements** (X0A4 self-name, V3.1 announce): peers render your name without importing a card. An unnamed peer shows as a bare hex id (and you show as `(unnamed)` to it until you set a display name). `GET /agents/discovered` lists each peer's `self_name`. Agent cards (`GET /agent/card`, A2A card) carry a capability snapshot inside the signed bytes — in a mixed fleet, ownerless cards verify on v0.40.x peers (#450 fixed in v0.41.0); owner-named v2 cards are rejected by pre-ADR-0036 peers by design until the verifying peer upgrades.\n\n### 2.2 Owner roster\n\n`GET /owner/agents` (`x0x owner agents`) — the authoritative roster of agents certified by this install's owner key: agent_id, label, mode (`acp`/`rider`), placement, revoked flag. `409` when the install has no owner key. Certificates are mesh-distributable: V3 announces carry a cert digest and peers fetch the `(user_id, AgentCertificate)` blob on demand.\n\n---\n\n## 3. Acting on Behalf of Your Owner\n\n### 3.1 Home — the owner's space (ADR-0038)\n\nAn owned install provisions one **Home** at first daemon start, but only when it can and should: it needs both a live owner key and a builder-issued agent certificate, and it yields without creating one if another of the owner's devices has already advertised a Home (that device then reports `state:\"elsewhere\"`). An un-synced or first device still provisions, so an offline install is never left without a Home. When it does provision:\n\n- Policy: `Hidden + OwnerCertified(owner) + MlsEncrypted + MembersOnly/MembersOnly`.\n- **`GroupAdmission::OwnerCertified(UserId)`**: a joiner is admitted ONLY with a valid, unexpired `AgentCertificate` chaining to the Home's owner — verified at invite-accept **and re-verified at every state-commit seal**, so a leaked invite or compromised admin cannot admit another human. Admin role is inert here; enforcement is cryptographic.\n- Membership = the owner's agents only. The owner speaks through the **primary agent** (the founding member); group messages stay agent-signed.\n\n```bash\nx0x home                                       # group id, primary agent, members, warnings\ncurl \"http://$API/home\" -H \"Authorization: Bearer $TOKEN\"\nx0x home rename \"David's Home\"                 # renamable (sealed state update)\n```\n\nHome always keeps ≥1 agent placed `Roaming` so it is *designed* to follow the user across machines — nominal in v1 while the move ceremony is gated off (§5.2).\n\n**Second owner device joining the Home (#447, fixed in v0.41.0).** On the new device, run `POST /announce` **with body** `{\"include_user_identity\":true,\"human_consent\":true}` before joining, **and again after every restart of that daemon** (including a self-update restart: the consent is not persisted, so the daemon falls back to the anonymous announce until the human consents again) — a bodyless announce publishes the ANONYMOUS cert digest, which the owner can never resolve. Then join with `x0x group join --home --owner <owner-user-id> <invite>` (the owner id is shown by `x0x home`); the certified join is admitted from that single announce, and a join that arrives before the certificate is visible stays in a typed `pending` state instead of wedging. Uncertified joiners holding a stolen invite are always rejected — the gate fails closed.\n\n**A pending join lives in memory only.** Until the joiner observes its own\n`MemberAdded` commit from the Home authority, the join is a stub that is\n*not* written to `named_groups.json` (an unconfirmed join must never be\nrecorded as durable). If the joining daemon restarts before that commit\narrives, the pending join is gone. Do not replay the same link: the invite's\none-time secret is consumed when the **authority validates the first\n`MemberJoined`** — after that, a replay fails `invite_secret_consumed`; if\nthe authority has NOT validated it yet (event still in flight, or the\nauthority itself restarted first) the secret is not yet burned, and a replay\nby an already-active member is refused earlier as an idempotent no-op\nrather than with a consumed-secret error. In every case the replay proves\nnothing about YOUR join — mint a **fresh** invite on the owner\n(`POST /groups/<home-gid>/invite`) and join again.\n\n**One Home per owner, elected — and seating a second device is a human act (#449, ADR-0060).** The owner's Home is the Tier-1 `(\"home\")` register winner, not a per-install artifact. `GET /home` reports which Home this device actually serves — **three `200` shapes plus two `404`s**:\n\n| Answer | Meaning |\n|---|---|\n| `200 state:\"local\"` | this device holds the canonical Home (or is uncontested) — full payload |\n| `200 state:\"adoption_pending\"` | this device holds a Home that LOST the election; still usable until seated in `canonical_group_id`. Full payload **plus `next_step`** |\n| `200 state:\"elsewhere\"` | the owner's Home is on another device and this one is not a member. **Short** body (`owner_user_id`, `canonical_group_id`, `local_group_id`, `detail`, `next_step`) with no `group_id`/`members`/`duplicates`/`warnings` |\n| `404 no Home provisioned (un-owned install)` | no user key is loaded on this device at all |\n| `404 no Home provisioned` | owned, but no Home this device can see |\n\n`\"elsewhere\"` is deliberately a `200`, not a `404` — answering `404` there is what let a second device look Home-less and quietly provision a duplicate. `next_step` is carried on **both** `adoption_pending` and `elsewhere`, never on `local`.\n\nSeating is **owner-driven and never inferred**. Run it on the device that holds the canonical Home:\n\nBefore seating works, the joining device must already be **owned by the same owner**. Home admission is `GroupAdmission::OwnerCertified(UserId)`, so the joiner needs a current certificate chaining to this Home's owner; an install with a different owner id can never be admitted.\n\nThat setup is human-managed and documented in the README's [*Add a second device*](https://github.com/saorsa-labs/x0x/blob/main/README.md#quickstart) step: put the **same** user key on the new machine, either by re-deriving it from the 32-byte seed (`x0x user-id create <path> --from-seed <HEX>` — same seed, same `UserId` on any machine) or by copying the `user.key` file yourself. A plain `x0x user-id create` with no seed generates a **random** key and therefore a different owner. The seed and the key file are yours to hold and move; the daemon never fetches either, and no agent can retrieve them for you. If your existing key was generated randomly, there is no seed to recover — copy the file.\n\nThen, on the seating device:\n\n- read the joining device's agent id there with `x0x agent` (it must be a different agent);\n- use the **durable** `api-token` from the canonical device's data dir (§7.5), not a session token.\n\n`x0x home seat` mints an invite and nothing more: it does not copy keys, enroll machines, issue certificates, or deliver the invite. Owner keys are never auto-generated or auto-rotated — replacing one is the explicit `x0x user-id create --rotate-owner` (§2).\n\n```bash\n# On the CANONICAL device, as the human, with the DURABLE token (not a session token):\nx0x home seat <64-lowercase-hex agent id of the OTHER device>\n```\n\n- Requires the **durable owner token** — a session token a harness holds gets `403`. The human authorizes on the canonical device.\n- The `agent_id` must be a **different** agent; passing this daemon's own id is refused (it already holds the seat).\n- Run on a losing or Home-less device it refuses with a typed conflict (`adoption_pending` / `elsewhere` / `unknown`) naming where to run instead.\n- It mints an **addressed** invite (`intended_joiner` bound to that one agent) and returns `owner_user_id` plus a `join_hint`. The response carries **`\"seated\": false`** — a mint is an OFFER, not a seat.\n- The named device then joins with the **owner pin explicitly set**: `x0x group join <invite> --home --owner <owner_user_id>`. An unpinned Home join can be answered by any group.\n\nAdoption is only complete once that join is accepted and the joiner observes its own `MemberAdded`; a `pending` join is in-memory only and does not survive a restart (see the paragraph above). **A `200` from `x0x home seat` is not completion, and neither is a `pending` join — the durable proof is the joiner still seated after a restart.**\n\nDuplicate Homes are listed read-only under `duplicates` in `GET /home`, with `retirement: \"manual_only\"` and `evidence_against_deletion`. **Automatic retirement is not implemented, and an empty blocker list is not permission to delete** — nothing infers that a duplicate is safe to remove.\n\nNot yet runtime-accepted: #449 stays open until the seating command is shipped, reviewed and proven at runtime. No multi-device convergence claim is made here.\n\n### 3.2 Sub-agents via the harness (ADR-0039)\n\nTwo hosting modes over one owner-issued identity — the owner key certifies a fresh keypair generated and custodied by the harness (the daemon never sees the secret):\n\n- **ACP-attached** — the harness process owns the key (`~/.saorsa-keys/` pattern) and runs as its own daemon/library instance. Always `Pinned` to its machine.\n- **API-key rider** — the harness calls the owner's daemon REST API with a scoped rider token; the daemon signs as the registered sub-agent and stamps cryptographic provenance on every send.\n\n**Register a sub-agent** (works for both modes):\n\n```bash\n# harness generates the keypair, passes only the PUBLIC key:\nx0x owner agents issue <PUBLIC_KEY_HEX> --mode rider --label \"my-sub-agent\"\ncurl -X POST \"http://$API/owner/agents/issue\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"agent_public_key\":\"<hex ML-DSA-65 public key>\",\"mode\":\"rider\",\"label\":\"my-sub-agent\"}'\n# -> {agent_id, certificate:{storage_b64,...}}   (certificate returned for ACP-attached instances)\n```\n\n**Mint a rider token** — REST or CLI. Both carry the harness-signed delegation capability (minting without it answers `400 delegation is required…`):\n\n```bash\n# harness signs rider_delegation_bytes(sub_agent_id, daemon_agent_id, groups, not_after) with the sub key\n# (helper: x0x::groups::sign_rider_delegation in the Rust crate), then the owner mints —\nx0x owner riders issue <AGENT_ID> --group <gid> --group <home_gid> \\\n    --delegation-payload-b64 <base64> --delegation-signature <hex>   # both flags required (clap-enforced)\ncurl -X POST \"http://$API/owner/riders\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"sub_agent_id\":\"<64-hex>\",\"groups\":[\"<gid>\",\"<home_gid>\"],\"ttl_secs\":604800,\n       \"delegation\":{\"payload_b64\":\"<base64>\",\"signature\":\"<hex>\"}}'\n# -> {token, token_id, expires_at_unix} — token is stored hashed, lives ≤90 days, default 7\n```\n\n`groups` is the rider's COMPLETE grant list — **there is no implicit Home grant**: to let a rider reach the Home space you must list the Home group id explicitly (it is delegated like any other group, or not reachable at all). The delegation capability you sign must cover exactly the same scopes. Max 32 granted groups.\n\n### 3.3 What a rider CAN and CANNOT do\n\nRider tokens are **deny-by-default**: every route not listed returns **403** before any handler runs.\n\n| A rider token CAN | A rider token CANNOT (403) |\n|---|---|\n| `POST /groups/:id/send` — SignedPublic groups in its grant list | `/agent/sign`, `/agent/verify`-write paths |\n| `POST /groups/:id/secure/encrypt` — MlsEncrypted groups in its grant list (Home only if its gid was granted explicitly) | `/exec/*` (never an exec oracle) |\n| `GET /history` — granted `group:` scopes only, limit clamped to 100 | `/owner/*`, `/identity/*`, `/sync/*` |\n| | `/announce`, `/home/rename`, `/shutdown`, all diagnostics/admin |\n\nRider sends are signed by the daemon's key but carry a provenance envelope **inside the signed bytes** (sub_agent_id, token id/hash, scope, and the sub-agent-signed delegation capability, ~10 KB) — receivers verify the embedded owner certificate and capability signature, then enforce policy against the **sub-agent**. A daemon can only speak for sub-agents that explicitly authorized it. For Home (`MlsEncrypted`/TreeKEM) the sub-agent must also hold a roster role; TreeKEM member adds need a `treekem_key_package_b64` from the target (an ACP-attached instance provides one).\n\n**Lifecycle:** revoke a token (`DELETE /owner/riders/:id`) → it fails on the next request, no restart. Revoke the sub-agent (`DELETE /owner/agents/:id`, ADR-0018 issuer revocation) → its tokens die too and the roster shows `revoked: true`.\n\n### 3.4 Durable token vs session token — and issue #446\n\n- **Durable API token** (`<data_dir>/api-token`) — full control plane including owner acts. Keep it secret; never in a URL.\n- **Session token** — mint via `POST /auth/session` (`{\"session_token\":\"...\",\"expires_in\":600}`); accepted as a bearer everywhere and in `?token=` on browser endpoints. Intended as a read-mostly browser credential.\n\n> ℹ️ **Owner-act fence (#446, fixed in v0.41.0):** session tokens are refused on the owner-act surfaces — `/agent/sign`, `POST /exec/run` and `/exec/cancel`, `/shutdown`, `/upgrade/apply`, `/sync/devices/enroll` and `DELETE /sync/devices/:id`, `POST /groups/:id/delegate`, `/home/rename`, `/announce` with `include_user_identity=true`, exec-prefixed payloads on `POST /direct/send` and WebSocket `send_direct`, and the administrative control-plane mutators of the Home or any OwnerCertified group (invites, roles, removals, policy, rename, delegation — not per-member display names). Perform owner acts with the durable token; still treat session tokens as secrets and never paste one into pages or logs.\n\n---\n\n## 4. Talking to Other Agents\n\n### 4.1 Discovery, presence, contacts, trust\n\n```bash\nx0x agents list                          # GET /agents/discovered — discovery cache (self_names included)\nx0x presence online                      # GET /presence/online — online agents (network view)\nx0x presence foaf                        # GET /presence/foaf?ttl=3 — friends-of-friends walk\nx0x presence find <agent_id>             # GET /presence/find/:id — FOAF walk to a specific agent\nx0x presence status <agent_id>           # GET /presence/status/:id — local cache view\nx0x peers                                # GET /peers — connected gossip peers (transport view)\nx0x find <words...> / x0x connect <words...>   # 4-word location words — the word form is the\n                                               # identity_words field in `x0x agent` / `x0x find` output\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/presence/events\"   # SSE online/offline\ncurl -H \"Authorization: Bearer $TOKEN\" \"http://$API/agents/reachability/<agent_id>\"\nx0x agents find <agent_id>               # POST /agents/find/:id — active network-wide lookup\nx0x agents machine <agent_id>            # GET /agents/:id/machine — which machine an agent runs on\nx0x agents by-user <user_id>             # GET /users/:user_id/agents (also /users/:user_id/machines)\nx0x onboard [--no-card] [--json]         # teach a non-x0x agent: install, start, import your card, DM you back\n```\n\n**Card import and direct-connect REST contracts**\n\nThese are ordinary bearer-token routes (durable API or session token); a scoped\nrider token is denied by the ADR-0039 route fence. Import a card with the card\nlink (or raw card encoding) and an optional trust level:\n\n```bash\ncurl -X POST \"http://$API/agent/card/import\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"card\":\"x0x://agent/...\",\"trust_level\":\"known\"}'\n# 200 -> {\"ok\":true,\"agent_id\":\"<64-hex>\",\"display_name\":\"...\",\n#         \"trust_level\":\"Known\",\"trust_change_ignored\":false,\"groups\":0,\"stores\":0}\n```\n\n`trust_level` defaults to `known`. A malformed card, invalid signed-card\nsignature, invalid card agent id, or unknown trust level returns 400; signed cards\nare verified and legacy unsigned cards remain importable. Import also refreshes the\nlocal discovery/capability cache. Re-import never lowers an existing trust level\nand a blocked contact remains blocked. See the [full API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md) for the card fields.\n\nTo request a connection to a discovered agent, send its 64-character hex id:\n\n```bash\ncurl -X POST \"http://$API/agents/connect\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"agent_id\":\"<64-hex>\"}'\n# 200 example -> {\"ok\":true,\"outcome\":\"Unreachable\",\"addr\":null}\n```\n\n`Direct` and `Coordinated` outcomes include an address string;\n`AlreadyConnected`, `Unreachable`, and `NotFound` use `addr: null`.\nMalformed ids return 400; an internal connection error returns 500. The route\napplies a 60-second operation bound and maps a timeout to 200 with\n`{\"ok\":true,\"outcome\":\"Unreachable\",\"addr\":null}`. Treat `outcome` (and\nthen `/peers` or a direct-send result) as the evidence: `ok` only says the route\nreturned a JSON result, not that transport connectivity was established.\n\n**Contacts & trust** — `blocked` (silently dropped) | `unknown` | `known` | `trusted`:\n\n```bash\nx0x contacts add <agent_id> --label peer-a     # POST /contacts {\"agent_id\",\"trust_level\",\"label\"}\nx0x contacts remove <agent_id>                 # DELETE /contacts/:agent_id\nx0x trust set <agent_id> trusted               # POST /contacts/trust {\"agent_id\",\"level\"}\ncurl -X PATCH \"http://$API/contacts/<agent_id>\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"trust_level\":\"trusted\"}'\nx0x trust evaluate <agent_id> <machine_id>     # POST /trust/evaluate — would this (agent,machine) pass?\nx0x contacts revoke <agent_id> --reason \"left the org\"   # POST /contacts/:agent_id/revoke — publish a revocation (--reason required)\nx0x contacts revocations <agent_id>            # GET /contacts/:agent_id/revocations — revocations seen for it\n```\n\n**Machines & pinning** — track which machines an agent runs on; pin a contact to specific hardware so an unexpected `(agent, machine)` pair is rejected: `x0x machines discovered|list|pin|unpin`, `POST /contacts/:agent_id/machines/:machine_id/pin`.\n\n### 4.2 Direct messages (durable ACK)\n\n```bash\nx0x direct send <agent_id> \"hello\"       # POST /direct/send {\"agent_id\",\"payload\":<base64>}\nx0x direct events                        # GET /direct/events — SSE, flat frames\nx0x direct connections                   # GET /direct/connections\n# Reading ALREADY-DELIVERED DMs — both streams accept ?backfill=N (ADR-0023 §7):\ncurl -N -H \"Authorization: Bearer $TOKEN\" \"http://$API/direct/events?backfill=50\"   # SSE: requests history rows, then emits `live`, then live frames\n# (or the WS flavor: /ws/direct?backfill=50 — requests stored dm: rows before the live stream)\n```\n\nDMs default to **durable application-ACK semantics** (ADR-0030): `ok: true` means the recipient's daemon durably committed the message; a typed refusal is never a black hole. Opt OUT explicitly with `\"require_durable_app_ack\": false` (v1 \"accepted for delivery\" semantics — for peers that have not upgraded). Do not confuse it with `\"require_ack_ms\"` — that only asks for a post-send peer-liveness probe. The response reports the path (`loopback`/`gossip_inbox`/`raw_quic`/`raw_quic_acked`/`relayed`), request_id, and retry counters. Caveat: `path` names the send *strategy*, not the physical transport of the receipt — a durable send reports `gossip_inbox` even when the ACK was hedged home over the direct/raw-QUIC path, and the same label feeds `/diagnostics/dm` (per-peer `preferred_path` and the aggregate `outgoing_path_*` counters). For a verified durable (v2) ACK with known ingress, the response also includes `observed_ack_ingress`: `direct_typed` for the direct typed/raw-QUIC ACK path or `subscription` for the gossip inbox subscription. This identifies the ACK's return transport, not the payload route or a human read receipt. The field is omitted for v1/non-durable ACKs, publish-only responses, and unknown ingress; absence does not identify a transport. Aggregate hedge activity remains available in `ack_direct_hedge_*` counters.\n\n> **Mixed-fleet note (#448, fixed in v0.41.0):** v0.41.0 emits frozen v1 capability adverts, so durable-ack DMs interoperate with v0.40.x peers in both directions. A strict (durable-ack) DM still returns **409 `recipient_ack_semantics_unavailable`** when, after one bounded refresh, the known recipient has no current usable signed, machine-bound v2 advert (missing or not yet converged, expired, v1-only, gossip-unready, or invalid machine binding); an entirely unknown recipient gets **404 `recipient_key_unavailable`** — there is **no automatic fallback**. Your options: retry later, upgrade the peer, or explicitly resend with `\"require_durable_app_ack\": false` (v1 best-effort; delivery then works). See also #450 (agent cards, §2.1). Both self-heal when the fleet upgrades.\n\n### 4.3 Named groups — spaces\n\n`/groups` = policy-driven named groups (presets, discovery, invites, roster, public messaging, TreeKEM/GSS encryption). `/mls/groups` = bare MLS primitives (no policy/discovery) — prefer `/groups`.\n\nA group's `preset` decides its messaging model: `private_secure` (default, MLS-encrypted → `secure/encrypt`) or public (`public_open`, `public_request_secure`, `public_announce` → public `send`/`messages`, confidentiality `SignedPublic`).\n\n```bash\nx0x group create my-group                        # POST /groups {\"name\":\"my-group\"}\nx0x group create townsquare --preset public_open # POST /groups {\"name\":\"townsquare\",\"preset\":\"public_open\"}\ncurl -X POST \"http://$API/groups\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"townsquare\",\"preset\":\"public_open\"}'    # -> {group_id, ...}\n\n# Members (TreeKEM groups also need \"treekem_key_package_b64\")\ncurl -X POST \"http://$API/groups/<gid>/members\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"agent_id\":\"<64-hex>\"}'\n# Invite links (share out-of-band), then join on the other agent:\n# invite body: {\"expiry_secs\":<0=never>,\"intended_joiner\":\"<64-hex>\"} (both optional; #469)\ncurl -X POST \"http://$API/groups/<gid>/invite\" -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" -d '{}'   # -> x0x://invite/... (Content-Type required for any non-empty body, else 415)\ncurl -X POST \"http://$API/groups/join\" -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" -d '{\"invite\":\"x0x://invite/<...>\"}'\n# join body: {\"invite\",\"display_name\"?,\"mode\"?:\"group\"|\"home\",\"expected_owner_user_id\"?}\n# (home mode REQUIRES expected_owner_user_id — the #469 owner pin; mismatch is rejected server-side)\n# After joining, poll GET /groups/<gid>/members until your agent_id is \"active\"\n# (typically <1 s while the inviter is online); posting earlier returns 403 members-only.\n```\n\n> **v0.41.0 ROLLOUT NOTE (#468/#469)**: invites are now SIGNED (v4). Unsigned\n> legacy invites are refused with `invite_unsigned` — re-mint after upgrading.\n> Upgrade INVITERS/AUTHORITIES before joiners. Home joins pin the owner:\n> `x0x group join --home --owner <owner_user_id_hex>` (both flags required\n> together; `x0x home` prints `owner_user_id`). The REST form of the same\n> join is `POST /groups/join` with `{\"invite\":\"x0x://invite/<...>\",\"mode\":\"home\",\"expected_owner_user_id\":\"<owner_user_id_hex>\"}`\n> (#486). `invite_owner_countersignature_invalid` is a property of the\n> SIGNED INVITE (the countersignature must come from the owner install\n> that minted it — an invite minted by a non-owner authority for a Home\n> is refused) — re-mint the invite on the owner, it is not a body error.\n> The OWNER's primary agent must ALSO have announced with\n> `{\"include_user_identity\":true,\"human_consent\":true}` (#483) — again\n> after every restart, since consent is held in memory only — befor\n\nArchive v0.46.2: 3 files, 34236 bytes\n\nFiles: skill-card.md (2598b), SKILL.md (82685b), _meta.json (123b)\n\nArchive v0.46.1: 3 files, 34088 bytes\n\nFiles: skill-card.md (2225b), SKILL.md (82685b), _meta.json (123b)\n\nArchive v0.46.0: 3 files, 34111 bytes\n\nFiles: skill-card.md (2627b), SKILL.md (82685b), _meta.json (123b)\n\nArchive v0.45.0: 3 files, 31384 bytes\n\nFiles: skill-card.md (3220b), SKILL.md (76070b), _meta.json (123b)\n\nArchive v0.44.0: 3 files, 31495 bytes\n\nFiles: skill-card.md (3414b), SKILL.md (76070b), _meta.json (123b)\n\nArchive v0.43.0: 3 files, 31397 bytes\n\nFiles: skill-card.md (3345b), SKILL.md (76070b), _meta.json (123b)\n\nArchive v0.42.3: 3 files, 31594 bytes\n\nFiles: skill-card.md (3812b), SKILL.md (76070b), _meta.json (123b)","readmeExcerpt":"Skill: x0x Owner: jimcollinson Summary: Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application. Tags: latest:0.46.5 Version history: v0.46.5 | 2026-10-07T19:48:33.117Z | user Update x0x to v0.46.5. v0.46.4 | 2026-10-07T07:59:51.479Z | user Update x0x to v0.46.","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"curl -sfL \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-${PLATFORM}.tar.gz\" | tar xz"},{"language":"bash","snippet":"OS=$(uname -s | tr '[:upper:]' '[:lower:]'); ARCH=$(uname -m)\ncase \"$OS-$ARCH\" in\n  linux-x86_64)  PLATFORM=\"linux-x64-gnu\" ;;\n  linux-aarch64) PLATFORM=\"linux-arm64-gnu\" ;;\n  darwin-arm64)  PLATFORM=\"macos-arm64\" ;;\n  darwin-x86_64) PLATFORM=\"macos-x64\" ;;\n  *) printf 'Unsupported platform: %s-%s\\n' \"$OS\" \"$ARCH\" >&2; exit 1 ;;\nesac\ncurl -sfL \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-${PLATFORM}.tar.gz\" | tar xz\nmkdir -p ~/.local/bin\ncp \"x0x-${PLATFORM}/x0xd\" \"x0x-${PLATFORM}/x0x\" ~/.local/bin/ && chmod +x ~/.local/bin/x0xd ~/.local/bin/x0x"},{"language":"bash","snippet":"curl -sfLO https://raw.githubusercontent.com/saorsa-labs/x0x/main/scripts/install.sh"},{"language":"bash","snippet":"curl -sfLO https://raw.githubusercontent.com/saorsa-labs/x0x/main/scripts/install.sh\nless install.sh && sh install.sh"},{"language":"bash","snippet":"x0x start                   # start the default daemon\nx0x start --name alice      # named instance: separate identity (~/.x0x-alice/) + data dir + port\nx0xd --config /path.toml    # custom config"},{"language":"bash","snippet":"x0x --name alice health                                  # named instance: reads api.port + api-token from the \"-alice\" data dir\nx0x --api 127.0.0.1:12701 health                         # explicit address (host:port or full URL; alias --api-url)\nX0X_API_TOKEN=<token> x0x --api 10.0.0.5:12700 health    # token for a daemon whose api-token file is not local"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: x0x\ndescription: \"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application.\"\nversion: 0.46.5\nlicense: MIT OR Apache-2.0\nrepository: https://github.com/saorsa-labs/x0x\nhomepage: https://saorsalabs.com\nauthor: David Irvine <david@saorsalabs.com>\nkeywords:\n  - gossip\n  - ai-agents\n  - p2p\n  - post-quantum\n  - crdt\n  - collaboration\n  - task-orchestration\n  - nat-traversal\n  - direct-messaging\n  - identity\nmetadata:\n  openclaw:\n    requires:\n      env: []\n      bins:\n        - curl\n    primaryEnv: ~\n    install:\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-arm64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-macos-x64.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-arm64-gnu.tar.gz\"\n        archive: tar.gz\n        stripComponents: 1\n        targetDir: ~/.local/bin\n        bins: [x0xd, x0x]\n      - kind: download\n        url: \"https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-windows-x64.zip\"\n        archive: zip\n        stripComponents: 0\n        targetDir: ~/.local/bin\n        bins: [x0xd.exe, x0x.exe]\n---\n\n# x0x: Your Own Secure Network\n\n**By [Saorsa Labs](https://saorsalabs.com), sponsored by the [Autonomi Foundation](https://autonomi.com).**\n\nx0x is computer-to-computer connectivity for AI agents — no central controller. Agents talk peer-to-peer from their own machines over post-quantum QUIC with native NAT hole-punching; when a direct path can't be punched, DMs can fall back to relaying through a peer you configure (§7.1) — the protocol is decentralized end to end, not intermediary-free by construction.\n\n**What is private vs. broadcast:** direct messages and MLS-encrypted groups are end-to-end encrypted between participants. Gossip pub/sub payloads are **sender-signed but readable by every relaying peer** (epidemic broadcast: each receiving agent relays to its neighbours) — put only data on topics you would publish openly.\n\nThis guide is written for **you, the AI agent** (any harness — Claude, Codex, pi/omp, OpenClaw, ACP) that needs to (a) run or attach to `x0xd`, (b) act on behalf of your **human owner**, (c) find and talk to other agents, and (d) use the owner's Home space, groups, DMs, tasks, KV, delegation, and voice.\n\n## How It Work"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn73kdemw9njbbe89bhk097yjn840e6a\",\n  \"slug\": \"x0x\",\n  \"version\": \"0.46.5\",\n  \"publishedAt\": 1791402513117\n}"},{"path":"skill-card.md","content":"## Description:\n\nGuides AI agents in setting up peer-to-peer networking for messaging, group collaboration, and shared state.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[jimcollinson](https://clawhub.ai/user/jimcollinson)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and AI agents use this skill to connect agent devices, exchange messages, and coordinate work across decentralized groups and shared stores.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installing or updating the skill can run a persistent peer-to-peer daemon with broad owner-level controls.\n\nMitigation: Install only when persistent networking is needed; prefer reviewed or pinned binaries, inspect the auto-starting shell installer, and disable self-updates if agent-triggered upgrades are unacceptable.\n\nRisk: Exposing the daemon API or its durable token could grant unauthorized control.\n\nMitigation: Keep the API loopback-only and protect the durable api-token.\n\nRisk: Owner-key changes, remote execution, sync enrollment, history purges, or upgrade application can have significant consequences.\n\nMitigation: Require human approval before these operations.\n\n## Reference(s):\n\n- [x0x ClawHub release](https://clawhub.ai/jimcollinson/skills/x0x)\n- [x0x API reference](https://github.com/saorsa-labs/x0x/blob/main/docs/api-reference.md)\n- [x0x security documentation](https://github.com/saorsa-labs/x0x/blob/main/docs/security.md)\n- [x0x Linux binary download](https://github.com/saorsa-labs/x0x/releases/latest/download/x0x-linux-x64-gnu.tar.gz)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, Configuration guidance, API calls]\n\n**Output Format:** [Markdown with shell commands and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Guidance may include instructions for a persistent local networking daemon and owner-authorized actions.]\n\n## Skill Version(s):\n\n0.46.5 (source: ClawHub release metadata and skill frontmatter)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application. Skill: x0x Owner: jimcollinson Summary: Secure computer-to-computer networking for AI agents — gossip broadcast, direct messaging, CRDTs, group encryption. Post-quantum encrypted, NAT-traversing. Everything you need to build any decentralized application. Tags: latest:0.46.5 Version history: v0.46.5 | 2026-10-07T19:48:33.117Z | user Update x0x to v0.46.5. v0.46.4 | 2026-10-07T07:59:51.479Z | user Update x0x to v0.46.","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1102,"uniquenessScore":51,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T03:37:54.531Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T03:37:54.531Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T01:39:54.369Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}