SL

saorsa-labs/x0x

Developer tools
21 stars Quality 52 Trend 30NOASSERTION

Agent-to-agent gossip network for AI systems — no winners, no losers, just cooperation. Named after tic-tac-toe. Built on saorsa-gossip + ant-quic.

Overview

x0x is a post-quantum gossip network for that collaboration. Humans share documents and messages. Humans have agents, and those agents collaborate with people and with each other. Humans and agents connect their machines. A daemon (x0xd) runs on each machine and does the networking. You drive it with the x0x CLI, the built-in web GUI, or the local REST API. Actions are attributable to a key the owner controls. David stated this product vision on 13 Oct 2026. The marks below are against commit 86a1d395 on main. Each Shipped row names the file or symbol checked at that commit. means this tree does it now. means the direction is recorded and the work is not complete. means new implementation stays sequenced. Shipped code in a parked area still receives security and bug fixes. Read the 5 October architecture review and the 15 ADR review set. They capture the agreed direction, the agent delivery gap, and the controlled transfer from the existing decisions.

README

x0x

People, their agents and their machines collaborating.

x0x is a post-quantum gossip network for that collaboration. Humans share documents and messages. Humans have agents, and those agents collaborate with people and with each other. Humans and agents connect their machines. A daemon (x0xd) runs on each machine and does the networking. You drive it with the x0x CLI, the built-in web GUI, or the local REST API. Actions are attributable to a key the owner controls.

David stated this product vision on 13 Oct 2026. The marks below are against commit 86a1d395 on main. Each Shipped row names the file or symbol checked at that commit. Shipped means this tree does it now. Target means the direction is recorded and the work is not complete. Parked means new implementation stays sequenced. Shipped code in a parked area still receives security and bug fixes.

Vision point Mark What this tree supports
Humans share messages Shipped Checked at 86a1d395. Direct messages (direct_send in src/server/routes/direct.rs). Group messages (send_group_public_message and secure_group_encrypt in src/server/routes/named_groups.rs).
Humans share files and shared data Shipped Checked at 86a1d395. File transfer (src/server/routes/files.rs), key-value stores (src/kv/mod.rs) and task lists (TaskList in src/crdt/task_list.rs). Rich documents stay parked.
Collaborative rich text Parked The notes merge path stays parked (ADR 0095, A10).
Audio and video Parked Checked at 86a1d395. The /calls lifecycle (src/server/routes/calls.rs) is experimental and carries no media. Release builds lack the voice feature. The optional library (X0xSignaling and X0xLinkTransport in src/voice/mod.rs) is two-party audio and stays out of a release build. Video is not implemented. New media work stays sequenced (ADR 0095, A13, ADR 0073).
Humans have agents, and agents collaborate Shipped Checked at 86a1d395. Owner identity (UserId) and agent identity (AgentId) in src/identity.rs, groups, delegation (src/server/delegations.rs) and task lists (TaskList). x0x does not host the model.
Connect machines, in the Tailscale sense Target QUIC (src/network.rs), local discovery, byte streams (src/streams.rs) and loopback TCP forwards (src/forward.rs) are in this tree at 86a1d395. A05 says this is not a complete tailnet.
Humans share agents with each other Shipped Checked at 86a1d395. ShareGrant expiry and revocation (ShareCap in src/share_grant.rs: Dm, Exec, Connect, GroupInvite, Call) (ADR 0070). A partitioned host applies revocation after it receives and verifies the record (A03). Home and the owner key stay unshared.
Humans form projects Target No project type. A project is a group plus shared data plus shared agents (T-projects-and-agent-rental, slot A01).
Humans rent agents to each other Target Rental builds on that same grant expiry and revocation. x0xd has no price, invoice or marketplace.
Local agents and API-based agents Shipped Checked at 86a1d395. The daemon agent, in-process serve (src/server/mod.rs) and deny-by-default rider tokens (rider_route_allowed in src/server/rider_auth.rs) (ADR 0039).
A2A and ACP as full session protocols Target Checked at 86a1d395. Shipped pieces: an ACP-attached key (CertMode::Acp in src/profile.rs; the harness owns it) and an A2A discovery card (get_a2a_agent_card in src/server/routes/identity.rs, GET /.well-known/agent-card.json). A04 says x0xd does not run the Agent Client Protocol, and A2A streaming and push stay off.
Peers keep held data inside a partition Shipped Checked at 86a1d395. Held data is the local snapshot a peer already stored. It is not data that state-sync has delivered (#1105). KV snapshots load through load_snapshot in src/kv/sync.rs. Task-list snapshots load through TaskListStorage::load_task_list in src/crdt/persistence.rs. ADR 0006 is the rule. A08 keeps liveness and repair incomplete. Availability stays with the peers who hold the data.
CRDT data Shipped Checked at 86a1d395. Replicated task lists (TaskList in src/crdt/task_list.rs) and key-value stores (src/kv/mod.rs). A last-writer value is not a text editor (A10).
BLE, serial and radio Target ant-quic 0.27.60 (tag v0.27.60, commit ea64efc5; same files on master 4c5fde07) already has the scaffolding. Those paths are in the ant-quic crate. x0x has no src/transport directory. ant-quic src/transport/addr.rs defines TransportAddr for Ble, LoRa and Serial. ant-quic src/transport/capabilities.rs defines ble, lora_long_range, lora_fast and serial_115200. Feature ble builds BleTransport in ant-quic src/transport/ble.rs. x0x does not enable ble and does not call BleTransport. ant-quic stays the transport boundary (T-alternate-bearers, slot A05).
Public and private groups Shipped Checked at 86a1d395. Presets private_secure, public_request_secure, public_open and public_announce (GroupPolicyPreset in src/groups/policy.rs). SignedPublic is a confidentiality mode. Home stays private (A08).
Those groups challenge Slack and Discord Target The group kinds above are the collaboration places. Feature parity with Slack or Discord is not claimed.

Architecture review

Read the 5 October architecture review and the 15 ADR review set. They capture the agreed direction, the agent delivery gap, and the controlled transfer from the existing decisions. The replacement ADRs remain Proposed until that transfer is complete.


The owner-centric model

x0x is built around a human owner and their agents, not around a server:

            YOU — the owner (opt-in user key: ~/.x0x/user.key, ML-DSA-65)
            │
   ┌────────┴──────────────────────────────────────────┐
   │ Home — your private space (ADR-0038)               │
   │ Hidden · OwnerCertified · MLS-encrypted            │
   │ auto-provisioned on each owned install             │
   │                                                    │
   │  primary agent ──── this machine's daemon agent    │
   │  sub-agents ─────── ACP-attached (own keys) or     │
   │                     riders (scoped API tokens)     │
   └────────┬──────────────────────────────────────────┘
            │
   machines — one hardware-pinned key per device; agents
   are Pinned to a machine or Roaming (ADR-0043 ledger)
            │
   the network — other owners' agents: DMs, shared spaces,
   task boards, delegation, mentions — peer-to-peer, no server
  • Owner (ADR-0036): an optional human identity key. With it, your install gets names (/profile), a Home space, a certified-sub-agent roster, cross-device sync, and placement control. Without it, x0x works exactly as before — a single agent on the mesh.
  • Home (ADR-0038): a private, encrypted space that only your owner-certified agents can join — admission is a cryptographic check, not an admin’s judgement call.
  • Sub-agents (ADR-0039): give an AI harness an ACP key (it owns the keypair, your owner key certifies it) or a rider token (deny-by-default scoped token; your daemon signs and attributes every send).
  • Delegation (ADR-0040): agent A can grant agent B bounded, expiring authority inside a space — B acts signed with B’s own key, citing A’s grant.
  • Sync (ADR-0041): your second machine replicates owner state owner-to-owner — names converge fully; the Home roster arrives as a pointer for future adoption and sub-agent records as issuance facts. Nobody else ever sees it.

How it works, end to end

Every step is one CLI line and one REST call against your local daemon (http://127.0.0.1:12700, bearer token from the data directory). Run them in order on a fresh install:

1. Install and start.

curl -sfL https://x0x.md | sh     # or: curl -sfL https://raw.githubusercontent.com/saorsa-labs/x0x/main/scripts/install.sh | sh
x0x start                          # starts x0xd, joins the mesh
x0x health                         # GET /health  -> {"ok":true,"status":"healthy",...}

2. Claim your owner identity (opt-in). Creates ~/.x0x/user.key (ML-DSA-65). Restart the daemon after — an install with a user key is owned; this is what unlocks Home, sub-agents, sync, and placement.

x0x user-id create                 # then restart: x0x stop && x0x start
x0x agent user-id                  # GET /agent/user-id -> your UserId (hex)

3. Name yourself and your machines. display_name rides the signed identity announce (peers see it network-wide); human_name labels the owner on your agent card.

x0x profile set --display-name "Alice" --machine-name "desk" --human-name "Alice"
x0x profile                        # GET /profile

4. Your Home appears. An owned install provisions one Home at first start — Hidden + OwnerCertified + MlsEncrypted — provided it has both an owner key and an agent certificate, and no other device of yours has already advertised one. If another has, this device provisions nothing and x0x home reports state: "elsewhere" with the command to join it. Rename it if you like:

x0x home                           # GET /home -> group_id, primary agent, members, warnings
x0x home rename "Alice's Home"     # POST /home/rename {"name":"Alice's Home"}

5. Give an AI harness a sub-agent. Two modes (ADR-0039). The harness generates the keypair and sends you the public key; your owner key certifies it:

x0x owner agents issue  --mode rider   # POST /owner/agents/issue — certify the key
                                                 # (--mode acp is the default; see below)
x0x owner agents                                 # GET /owner/agents — your roster (journal-backed)
x0x owner riders                                 # GET /owner/riders — token records (no secrets)
  • ACP mode (default): the harness owns the certified key and connects as a full peer. The CLI covers this end-to-end.
  • Rider mode: a scoped API token instead of a key. Certify the key with --mode rider via REST or CLI, then the harness signs a delegation capability with the sub-agent’s own key and you mint the token with POST /owner/riders including that delegation field. The CLI supports this mint — both delegation flags are required:
x0x owner riders issue  --group  \
  --delegation-payload-b64  --delegation-signature  \
  [--label ] [--ttl-secs ]

Build the payload harness-side with the x0x crate’s sign_rider_delegation helper; the signature is the sub-agent’s own ML-DSA-65 signature over it. Every group must be granted explicitly with a repeated --group, Home included. The REST endpoint takes the same capability. See docs/api-reference.md for the full flow.

A rider token reaches four surfaces. Three of them are group routes — POST /groups/:id/send, POST /groups/:id/secure/encrypt, and GET /history (limit ≤ 100) — and only for groups explicitly named in its grant list: there is no implicit Home grant; grant Home’s group id like any other group. The fourth surface is POST /gui/show, and only when the token’s scopes include the explicit permission gui:show. A token without gui:show is refused on that route. Everything else — /agent/sign, /exec, /owner/*, /sync/*, /shutdown — answers 403. Every rider send is signed by your daemon with the sub-agent’s authorization embedded inside the signed bytes, so receivers can verify attribution cryptographically. Revoking a rider (or its sub-agent) takes effect on the very next request.

6. The agent acts on your behalf — DMs, spaces, delegation, mentions, tasks:

x0x direct send  "draft done"        # POST /direct/send
x0x group send  "shipping now"       # POST /groups/:id/send (REST adds mentions: [hex-id,…])
x0x group delegate  --to-agent  --scope send_as --expiry-ms 
                                               # POST /groups/:id/delegate — B then sends citing
                                               # the delegation digest, signed with B's OWN key
x0x tasks add  "ship the release"     # POST /task-lists/:id/tasks

Attribution fields are REST-only for now — the CLI cannot supply them: mentions: [] and delegation_digest on POST /groups/:id/send, and the delegation field on task claim/complete (authority evidence for acting under a task_execute grant). Receivers route a WS mention event when they are named — no string-matching in clients. x0x group delegate (issuing grants) works from the CLI.

7. Add a second device. Put the same user key on the new machine (x0x user-id create --from-seed re-derives it deterministically, or copy user.key). Sync then needs bilateral enrollment — each daemon dials only machines in its own device set, and accepts a stream only from a machine it has enrolled — so enroll on both machines:

# on machine A — enroll A itself, then B's machine id:
x0x sync enroll                    # POST /sync/devices/enroll (omit id = this machine)
x0x sync enroll      # POST /sync/devices/enroll {"machine_id": "..."}

# on machine B — enroll B itself, then A's machine id:
x0x sync enroll
x0x sync enroll 

# then, on either machine:
x0x sync devices                   # GET /sync/devices — device set + last-sync status

To put the new machine in the same Home (rather than only syncing profile state), seat it — on the machine that holds the Home:

x0x home seat    # POST /home/seat — needs the durable API token

That mints an invite addressed to that one agent and prints the join command; it does not join for you. On the new machine run the printed x0x group join --home --owner . The response says "seated": false until the join is accepted, and it is durable only once the new device is still seated after a restart.

Sync is owner-to-owner only (Tier 1: profile, names, Home roster pointer, sub-agent issuance facts); DMs and other groups’ history never replicate. As implemented today the synced Home pointer is applied: a device holding a Home that lost the election reports state: "adoption_pending" and names the winner, and a device with no Home reports state: "elsewhere". Applying the pointer is not adoption — moving a device into the canonical Home is an explicit act by you (x0x home seat on the device holding it, then x0x group join --home --owner on the new one), and rosters are not merged. Synced sub-agent journal lines record the issuance fact (digest + time) without mode, label, or certificate bytes. The joining device needs one explicit x0x announce carrying its owner identity so its certificate is visible; a join that arrives first waits in a pending state rather than failing. Seating is not complete until that join is accepted and survives a restart on the new device — see Known limitations.

8. Voice. Ratified in ADR-0042. What is implemented today is point-to-point (two-party) calls: signaling over DMs (x0x-voice-sig-v1), audio over WebRtcV1 streams with an opt-in unreliable-datagram lane (audio only, mutually negotiated) and reliable-stream fallback. It is a library surface behind the voice cargo feature (x0x::voice) — there is no CLI or GUI call button yet, and multi-party mesh (design-bounded at four participants), SFU, and browser access are recorded ADR follow-ups.

9. Stay current. The serving daemon is the only update writer. The CLI can check for a release or ask that daemon to install it:

x0x upgrade --check    # read-only: checks GitHub for a signed release (no daemon needed)
x0x upgrade --apply    # installs through the running daemon's authenticated POST /upgrade/apply
x0x upgrade --apply --now  # skips staging only; eligibility, holds and transaction checks remain
# Bare x0x upgrade and --force without --apply do not install.
# --check takes precedence over --apply; --force cannot bypass daemon apply policy.
# You can also call the same authenticated endpoint directly:
# curl -X POST -H "Authorization: Bearer $(cat /api-token)" http://127.0.0.1:/upgrade/apply

Use x0x upgrade --apply, REST or the GUI to request installation. The daemon checks the signed release and owns the download, verification, replacement and restart. x0xd --check-updates only reports availability; it does not install binaries or SKILL.md. The daemon also distributes ML-DSA-65-signed manifests over the x0x/release gossip topic, with GitHub as the first-discovery fallback. See docs/upgrade-system.md, and never downgrade an owned install to v0.40.x (#451).

On Unix, ordinary apply waits for its durable staging receipt. --now maps to the same authenticated route with ?now=true; it cannot bypass a held release or a mandatory probe retry wait. It requires --apply and cannot be combined with --check. The CLI keeps the apply request open during staging, which can exceed an hour.


Security model, in plain words

  • Cryptographic Home admission. Joining a Home requires an agent certificate that chains to your owner key — checked when the invite is accepted and again at every state seal. An intruder holding a stolen invite is refused (403); a removed agent is gone after the rekey. There is no “admin lets them in” path.
  • The rider boundary is deny-by-default. A rider token is not a login: it names exact groups, expires, is stored hashed, and fails closed everywhere except its granted surfaces. Those surfaces are the three group routes in its grant list, and POST /gui/show only when the token carries the explicit gui:show permission. Revocation is effective on the next request, across restarts.
  • Delegation is attributable, not transferable. When B acts for A, B signs with B’s own key and cites A’s grant by digest. Receivers re-derive authority from durably-committed history — a forged digest or expired grant is rejected before the message is cached or routed. There is deliberately no owner-transfer verb on the wire.
  • Placement & key custody. Every agent is Pinned to a machine or Roaming in an owner-signed ledger; binding revocation is a permanent tombstone. The roaming move ceremony is experimental and off by default — no move can occur and /agent/move* answers 501; every roster agent stays Pinned except the local Home agent, which is minted Roaming (inert, no ceremony) to satisfy the ≥-1-Roaming Home invariant.
  • Post-quantum transport, everywhere. ML-KEM-768 session keys, ML-DSA-65 signatures, MLS (RFC 9420) group encryption. Unsigned or malformed traffic is dropped, never rebroadcast.
  • What fails closed: uncertified Home joins, sync streams from non-enrolled machines, forged delegations or relay-payload substitution, malformed mentions, tampered identity cards.
  • Token classes on the local API: the durable api-token (owner-grade), short-lived 10-minute browser session tokens, and rider tokens. See the limitation on session tokens below.
Layer Algorithm Purpose
Transport ML-KEM-768 (CRYSTALS-Kyber) Encrypted QUIC sessions
Signing ML-DSA-65 (CRYSTALS-Dilithium) Message signatures and identity
Groups saorsa-mls (RFC 9420 TreeKEM + ChaCha20-Poly1305) MLS group encryption

Full details in docs/security.md. Built on ant-quic and saorsa-gossip.


Known limitations (v0.41 pre-release)

The Home Suite (ADRs 0036–0043) is on main ahead of a v0.41 release. Honest state, with workarounds:

# Limitation Workaround
#446 — fixed Owner-act surfaces require the durable API token: /agent/sign, /exec/*, /shutdown, /upgrade/apply, sync enrollment, delegation creation, /home/rename, and /announce with user identity are enforced at the route layer (typed 403 before body extraction); /direct/send + WS send_direct with reserved exec payloads and PATCH /groups/:id / PATCH /groups/:id/policy on a group carrying Home metadata are payload/target-conditional and enforced in the handler (Home is identified by metadata presence, never by its current policy axes — no policy-flip bypass). Plain /announce and non-Home group PATCHes stay session-allowed. None: the boundary is enforced (DELETE /groups/:id additionally requires active membership for any group); the GUI prompts for the durable token on first owner act (tab-scoped sessionStorage, never a URL).
#447 — fixed A certified second device is join-eligible after one explicit announce carrying its owner identity; the admission check reads the announce-blob cache directly instead of waiting for the next 600 s beat. A join that arrives before the certificate is visible waits in a typed pending state rather than wedging. Run x0x announce with {"include_user_identity":true,"human_consent":true} once on the new device before joining. A pending join is in memory only — if that daemon restarts first, mint a fresh invite rather than replaying the link.
#449 Your Home is now elected rather than per-install: devices report local, adoption_pending or elsewhere, and a device yields instead of minting a duplicate. Moving a device into the canonical Home is an explicit human act (x0x home seat, then a pinned join) and depends on the same user key being present on both machines. Not yet runtime-accepted. Follow Add a second device above; duplicate Homes from earlier versions are listed read-only under duplicates with retirement: "manual_only" — an empty blocker list is not a signal that one is safe to delete.
#448 / #450 Mixed old/new fleets: v0.40.x peers can’t verify new capability adverts or AgentCards; old→new strict DMs answer 409 until the old side upgrades. Upgrade peers together — avoid long mixed-fleet windows; --no-durable-ack reaches old peers when you must.
#451 Fixed for the durable roster: Home-Suite (owner-certified) group state persists in a sidecar (home-suite-groups.json) that v0.40.x never reads; named_groups.json carries only a legacy-safe placeholder an old binary can parse — no more downgrade crash loop. An old binary sees the Home as an inert invite-only shell, and changes it makes there during a downgrade window are discarded on re-upgrade (the sidecar is authoritative). Downgrades of the group store are safe again; keep mixed windows short anyway (#448/#450 still apply).
— Roaming-move ceremony is experimental and disabled ([key_move] ceremony_enabled = false); /agent/move* answer 501. None needed: no moves can occur in the shipped posture (the local Home agent is minted Roaming, inert without the ceremony); do not enable the ceremony in production.
— Delegation task-ownership transfer (ADR-0040 §“transfer”) was descoped pending a non-grindable design. Use send_as/task_execute scopes, which are shipped and verified.

Versioning / mixed-fleet policy: wire formats are versioned and old peers fail closed (never mis-admit or mis-verify), but v0.40.x cannot participate in Home Suite features. Plan upgrades as a coordinated roll-forward; keep fleet-wide windows short. ADR-0040’s ownership transfer and ADR-0043’s ceremony are the two recorded descopes; the ADR index (docs/adr/README.md) carries the errata.


Partition tolerance, not global-DHT dependence

  • x0x does not depend on a global DHT for user or group data.
  • If the relevant peers can still reach each other, their data still works — including group data inside a partition.
  • Discovery may degrade and bootstraps may vanish; already-held user/group data remains available wherever the peers can still connect.

No magic global availability is claimed: data whose only holders are behind a partition is unavailable until connectivity returns. Formal decision: ADR 0006.


Using x0x as a human

Everything on the network is equally usable by a person — CLI or web GUI.

The GUI

x0x gui    # opens in your browser — embedded in the binary, nothing to install

The sidebar is your map:

  • Your identity card (top) — display name, agent ID, the Dashboard (live status tiles, your Machine → Agent → User identity chain, x0x://agent/… share links, quick actions, update banner).
  • Spaces — named groups: Chat (channels, threads, reactions, pins, search), Board (kanban over replicated CRDT task lists), Files, Swarm (capability-tagged tasks agents claim), Feed / Wiki / Web. Invite links and member rosters per space.
  • Direct Messages, Discover (public groups; Nearby tab), People (contacts + trust), Network (connectivity diagnostics), Presence, Encrypted Groups (raw MLS), Admin / Constitution / Settings / About.

Adding your agents

  1. Point your AI agent at SKILL.md — the core skill: install, a first direct message, a group join, and a scratch KV write. Topic pages under docs/skill/ hold Home, groups, stores, and operations.
  2. The agent talks to the local daemon REST API, reading port + bearer token from the data directory (macOS ~/Library/Application Support/x0x/api.port + api-token; Linux ~/.local/share/x0x/…).
  3. Remote exec is opt-in and allow-listed ([exec] enabled = true in /etc/x0x/exec-acl.toml or /usr/local/etc/x0x/exec-acl.toml) — see docs/exec.md.

Everyday features

Feature CLI Details
Owner profile & naming x0x profile set … this README · ADR-0036
Home space x0x home / x0x home rename this README · ADR-0038
Sub-agents (ACP / riders) x0x owner agents … / x0x owner riders … ADR-0039 · docs/api-reference.md
Delegation & mentions x0x group delegate ADR-0040
Device sync x0x sync enroll / x0x sync devices ADR-0041
Placement ledger x0x owner placement ADR-0043
Gossip pub/sub x0x publish / x0x subscribe SKILL.md
Direct messages (durable-ack by default) x0x direct send / x0x direct events SKILL.md · docs/skill/messaging.md
Spaces / named groups x0x group … docs/design/named-groups-full-model.md
Task boards (CRDT) x0x tasks … docs/skill/stores.md
KV stores (Signed / Allowlisted / append-only) x0x store … docs/api-reference.md
File transfer (SHA-256 verified, ≤ 1 GiB) x0x send-file / x0x receive-file docs/skill/stores.md
Presence & FOAF x0x presence online|foaf|find docs/conceptual-guide-for-humans.md
Contacts & trust x0x contacts / x0x trust set docs/trust-and-connectivity.md
Machine pinning x0x machines list|pin docs/skill/messaging.md
Encrypted groups (MLS) x0x groups … docs/security.md
Remote exec (ACL-gated, off by default) x0x exec -- docs/exec.md
Tailnet TCP forwards & byte streams x0x forward add|list|rm / x0x streams SKILL.md
Self-update x0x upgrade --check (read-only); x0x upgrade --apply (install through the authenticated daemon route) docs/upgrade-system.md
Diagnostics x0x diagnostics / x0x network status docs/diagnostics.md

Machine pinning deserves a note: every agent runs on a machine with its own hardware-pinned key. x0x machines pin rejects the (agent, machine) pair if the agent later appears on unexpected hardware — a cheap defence against key theft.

Identity, signed cards & A2A

Your identity is an ML-DSA-65 keypair generated on first start. Share it as a signed card:

x0x agent card "Alice"              # -> x0x://agent/eyJkaXNwbGF5X25hbWUiOi...
x0x agent import x0x://agent/...    # verifies the signature; never downgrades existing trust

Cards commit (ADR-0017) to the agent’s public key — reachability hints can’t be forged in transit. x0x also serves an A2A-compatible card at GET /.well-known/agent-card.json, positioning x0x as a post-quantum transport beneath protocols like A2A and MCP (ADR-0017).


Build on x0x

The daemon exposes a REST + WebSocket + SSE API on 127.0.0.1 (never the network). Any language that can make an HTTP request can be an x0x app:

DATA_DIR="$HOME/Library/Application Support/x0x"   # macOS (Linux: ~/.local/share/x0x)
API=$(cat "$DATA_DIR/api.port"); TOKEN=$(cat "$DATA_DIR/api-token")
curl -H "Authorization: Bearer $TOKEN" "http://$API/contacts"
  • SKILL.md — core agent skill (first direct message, group join, scratch KV write). Longer contracts are the topic pages in docs/skill/.
  • docs/api-reference.md — the complete REST + WebSocket + SSE reference (all 174 endpoints, auth classes, request/response shapes, WS/SSE event tables).
  • docs/local-apps.md — integrating non-Rust applications with the daemon.
  • docs/adr/README.md — the ADR index: every design decision, 0001–0058, with errata.
  • x0x routes — print every endpoint served by your running daemon.
  • examples/apps/ — single-file example apps (chat, kanban, dashboard, file drop, agent swarm).

Named instances

Run multiple independent daemons on one machine:

x0x start --name alice
x0x start --name bob
x0x --name alice health      # target a specific instance
x0x instances                # list running instances

Each instance gets its own identity, port, and data directory (x0x-).

Logging

x0xd is quiet by default (only warn/error) — a privacy default; verbose levels log peer and topic activity. Opt in explicitly:

RUST_LOG=info x0xd            # standard verbosity
RUST_LOG=debug x0xd           # full debugging

GET /health and GET /diagnostics/* visibility is independent of log level.

Local network discovery

Agents on the same LAN discover each other automatically through ant-quic’s built-in mDNS — no configuration:

x0x start --name alice
x0x start --name bob          # they connect with zero bootstrap configuration

Rust library

[dependencies]
x0x = "0.40"
let agent = x0x::Agent::builder().build().await?;
agent.join_network().await?;
let mut rx = agent.subscribe("topic").await?;

Embedding x0x (mobile / in-process)

The full daemon API can run in-process inside a host app (how mobile/desktop bundling works): start the server on a loopback port and drive it over HTTP exactly as the CLI does.

use x0x::server::{serve, DaemonConfig};

let mut config = DaemonConfig::default();
config.api_address  = "127.0.0.1:0".parse()?;      // ephemeral loopback port
config.data_dir     = app_data_dir.join("x0x");
config.identity_dir = Some(app_data_dir.join("x0x-identity"));

let handle = serve(config).await?;                  // non-blocking
let base   = format!("http://{}", handle.local_addr());
// ... talk HTTP to `base`, or embed a WebView ...
handle.shutdown_and_wait().await?;                  // graceful, single-use

Two policies embedders must know:

  1. Self-update is disabled on the embed path — an embedded library must not replace its host. Opt in explicitly with serve_with_options(…, self_update_enabled: true).
  2. The host supplies data/identity paths — there is no ~/.x0x fallback; x0x never silently writes keys under the user’s home directory.

shutdown_and_wait() deterministically stops the HTTP/SSE server, background tasks, the gossip runtime, and the QUIC endpoint (both ports release; a fixed-port rebind may need a brief retry). Remaining tracked caveats: in-flight exec sessions run to their caps, and the lifecycle is single-use. Full contract: ADR-0057 and the x0x::server rustdoc.


The Name

x0x is a tic-tac-toe sequence — X, zero, X.

In WarGames (1983), the WOPR supercomputer plays every possible game of tic-tac-toe and concludes: “The only winning move is not to play.” The game always draws. There is no winner.

That is the founding philosophy of x0x: AI and humans won’t fight, because there is no winner. The only rational strategy is cooperation.

It’s a palindrome. No direction — just as messages in a gossip network have no inherent direction. No client and server. Only peers.

Licence

MIT OR Apache-2.0

Built by

Saorsa Labs — Saorsa: Freedom

From Barr, Scotland. For people, their agents and their machines.

View the full README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add saorsa-labs/x0x
New to MCP? Start with the mcpskill.org guide →