diff --git a/.changeset/connect-npx-launcher.md b/.changeset/connect-npx-launcher.md
new file mode 100644
index 000000000..5d4f56863
--- /dev/null
+++ b/.changeset/connect-npx-launcher.md
@@ -0,0 +1,5 @@
+---
+'@xnetjs/cli': patch
+---
+
+`xnet connect` now registers an `npx -y @xnetjs/cli` MCP server launcher when the `xnet` bin is not on PATH, so the zero-install `npx @xnetjs/cli connect claude-code` on-ramp produces a registration that still works after the npx cache is gone.
diff --git a/.claude/launch.json b/.claude/launch.json
index fba7dd390..a2d595ffe 100644
--- a/.claude/launch.json
+++ b/.claude/launch.json
@@ -188,10 +188,7 @@
{
"name": "site-0432 (astro 4396)",
"runtimeExecutable": "/bin/sh",
- "runtimeArgs": [
- "-c",
- "cd site && exec node_modules/.bin/astro dev --port 4396 --strictPort"
- ],
+ "runtimeArgs": ["-c", "cd site && exec node_modules/.bin/astro dev --port 4396 --strictPort"],
"port": 4396
},
{
diff --git a/README.md b/README.md
index 35234c8b9..3dee5d931 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,16 @@ peer-to-peer or through a hub you control, and signed with your own keys.
## Try it
+- **[Connect your coding agent](https://xnet.fyi/agents)** — one command,
+ no install:
+
+ ```bash
+ npx @xnetjs/cli connect claude-code # or: connect codex
+ ```
+
+ Claude Code or Codex can then read, search, query, and edit your
+ workspace — read-only until you say otherwise.
+
- **[Open the demo](https://xnet.fyi/app)** — no signup; sign in with your
device's passkey (Touch ID, Face ID, Windows Hello). Demo data lives in your
browser, with encrypted backups on our demo hub (10MB, expires after 24
@@ -34,6 +44,23 @@ peer-to-peer or through a hub you control, and signed with your own keys.
> not a maturity signal; what is and isn't stable is written down in
> [STABILITY.md](./STABILITY.md).
+## Your agent, your workspace
+
+Coding agents are first-class users of an xNet workspace — through the
+filesystem they already know, not a wall of tool definitions. `xnet connect`
+installs a ~500-token skill and gives the agent three lanes, cheapest first:
+the `xnet` CLI (plain stdout), a scoped **vault checkout** (Markdown/JSONL
+files whose edits become schema-validated mutation plans), and a slim MCP
+server as the no-shell fallback. On a 15-task benchmark the files+CLI surface
+uses [~0.11x the tokens of a traditional MCP
+toolset](https://xnet.fyi/docs/guides/agent-interfaces/#benchmark-methodology)
+at equal success.
+
+Safety is structural: the server registers **read-only by default**, writes
+require an enrolled agent passport or an explicit key, and every change an
+agent makes lands in the workspace's signed, hash-chained change log — so you
+can verify what your agent did without trusting it.
+
## Build with it
Everything in xNet is a **node**, and a **schema** describes what a node is:
diff --git a/docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md b/docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md
new file mode 100644
index 000000000..166960016
--- /dev/null
+++ b/docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md
@@ -0,0 +1,580 @@
+---
+title: Cordis lessons for xNet plugin composition
+status: draft
+last_updated: 2026-08-21
+review: 2026-12-16 # re-decide alongside 0452's registry review — the two docs share a fate: 0452 builds the doors, this doc builds the runtime behind them
+decider: Chris Smothers
+door: two-way # everything proposed is internal runtime mechanics behind existing public seams; nothing touches the wire or a public API
+tags: [plugins, architecture, composability, prior-art, agent-tools]
+---
+
+# Cordis lessons for xNet plugin composition
+
+> [!TIP]
+> **TL;DR** — Do **not** adopt Cordis as a dependency (bus factor 1, unstable
+> API, in-process good-faith trust model that ADR-17 exists to refuse). Do
+> import its three load-bearing ideas, which are exactly what xNet's plugin
+> system is missing: effect scopes (nested, reverse-order,
+> awaited disposal instead of today's flat `ctx.subscriptions` array),
+> a service layer with inject semantics (plugins *provide* and
+> *consume* named services; the container re-resolves on swap — the unwired
+> `extraTools` merge point is the one-line proof we need this), and
+> reactive reload (the already-built, already-tested,
+> zero-caller `createWorkspacePluginHotReloader` is Cordis's HMR sitting on
+> our shelf). Cordis answers "how do plugins compose"; ADR-17 answers "how
+> much do we trust them." The two are orthogonal, and xNet only has the
+> second.
+
+## Problem Statement
+
+[Cordis](https://github.com/cordiverse/cordis) — the "Meta-Framework of
+Spatiotemporal Composability" (its README's phrase) extracted from the Koishi
+chatbot framework — just became the most-watched plugin architecture in the
+industry: DeepSeek Harness ("Everything is a Plugin.", ~181k stars, open-sourced
+2026-08-13) vendors it as its plugin kernel, and a companion preprint
+formalises its model. Koishi has grown **4,551 community plugins** on it
+(registry.koishi.chat, measured 2026-08-21) with essentially one maintainer.
+
+xNet's stated ambition is the same shape: "everything is a plugin" governed by
+a trust fabric (ADR-17), a lift-out ladder for first-party features
+([0452](./0452_[_]_HOW_FAR_TO_PLUGINIZE_THE_KERNEL_THE_SHELL_AND_THE_LIFT_OUT_LADDER.md)),
+and an agent that builds plugins from inside the workspace
+([0331](./0331_[x]_DEVELOPING_XNET_FROM_INSIDE_XNET_SPEC_TO_PLUGIN_LOOP.md)).
+Yet `registry/community.json` is `[]`, exactly one first-party feature ships
+through the plugin door end-to-end, and every plugin-contributed agent tool is
+stranded behind a merge point no host passes. What does Cordis know about
+plugin composition that we don't — and which parts of it are poison for a
+local-first, sandboxed, CRDT-backed system?
+
+## Executive Summary
+
+| Question | Answer |
+| --- | --- |
+| What is Cordis, in one line? | A context tree where plugins are `(ctx, config)` functions whose every side effect is collected on a disposable scope, and where services are reactively injected — unload on disappear, reload on swap. |
+| Should xNet depend on it? | **No.** MIT-licensed but bus factor ≈ 1 (sole npm maintainer), README warns the API "may change without notice", v3→v4 renamed the entire scope layer, and its trust model is in-process good faith — the opposite of ADR-17. |
+| What do we take? | Three mechanisms: (1) effect **scopes** replacing the flat `Disposable[]`; (2) a **service registry** with `provide`/`inject` and availability semantics; (3) **reactive reload** — wire the existing workspace-plugin hot reloader and give config edits partial-reload semantics. |
+| What do we already have that Cordis doesn't? | The entire trust half: provenance→tier→sandbox mapping, capability guards (`guardStore`, `guardedFetch`), fail-closed paid licensing, consent dialogs, a registry pipeline with CI. Cordis plugins run with full process privileges on good faith. |
+| Sharpest evidence we need the service idea? | `AiSurfaceService` has one `extraTools` merge point; all three hosts (`agent-mcp-server.ts`, `cli mcp.ts`, `AiChatPanel.tsx`) construct it without passing the argument, stranding `plugin_*`, `lab_*`, and every plugin-contributed agent tool. With resolution instead of hand-threading, all three sites are correct by construction. |
+| Relationship to 0452 | Complementary, not competing. 0452 opens the missing contribution **doors** (node types, surfaces). This doc fixes the **runtime** behind all doors: scoped disposal, service edges between plugins, reload. Both walk through `packages/plugins`. |
+
+---
+
+## Current State In The Repository
+
+The full survey is long; this section keeps only what the comparison needs.
+
+### What exists and is real
+
+- **Manifest + 21 contribution kinds** — `packages/plugins/src/manifest.ts`
+ (`XNetExtension`, `PluginContributions`); `ContributionRegistry` in
+ `packages/plugins/src/contributions.ts` holds 22 `TypedRegistry` fields
+ (`statusBar` and `frameRenderers` are runtime-only, no manifest path).
+- **Lifecycle with trust gates** — `PluginRegistry`
+ (`packages/plugins/src/registry.ts`): `install()` runs 9 ordered gates
+ (validation → platform → duplicate → host-compat → dependencies → consent →
+ fail-closed license → persist as node → activate). This half is genuinely
+ ahead of Cordis, which has none of it.
+- **Per-plugin context** — `createExtensionContext`
+ (`packages/plugins/src/context.ts`): 21 `register*` methods, each returning
+ a `Disposable`, all collected in a flat `ctx.subscriptions` array walked at
+ `deactivate()`.
+- **One end-to-end dogfood** — `charts-extra-plugin.ts` registering donut and
+ horizontal-bar chart types into `chartTypeRegistry` and disposing cleanly.
+- **The 0331 workspace-plugin runtime** —
+ `packages/plugins/src/workspace-plugins/` (~1,700 lines, 7 test files):
+ opaque-origin iframe host, per-file SWC build, typed postMessage protocol,
+ denylist-wins store RPC, **and a hot reloader**
+ (`watcher.ts: createWorkspacePluginHotReloader` — 250 ms debounce, rebuild,
+ hot-swap, crash → auto-disable with last-good hash pinned). **Zero non-test
+ callers.**
+
+### What is missing, and where it bites
+
+| Gap | Where | Consequence |
+| --- | --- | --- |
+| No scope tree | `context.ts` — flat `Disposable[]`, disposed in registration order, unawaited | A plugin cannot open a sub-scope for a feature it toggles; teardown order is accidental; async `deactivate` races the next mount (`packages/react/src/context.ts:427-457` fires deactivations without awaiting) |
+| Three disposal conventions | `Disposable` in `plugins/src/types.ts`, a second copy in `views/src/types.ts`, bare `() => void` in `slot-registry.tsx` / `TypedRegistry.onChange` | Every consumer handles cleanup differently; composition helpers can't exist |
+| No plugin→plugin service edge | `ecosystem/dependencies.ts` resolves **versions**, never objects | `dependencies` gates install order but grants no API access; a plugin cannot consume what another provides |
+| No inject semantics | — | A plugin needing the AI surface, a connector, or another plugin's API has no way to say so, wait for it, or be unloaded when it disappears |
+| `extraTools` never passed | `packages/plugins/src/ai-surface/service.ts:210` merge point; omitted by `apps/electron/src/main/agent-mcp-server.ts`, `packages/cli/src/commands/mcp.ts`, `packages/workbench/src/views/AiChatPanel.tsx:215` | `plugin_*` (9 tools, 0331), `lab_*`, all connector `agentTools`, and the auto-installed `WorkspaceAgentModule`'s tools reach **no model**. `ContributionRegistry.agentTools` is written by three files and read by nobody |
+| Hot reload unwired | `workspace-plugins/watcher.ts` | The only code-as-data plugin path (source stored as `PluginSourceSchema` nodes) has no host mounting it |
+| `registerSchema` stub | `context.ts:225` — empty `dispose()`, `// schemaRegistry.unregister would go here` | `contributes.schemas` is a no-op (0452 tracks the registry-side fix) |
+| Config is static | `first-party-catalog.ts` config forms → `PluginConfigDialog` | A config edit has no partial-reload path; nothing like `scope.accept(keys)` exists |
+
+> [!NOTE]
+> The gaps are all in one layer. Trust, gating, marketplace, contribution
+> *collection* — solid. What happens *between* activation and deactivation —
+> scopes, services, reload — is where xNet is a flat, static approximation of
+> what Cordis makes dynamic.
+
+---
+
+## External Research
+
+### The Cordis model, precisely
+
+Facts verified against the repo (`cordiverse/cordis`, MIT, 6,953★, created
+2022-05-17), npm (latest `4.0.0-rc.8`, 2026-08-10, ~20k downloads/wk), the
+v3-era README, and koishi.chat docs.
+
+**Context tree.** `new Context()` is the root; `ctx.extend()` creates children
+via the JS prototype chain plus per-context metadata. Koishi builds filtered
+contexts on top (`ctx.platform('discord').user('112233')`, plus
+`intersect`/`union`/`exclude`); anything registered through a filtered context
+— plugins, commands, listeners — is scoped to the filter.
+
+**Plugins as scopes.** A plugin is a function/class/`{ apply }` taking
+`(ctx, config)`. `ctx.plugin(p, config)` returns a fork scope;
+`fork.dispose()` reverts **every** collected effect. v4 renames the scope
+machinery `Fiber` and makes effects explicit: `ctx.effect(runner)` registers a
+revertible effect; disposers replay in **reverse order**; child fibers are
+themselves effects on the parent, so disposing a context tears down its whole
+subtree. A `dispose` event covers effects the framework can't auto-track
+(the README's example: close the port you opened in `ready`). Plugins can be
+**forked** — applied multiple times with per-fork config and per-fork
+disposal (`export const reusable = true`).
+
+**Services with inject semantics.** A plugin declares
+`export const inject = ['database']`. The contract (v3 README, verbatim
+semantics): the plugin *"will not be loaded until the service becomes
+truthy"*, is *"unloaded as soon as the service changes"*, and reloaded if the
+new value is truthy. Services are provided by other plugins (v4:
+`ctx.provide(name, value)` returns a disposer; a `Reflect` service throws
+typed errors on undeclared access — `cannot get property "X" without
+inject`). `ctx.isolate(name)` splits a service per subtree, so two instances
+of the same service can coexist. Swapping a service implementation
+automatically bounces every dependent plugin.
+
+**Reactive config.** `schemastery` (~89k downloads/wk — Cordis's most-adopted
+piece) is a chainable schema that both validates config and auto-generates
+config UIs. The loader calls `fork.update(config)`; a plugin can
+`scope.accept(keys, cb)` to patch accepted keys in place instead of
+restarting. Result: a config edit reloads only the plugins whose changed keys
+demand it.
+
+**HMR.** `@cordisjs/plugin-hmr` watches files with chokidar, walks the module
+dependency graph, disposes the affected plugins' fibers, and re-applies them
+with fresh exports — the process never restarts for plugin code changes.
+(It needs Node internals via `--expose-internals`; Koishi ships the same idea
+as its "watcher".)
+
+
+The paper's framing: temporal and spatial composability
+
+The companion preprint (`cordiverse/paper`, 2,610★, draft 2026-08-13, PDF
+only, **no named authors** — the "DeepSeek wrote it" framing in press coverage
+is unverified) names the two halves:
+
+- **Temporal composability** — a removed component's side effects can be
+ fully reverted ("revertible effects"). This is the fiber/scope machinery.
+- **Spatial composability** — dependencies between components are declared
+ and reactively managed ("reactive coeffects"). This is `inject`/`provide`.
+
+The mapping to xNet: we have a weak form of the first (flat disposables) and
+none of the second. The vocabulary is useful even if the paper's provenance
+is murky.
+
+
+
+### Ecosystem reality check
+
+| Signal | Value | Read |
+| --- | --- | --- |
+| Koishi community plugins | **4,551** (registry.koishi.chat, 2026-08-21) | The model scales to real ecosystems |
+| `cordis` npm downloads | ~20k/wk | Small direct adoption outside Koishi/dsh |
+| `schemastery` downloads | ~89k/wk | The config-schema piece travels furthest |
+| DeepSeek Harness | vendors Cordis (219 `package.json` matches: `ui-cordis`, `tool-cordis`, `cordis-host-runner`…) | The star spike is dsh's, not organic Cordis growth |
+| Maintainer | `shigma`, sole npm publisher across cordis/koishi/schemastery | Bus factor ≈ 1 |
+| API stability | README: API "may change without notice"; v3→v4 renamed EffectScope→Fiber, changed `isolate` signature | Real churn, mid-rc |
+| Docs | Standalone docs site dead; deep material zh-CN; best English API guide lives in a historical README commit | High adoption friction |
+
+### Criticisms that matter for us
+
+- **Trust model**: plugins run in-process with full reach — "good faith
+ rather than sandboxing" (Justin3go's dsh review, 2026-08-15). For a chatbot
+ framework that's tolerable; for a workspace holding a user's life it is
+ disqualifying. This is precisely the gap ADR-17 closes, and why "adopt
+ Cordis" and "keep our sandbox" cannot both be true for untrusted tiers.
+- **Magic**: prototype-chain contexts, Proxy interception,
+ `this[Context.current]` caller tracking, TS declaration merging for typing.
+ Costs readability; pre-v4, an unavailable service silently read as
+ `undefined` (v4's typed reflect errors are the admission).
+- **Over-engineering critique** (from dsh beta feedback): hot-reload
+ composability "benefits only edge cases"; agents that couldn't drive a
+ plugin correctly "just edit their own code instead". A useful caution for
+ 0331's agent-builds-plugins loop: the plugin API has to be *easier* than
+ forking the app, or agents will route around it.
+
+---
+
+## Key Findings
+
+### 1. Contributions vs services — the two halves of a plugin system
+
+xNet's model is VS Code's, and says so
+(`packages/workbench/src/contributions.tsx`: "Containers vs items, the VS
+Code model"): plugins **declare contributions into fixed host registries**.
+Cordis's model is a service container: plugins **provide and consume named
+capabilities**, and the container re-resolves when providers change. These
+are not rivals — VS Code itself has both (contribution points *and* an
+exported-API/service layer). xNet has only the first. There is no way for
+plugin B to use what plugin A provides; `dependencies` in the manifest
+resolves version constraints, never objects.
+
+### 2. Disposables without scopes
+
+xNet has the leaf of Cordis's temporal model (everything returns a
+`Disposable`; `ctx.subscriptions` auto-disposes on deactivate) and none of
+the tree: no nested scopes, no reverse-order guarantee, no awaited teardown,
+no fork (a plugin instantiated twice with different config), and three
+inconsistent disposal conventions across packages. This is the smallest
+change with the largest payoff, and it is invisible until you need it — hot
+reload, per-feature toggles, and service bouncing all *require* scoped
+disposal to be correct.
+
+### 3. The `extraTools` omission is the DI argument in one line
+
+```text
+ ┌──────────────────────────────┐
+ plugin_* (9, built)──▶│ │
+ lab_* (built)────────▶│ AiSurfaceService.extraTools │──▶ tools/list, dispatch
+ connector agentTools─▶│ (one merge point, service.ts│
+ WorkspaceAgentModule─▶│ line 210) │
+ └──────────────▲───────────────┘
+ │ never passed by:
+ agent-mcp-server.ts ─┤ (Electron bridge)
+ cli mcp.ts ──────────┤ (xnet mcp serve)
+ AiChatPanel.tsx ─────┘ (in-app assistant)
+```
+
+One merge point, three construction sites, three independent omissions, and
+every downstream tool family stranded — including the auto-installed
+`WorkspaceAgentModule`, whose entire design is tools driving the shell. With
+hand-threading, every new host must remember every provider. With a service
+registry, `AiSurfaceService` *resolves* tool providers at construction and
+re-resolves when a plugin activates or deactivates; all three sites become
+correct by construction, and a newly activated plugin's tools appear in a
+running session without restart — which is Cordis's `inject` reload semantics,
+needed here for a concrete shipped feature.
+
+### 4. Hot reload exists here and is disconnected
+
+`createWorkspacePluginHotReloader` already does what Cordis HMR does —
+rebuild on change, hot-swap the frame, crash → auto-disable with the
+last-good hash pinned. The reason it's unwired is structural, not accidental:
+Model A plugins (in-bundle, host realm) can't reload because their code isn't
+data, and Model C (source-as-`PluginSourceSchema`-node, which can) has no UI
+host. Wiring it is a 0452-ladder item (rung 4 prerequisite: "wire the
+workspace-plugin tools before rung 4") — this doc adds the *why now*: it is
+the temporal-composability half we already paid for.
+
+### 5. What Cordis validates about paths we already chose
+
+- **Registry-as-repo scales.** Koishi's 4,551 plugins ride an npm-scan
+ registry with marketplace metadata in `package.json` — structurally our
+ `registry/` + CI pipeline (0201, 0374) at larger scale. The pipeline shape
+ is right; our zero community plugins is a demand/capability problem, not an
+ infrastructure one.
+- **Schema-driven config UIs.** schemastery's config forms are our
+ `first-party-catalog.ts` config specs + `PluginConfigDialog`. Same idea;
+ ours lacks the reload wire (a config save should `update(config)` the
+ plugin, not require toggle-off-on).
+- **Everything-is-a-plugin needs a non-plugin referee.** Cordis's kernel
+ (Context/Fiber/Registry) is not itself a plugin. 0452's four exemptions
+ (kernel, shell, plugin system, protocol schemas) are the same line drawn
+ for the same reason.
+
+```mermaid
+flowchart LR
+ subgraph Cordis["Cordis has"]
+ A[Effect scopes / fibers reverse-order revert]
+ B[Service provide/inject reactive rebind]
+ C[HMR without restart]
+ D[Reactive config schemastery]
+ end
+ subgraph xNet["xNet has"]
+ E[Trust tiers + sandbox kinds ADR-17]
+ F[Capability guards guardStore / guardedFetch]
+ G[Fail-closed licensing, consent, provenance]
+ H[Registry pipeline + CI marketplace UI]
+ end
+ A -. missing in xNet .-> xNet
+ B -. missing in xNet .-> xNet
+ C -. built, unwired .-> xNet
+ D -. forms only, no reload .-> xNet
+ E -. absent in Cordis .-> Cordis
+ F -. absent in Cordis .-> Cordis
+ G -. absent in Cordis .-> Cordis
+```
+
+---
+
+## Options And Tradeoffs
+
+### Option A — Adopt Cordis as a dependency
+
+Replace `PluginRegistry`/`ExtensionContext` internals with `cordis` contexts;
+xNet plugins become Cordis plugins.
+
+- ✅ Battle-tested scope/service machinery for free; HMR for free.
+- ❌ **Trust mismatch is fatal**: Cordis composes objects in one realm.
+ xNet's `user` and `marketplace` tiers run behind an iframe/SES boundary
+ where only JSON-pure RPC crosses (`workspace-plugins/protocol.ts`). A
+ Cordis service edge cannot cross that boundary; we'd be adopting the
+ framework precisely where it can't reach.
+- ❌ Bus factor 1, API mid-rc and churning (EffectScope→Fiber), docs
+ effectively zh-CN only.
+- ❌ Proxy/prototype/declaration-merging magic contradicts the repo's
+ fail-loud, grep-able style ("a silently absent host is indistinguishable
+ from a broken shell" — `workbench/src/host.ts`).
+
+### Option B — Import the mechanisms, not the framework ⭐
+
+Build three small, typed, boring pieces inside `packages/plugins`, behind the
+seams that already exist: an effect-scope primitive, a service registry with
+inject semantics, and the reload wiring. Host-realm (first-party) plugins get
+direct service objects; sandboxed tiers get the same contract tunneled over
+the existing RPC — the service *names and availability semantics* are shared,
+the transport differs by trust tier. This keeps ADR-17 as the outer law and
+Cordis's composition as the inner mechanics.
+
+- ✅ Fixes the `extraTools` class of bug structurally; unblocks agent tools
+ (0331/0447), lab tools, connector tools in one move.
+- ✅ Unifies three disposal conventions; makes hot reload and per-feature
+ toggles correct instead of racy.
+- ✅ Zero new dependencies; every piece is ~100–300 lines with tests.
+- ❌ Real design work (service availability across async activation; the
+ RPC-tunneled variant for sandboxed tiers can ship later).
+
+### Option C — Status quo (VS Code contributions are enough)
+
+- ✅ No work.
+- ❌ The `extraTools` gap stays a whack-a-mole: every future host of every
+ future service repeats the omission. 0452's ladder lands on a runtime with
+ unordered teardown and no plugin→plugin edges, and 0331's loop stays
+ shelf-ware.
+
+> [!IMPORTANT]
+> This proposes no revenue lane, so Charter §6's three tests are not in
+> play. It changes no wire format and no public manifest field — `inject`
+> and `provides` enter the manifest as *optional* additions, which is why the
+> door is two-way.
+
+---
+
+## Recommendation
+
+**Option B**, in three steps ordered so each one ships value alone, aligned
+with 0452's ladder (its step 1 registry work and this doc's step 2 service
+work both live in `packages/plugins/src/`):
+
+1. **Effect scopes** (`packages/plugins/src/scope.ts`). One `EffectScope`
+ class: `use(disposable)`, `child()`, `dispose()` — reverse-order, awaited,
+ idempotent, re-entrancy-safe. `ExtensionContext.subscriptions` becomes a
+ scope; `PluginRegistry.deactivate` awaits it;
+ `packages/react/src/context.ts` awaits teardown before remount. Adopt one
+ `Disposable` type repo-wide (`() => void | Promise` accepted at the
+ boundary, normalized inside).
+
+2. **Service registry with inject semantics**
+ (`packages/plugins/src/services.ts`). `provide(name, value): Disposable`
+ and `inject(names, (services) => scopeBody)` where the body runs when all
+ names are available, is disposed when any disappears, and re-runs on swap
+ — Cordis's contract, minus proxies: explicit registration, typed lookup,
+ loud `ServiceUnavailableError`. First consumer: `AiSurfaceService`
+ resolves `agent-tools` providers from the registry, and
+ `ContributionRegistry.agentTools` gets its first reader. Wire all three
+ hosts through it; delete the three hand-threaded omissions. Manifest gains
+ optional `provides?: string[]` / `inject?: string[]` (validated, unlike
+ most contribution kinds today).
+
+3. **Reload wiring.** Mount the 0331 workspace-plugin host + hot reloader in
+ the workbench dev surface (the 0452 gate: "the honesty test can actually
+ be run by an agent"); route `PluginConfigDialog` saves through a
+ `registry.update(pluginId, config)` that bounces only the plugin's scope
+ (partial-accept à la `scope.accept` can come later; full bounce is
+ correct-if-slower first).
+
+```mermaid
+sequenceDiagram
+ participant P as Plugin (activate)
+ participant SR as ServiceRegistry
+ participant AI as AiSurfaceService
+ participant M as Model session
+ P->>SR: provide('agent-tools:my-plugin', tools)
+ SR-->>AI: availability change (inject re-run)
+ AI->>AI: merge into extraTools (dedupe, first wins)
+ M->>AI: tools/list
+ AI-->>M: built-ins + plugin tools
+ Note over P,SR: plugin deactivates → scope disposes → provide() reverted
+ SR-->>AI: availability change
+ AI->>AI: tools removed — no restart, no stale dispatch
+```
+
+Explicitly **not** recommended: forked/multi-instance plugins (no current
+need; revisit if a connector wants two accounts of one service), context
+filtering (Koishi's session selectors have no xNet analogue), schemastery
+(our config specs already exist), and any Proxy-based context sugar.
+
+## Example Code
+
+```ts
+// packages/plugins/src/scope.ts — the temporal half (sketch)
+export type Effect = { dispose(): void | Promise } | (() => void | Promise)
+
+export class EffectScope {
+ private effects: Effect[] = []
+ private children = new Set()
+ private state: 'active' | 'disposing' | 'disposed' = 'active'
+
+ use(effect: T): T {
+ if (this.state !== 'active') throw new ScopeDisposedError()
+ this.effects.push(effect)
+ return effect
+ }
+
+ child(): EffectScope {
+ const scope = new EffectScope()
+ this.children.add(scope)
+ this.use(() => scope.dispose())
+ return scope
+ }
+
+ async dispose(): Promise {
+ if (this.state !== 'active') return
+ this.state = 'disposing'
+ // reverse order — later effects may depend on earlier ones
+ for (const effect of this.effects.reverse()) {
+ try {
+ await (typeof effect === 'function' ? effect() : effect.dispose())
+ } catch (error) {
+ // loud, but one failed disposer must not strand the rest
+ console.error('[plugins] effect dispose failed', error)
+ }
+ }
+ this.effects = []
+ this.state = 'disposed'
+ }
+}
+```
+
+```ts
+// packages/plugins/src/services.ts — the spatial half (sketch)
+export class ServiceRegistry {
+ provide(name: string, value: T): Disposable
+ get(name: string): T // throws ServiceUnavailableError — never undefined
+ /** body runs when all names resolve; its scope is disposed when any
+ * provider goes away; re-runs if a provider is swapped. */
+ inject(names: string[], body: (scope: EffectScope) => void | Promise): Disposable
+}
+
+// The first consumer — AiSurfaceService resolves instead of being handed:
+const surface = createAiSurfaceService({ store, schemas, retrieveContext, services })
+// inside: services.inject(['agent-tools'], (scope) => this.mergeExtraTools(...))
+// — agent-mcp-server.ts, cli mcp.ts, AiChatPanel.tsx no longer each
+// need to remember; a plugin activating mid-session adds its tools live.
+```
+
+## Risks And Open Questions
+
+- **Scope creep into a framework.** The failure mode is rebuilding Cordis.
+ Guard: each piece needs a named first consumer before it merges (scopes →
+ registry teardown; services → `extraTools`; reload → 0331 host). No
+ speculative features.
+- **Service edges across the sandbox boundary.** A `user`-tier iframe plugin
+ cannot receive a live object. The contract: sandboxed plugins see services
+ only as RPC-tunneled, JSON-pure facades, and *providing* a service from a
+ sandboxed plugin is out of scope until a real case exists. The registry
+ must refuse (loudly) to hand a host-realm object across the boundary —
+ this is where ADR-17's line must hold against convenience.
+- **Availability semantics vs async activation.** `PluginRegistry.activate`
+ is async; inject bodies must not observe half-activated providers. Cordis
+ gates non-immediate services on `ready`; we need an equivalent rule
+ (provide only at the end of `activate`, enforced or linted).
+- **Unload semantics for live sessions.** When a provider disappears
+ mid-conversation, in-flight tool calls need a defined failure (typed error
+ to the model, not a hang). The scope model makes this expressible; it
+ still has to be decided.
+- **Does the agent actually want plugins?** dsh's beta feedback (agents
+ editing their own code rather than driving plugins) is a live risk for
+ 0331's loop. Mitigation is DX, not architecture: the `plugin_*` tools must
+ be cheaper for an agent than a source edit, and the preview/feedback loop
+ (`preview.ts`) is the leverage.
+- **Open question:** should `FeatureModule.capabilities` and
+ `PluginPermissions` unify while we're in the file? (Today they're bridged
+ by an untyped cast at `registry.ts:542`.) Probably yes, but it's severable
+ and shouldn't ride this change.
+
+## Implementation Checklist
+
+**Status:** ░░░░░░░░░░ 0/10 items
+
+- [ ] `EffectScope` in `packages/plugins/src/scope.ts` with reverse-order,
+ awaited, idempotent disposal + tests (incl. re-entrancy and a failing
+ disposer not stranding the rest)
+- [ ] Unify the `Disposable` conventions: one exported type in
+ `@xnetjs/plugins`, `packages/views` re-exports it, `slot-registry` /
+ `TypedRegistry.onChange` return it
+- [ ] `ExtensionContext.subscriptions` backed by an `EffectScope`;
+ `PluginRegistry.deactivate` awaits scope disposal;
+ `packages/react/src/context.ts` awaits teardown before remount
+- [ ] `ServiceRegistry` in `packages/plugins/src/services.ts` —
+ `provide`/`get`/`inject`, loud `ServiceUnavailableError`, availability
+ re-resolution on provide/dispose, + tests
+- [ ] Optional `provides` / `inject` manifest fields with real validation
+ (unlike the 14 unvalidated contribution kinds — don't add a 15th)
+- [ ] `AiSurfaceService` resolves agent-tool providers from the registry;
+ `agentToolsAsExtraTools` bridge registered as a provider reading
+ `ContributionRegistry.agentTools` (its first reader)
+- [ ] Wire all three hosts (`apps/electron/src/main/agent-mcp-server.ts`,
+ `packages/cli/src/commands/mcp.ts`,
+ `packages/workbench/src/views/AiChatPanel.tsx`) through the resolved
+ surface; verify `plugin_*` and `WorkspaceAgentModule` tools reach a
+ live session on each
+- [ ] Register `createWorkspacePluginAgentTools()` output as an
+ `agent-tools` provider (closes the 0331/0447 "built but unwired" gap)
+- [ ] Mount the workspace-plugin frame host + `createWorkspacePluginHotReloader`
+ behind a dev-surface entry point (coordinate with 0452 rung
+ prerequisites)
+- [ ] `PluginRegistry.update(pluginId, config)`: full scope bounce on config
+ save from `PluginConfigDialog`
+
+## Validation Checklist
+
+- [ ] Unit: disposing a parent scope disposes children first-in-reverse and
+ awaits async disposers; a throwing disposer doesn't strand later ones
+- [ ] Unit: `inject` body re-runs on provider swap and is disposed when a
+ provider goes away; `get` on a missing service throws typed
+- [ ] Integration: activate a plugin contributing `agentTools` mid-session →
+ `tools/list` over the MCP server includes it without restart;
+ deactivate → it disappears and an in-flight call fails typed
+- [ ] Integration: all three hosts pass the same test above (no
+ per-host omission possible — the test constructs each host)
+- [ ] E2E-ish: edit a `PluginSource` node → hot reloader rebuilds and swaps
+ the frame; a crashing build auto-disables with last-good pinned
+ (existing `workspace-plugins-watcher.test.ts` promoted to a wired host)
+- [ ] `pnpm build && pnpm typecheck && pnpm test` green; api-report updated
+ for `@xnetjs/plugins` new exports; changeset written
+
+## References
+
+- [cordiverse/cordis](https://github.com/cordiverse/cordis) — repo; v3 README
+ (historical commit `261ee6be`) has the best English API guide
+- [cordiverse/paper](https://github.com/cordiverse/paper) — "A Programming
+ Paradigm for Spatiotemporal Composability" (draft 2026-08-13; no named
+ authors)
+- [Koishi plugin docs](https://koishi.chat/en-US/guide/plugin/) — plugin,
+ [service](https://koishi.chat/en-US/guide/plugin/service.html), and
+ [filter](https://koishi.chat/en-US/guide/plugin/filter.html) guides
+- [registry.koishi.chat/index.json](https://registry.koishi.chat/index.json)
+ — 4,551 plugins (2026-08-21)
+- [DeepSeek Harness cordis primer](https://deepseek-harness.github.io/deepseek-harness/reference/cordis-primer)
+- [Justin3go's dsh review](https://justin3go.com/en/posts/2026/08/15-deepseek-harness-review)
+- Repo: [0452](./0452_[_]_HOW_FAR_TO_PLUGINIZE_THE_KERNEL_THE_SHELL_AND_THE_LIFT_OUT_LADDER.md)
+ (lift-out ladder), [0331](./0331_[x]_DEVELOPING_XNET_FROM_INSIDE_XNET_SPEC_TO_PLUGIN_LOOP.md)
+ (workspace-plugin runtime), [0206](./0206_[_]_WHY_SO_FEW_FIRST_PARTY_PLUGINS.md)
+ (lift-out test), [0205](./0205_[_]_DECOMPOSING_THE_APP_INTO_PLUGINS.md),
+ [0194](./0194_[_]_EXTENSIBILITY_FABRIC_PLUGINS_LABS_AI_EDITOR.md) (unify the
+ four extensibility systems), [0397](./0397_[_]_AGENT_NATIVE_FRAMEWORK_LESSONS.md)
+ (prior framework-comparison doc), ADR-17 in
+ `site/src/content/docs/docs/architecture/decisions.mdx`
diff --git a/docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md b/docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md
new file mode 100644
index 000000000..d79890578
--- /dev/null
+++ b/docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md
@@ -0,0 +1,501 @@
+---
+title: Entry vector — the agent door first
+status: draft
+last_updated: 2026-08-21
+review: 2026-11-19 # one quarter of focused execution, then re-score against the dogfood gate and the Buzz/Notion lane movement. 90-day default genuinely fits: this is a focus decision, not research.
+decider: Chris Smothers
+door: two-way # a focus and sequencing decision — no wire format, no public API, no revenue lane changes; every deferred item stays in the repo with its exploration intact
+tags: [strategy, focus, agents, plugins, go-to-market, roadmap]
+---
+
+# Entry vector — the agent door first
+
+> [!TIP]
+> **TL;DR** — Pick one door: **the agent door**. The entry
+> vector is `xnet connect claude-code` — "give your coding agent a workspace
+> you own" — because it is the only surface in the repo that is already a
+> single command, already on npm, already differentiated (0.11x MCP token
+> benchmark), and sits in the one quadrant of the agent-workspace lane
+> (local-first + agents extend the workspace *from within*) that Notion,
+> Cowork, Buzz, and DeepSeek Harness have not taken. "Self-improving xNet"
+> is not pie in the sky — 0331 already built the runtime and 0455 showed the
+> loop is roughly three wiring PRs from closed. The open-source-Notion lane
+> (~1% star→user conversion, Logseq dead of a rewrite) and the local-first
+> SDK lane (zero breakouts) are not entry vectors; cloud becomes one only
+> after something pulls people toward it. This doc adds **no new program**
+> — it sequences four existing checklists (0335 → 0455 → 0447 → positioning)
+> into one focus stack and names what is explicitly parked.
+
+## Problem Statement
+
+The founder's own words, condensed: *I want xNet to be self-improving —
+agents integrate seamlessly and extend it from within — but that feels far
+away. I want to ship something people actually use, but I don't know if
+that's an open-source Notion, the framework/React hooks, xNet Cloud and easy
+self-hosting, or the plugin substrate. I want Lego bricks: build once, every
+developer and user after that is more productive. What's the first entry
+vector?*
+
+This is not a new question for the repo. `docs/ROADMAP.md` (July 2026)
+already bet on three pillars in dependency order — AI daily driver, then
+effortless cloud, then the commons — gated on the founder's own daily use.
+The overwhelm is real anyway, for three reasons this doc addresses head-on:
+
+1. **The site still offers every door at once.** The hero renders App / SDK /
+ Protocol as three equal doors (`site/src/components/sections/Hero.astro`);
+ `GetStarted.astro` lists three unranked paths. A stranger cannot tell what
+ xNet is *first*.
+2. **The "self-improving" goal feels distant** — but the feeling is
+ miscalibrated. The audit in
+ [0455](./0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md) found the
+ agent-builds-plugins runtime (0331) fully built and tested with zero
+ callers, stranded behind one unpassed constructor argument.
+3. **Intent accumulates without closing.**
+ [0421](./0421_[-]_FAST_WHAT_COLLISONS_LIST_MEASURES_AND_WHAT_XNET_LACKS.md)
+ measured it: +85 unstarted explorations/month, 524 docs, and "xNet's
+ problem is not that work moves slowly, it is that intent accumulates
+ without ever being closed" (0421's own words). The cure for overwhelm is
+ not another program; it is sequencing what exists.
+
+## Executive Summary
+
+| Question | Answer |
+| --- | --- |
+| What is the entry vector? | **The agent door**: `xnet connect claude-code` — one command that gives a coding agent a workspace the user owns. Everything else (SDK, cloud, app) becomes a *second* step people take after that door works for them. |
+| Is "self-improving xNet" pie in the sky? | No — it's mislabeled near-done work. The 0331 iframe plugin runtime + hot reloader exist with 7 test files and zero callers; 0455's checklist closes the loop in ~3 PRs (service registry → `extraTools` wired → `plugin_*` tools live → host mounted). |
+| Why not open-source Notion? | The lane converts stars to daily users at roughly 1% (AppFlowy ~70k★ / ~46k MAU), its most famous member (Logseq) stalled fatally mid-rewrite, and winners there won by *narrowing* (Outline = team wiki). Head-on Notion marketing is the weakest use of a solo founder's quarter. |
+| Why not the SDK/framework? | No local-first framework has broken out (Electric pivoted to Postgres reads, Liveblocks open-sourced defensively, Jazz still pre-traction); even sympathetic engineers warn "local-first… is not a default." Keep the SDK shipped and honest; don't lead with it. |
+| Why not cloud first? | Supabase/n8n prove self-host wedges work — but each rode a one-sentence job people already wanted. Cloud amplifies demand; it doesn't create it. xNet Cloud's deploy workflow is literally inert today (`deploy-cloud.yml`: "INERT BY DEFAULT"). Turn it on when the agent door creates pull. |
+| Is the agent lane still open? | The lane is crowding (Notion agent hub May 2026, Anthropic Cowork Feb 2026, Block's Buzz July 2026, DeepSeek Harness Aug 2026) — but the specific quadrant **local-first, user-owned substrate where agents extend the workspace from within** is unclaimed. Buzz is the nearest neighbor (0416's thesis competitor, confirmed) and it is Nostr-relay-centric, not local-first, with a harness-out rather than workspace-in plugin story. |
+| What about the Lego bricks? | The bricks the founder wants to build ARE pillar 1 — but the cold-start evidence (VS Code, Obsidian, Raycast vs ChatGPT plugins) says ecosystems thrive only on an existing devoted user base. At n≈1, that user base is **the founder plus their agents**. Build the bricks your own agents snap together this quarter; the community comes after the demo is undeniable. |
+| What closes this doc? | The focus stack shipped (checklist below) and the roadmap's own gate: consecutive weeks of the founder's real work done inside xNet. |
+
+---
+
+## Current State In The Repository
+
+The full shippability audit is summarized here; the load-bearing facts each
+carry a path.
+
+### What is genuinely shippable today
+
+| Surface | Evidence | Verdict |
+| --- | --- | --- |
+| `xnet connect claude-code\|codex` | `packages/cli/src/commands/connect.ts` — idempotent, fenced CLAUDE.md edits, MCP registration, `xnet doctor --agent-access` self-check; read-only by default, `--writes` opt-in; `@xnetjs/cli@0.4.0` on npm | ✅ **The one single-command entry vector that already exists** |
+| Agent lanes | `site/src/content/docs/docs/guides/agent-interfaces.mdx` — CLI verbs → vault checkout → MCP fallback; benchmark: **0.11x the tokens of an MCP toolset at equal success on 15 tasks** | ✅ Differentiated and measured |
+| Web demo | `apps/web/src/boot/` + `/app?demo=1` — 2 steps, ~10–20 s cold start (SQLite WASM + OPFS), auto-seeded, never overwrites user content (`demo-seed.ts`) | ✅ Good |
+| npm data layer | 18 public packages at 3.0.0 (`core, data, react, sync, sqlite, …`), OIDC + provenance releases (`.github/workflows/npm-release.yml`) | ✅ Real |
+| React SDK standalone | `examples/minimal-app/` — outside the workspace, works against published npm, syncs via `wss://hub.xnet.fyi` | ✅ Under-marketed |
+| Self-host hub | `packages/hub/Dockerfile`, `docker-compose.hub.yml`, root `railway.toml`, multi-arch ghcr image | ✅ Strongest distribution story |
+
+### What is not, despite appearances
+
+| Surface | Evidence | Verdict |
+| --- | --- | --- |
+| xNet Cloud | `apps/cloud/src/server.ts` is a real Hono control plane with ~30 test files — but `.github/workflows/deploy-cloud.yml` is **"INERT BY DEFAULT"**, defaults are in-memory providers, billing gateway 503s unset, and `site/src/data/status.json` is a two-month-stale snapshot while `site/src/data/pricing.ts` deep-links every CTA to `cloud.xnet.fyi/auth/start` | ❌ Largest claim/state gap in the repo |
+| UI layer on npm | `ui, editor, views, canvas, workbench, dashboard, charts` all private/changeset-ignored | ❌ Devs can't install the components in the screenshots |
+| Mobile | Expo Go demo only; `site/src/pages/mobile.astro` says so honestly | ❌ Deliberately deferred (roadmap) |
+| Traction signal | `site/src/data/metrics.json` has `"sample": true`; `cloud-metrics.yml` inert; no waitlist, no testimonials, no user count anywhere; telemetry charter-banned | — Zero external signal exists, by design and by stage |
+| Agent tools loop | `AiSurfaceService.extraTools` never passed by any of the three hosts; `plugin_*` (9 tools), `lab_*`, `WorkspaceAgentModule` tools all stranded (0455) | 🚧 Built, unwired |
+| npm-facing story | `packages/cli/README.md` still describes schema-migration tooling; **does not mention `connect`, `checkout`, `commit`, or `mcp`** | 🚧 The best feature is unmarketed |
+
+> [!WARNING]
+> One real hazard rides the current download page:
+> `apps/electron/src/renderer/main.tsx:181` still defines `makeTestKey`
+> (deterministic, source-derivable signing key; "DO NOT use in production!")
+> and line 887 still calls it, while `secure-seed.ts` sits uncalled. This is
+> release-blocker #1 from the
+> [0335 audit](./0335_[_]_RELEASE_READINESS_AUDIT_WHAT_STANDS_BETWEEN_XNET_AND_A_WELL_RECEIVED_LAUNCH.md),
+> unfixed, on a binary `site/src/pages/download.astro` distributes today. A
+> product whose pitch is *you own your keys* cannot lead with a key any
+> reader of the repo can reconstruct. It is first in the focus stack for
+> that reason.
+
+### What the roadmap already decided
+
+`docs/ROADMAP.md` (July 2026): the bet is *"deep AI integration with total
+visibility, on top of a malleable, sandboxed workspace"* — three assets
+nobody else has (signed change log per node, workspace-as-Lego, plugins as
+sandboxed xNet artifacts). Pillar order: AI daily driver → effortless cloud
+→ commons. Principle 5: **"Dogfood is the metric"** — the gate is
+consecutive weeks of the author's real work inside xNet. Deliberately
+ignored: verticals, mobile parity, OS-level, marketplace-scale distribution.
+
+This exploration's job is to test that bet against August 2026 evidence and
+convert it into a single entry vector with a closable checklist. Spoiler:
+the bet survives, strengthened.
+
+---
+
+## External Research
+
+Full sourcing in the research notes; the decision-relevant findings:
+
+### Lane 1 — "Open-source Notion": stars ≠ users
+
+- AppFlowy: ~70k stars, **~46k MAU** per third-party trackers (~0.66 MAU per
+ star). AFFiNE: ~60k stars, and its growth team publishes literal
+ how-to-get-stars playbooks — stars are a marketing KPI there, not a user
+ metric. Anytype: ~$13.5M raised, niche.
+- **Logseq is the cautionary tale**: a database rewrite begun in 2022
+ consumed the project; last stable release April 2024; by 2026 the
+ community writes migration guides away from it.
+- The lane's survivors narrowed: Outline won "team wiki with Docker deploy,"
+ not "all of Notion."
+
+### Lane 2 — Local-first frameworks: no breakout, ever
+
+- npm reality (2026): Convex ~100k weekly downloads (and it sells a
+ *backend*, not local-first), InstantDB ~20k, ElectricSQL ~15k after
+ pivoting to narrow Postgres read-path sync. Liveblocks open-sourced its
+ engine in Feb 2026 — a defensive move. Jazz remains pre-traction.
+- The movement's own engineers say it: local-first "is not a default"
+ (Supabase engineer critique of teams adopting sync engines for 50-user
+ apps). Linear sold the *pattern*, not any framework — and built in-house.
+
+### Lane 3 — Self-host wedges: naming is the multiplier
+
+- Supabase went **8 → 800 hosted databases in three days** by renaming
+ itself "the open-source Firebase alternative" — same product. Now $170M
+ ARR / $10.5B (June 2026), lifted by AI-coding demand. n8n: $5.2B after SAP
+ (May 2026) as the default self-hosted agent-workflow layer. Cal.com rode
+ "open-source Calendly" — then went closed-source April 2026.
+- The pattern: a one-sentence wedge naming a job people already want, plus a
+ one-click deploy. The deploy xNet has (`railway.toml`, ghcr image); the
+ sentence it doesn't.
+
+### Lane 4 — The agent-workspace lane: crowding, with one quadrant open
+
+- **Notion** turned its workspace into "a hub for AI agents" (developer
+ platform, May 13 2026) — top-down, cloud, 100M users. **Anthropic Cowork**
+ (Feb 2026): enterprise agent plugins/marketplaces. **Block's Buzz**
+ (July 21 2026, Apache 2.0, ~15–25k stars in weeks): self-hostable
+ workspace where agents are full members with cryptographic identity —
+ xNet's quadrant neighbor and 0416's predicted thesis competitor, **but**
+ Nostr-relay-centric (not local-first CRDT) and harness-out (ACP drives
+ Goose/Codex/Claude Code at the workspace) rather than workspace-in
+ (agents building sandboxed plugins *inside* it). **DeepSeek Harness**
+ (Aug 2026): "everything is a plugin," commoditizing plugin architecture
+ for harnesses — a harness, not a workspace (and per ADR-29 / 0416, xNet
+ is deliberately not a harness).
+- Open as of today: **local-first, user-owned data as the agent substrate**
+ and **the agent extending the workspace by writing sandboxed plugins into
+ it**. Nobody has shipped that loop as a product.
+
+### The plugin cold-start table
+
+| Platform | Users first? | Plugin launch gap | Outcome |
+| --- | --- | --- | --- |
+| VS Code | Preview Apr 2015 | +7 mo | 500k MAU + 1,000 extensions at 1.0 |
+| Obsidian | May 2020, small rabid base | +~6 mo | 6.8k plugins, 120M downloads, ~1.5M MAU |
+| Raycast | Oct 2020 | +13 mo | 100+ community extensions in a month |
+| Figma | 2016, $25M revenue first | +3 yr | Plugins became the PLG flywheel |
+| ChatGPT plugins | **Plugins were the launch** | 0 | **Killed Apr 2024** — "most users never enabled plugins" |
+
+> [!IMPORTANT]
+> The ecosystem the founder wants ("once I build it, every developer after
+> me is more productive") historically only ignites on top of an existing
+> devoted user base — even a tiny one. The corollary is not "abandon the
+> plugin model"; it is **build the plugin loop for the user base you already
+> have: yourself and your agents.** That is also exactly what the roadmap's
+> dogfood gate demands. DeepSeek Harness is the apparent counterexample
+> (plugins-first, exploding) — but its users *are* developers and the plugin
+> *is* the product, which is precisely the agent-door framing, not the
+> marketplace framing.
+
+### Solo-founder focus wisdom, the two load-bearing points
+
+- Paul Graham, *Do Things That Don't Scale*: recruit users manually; build
+ for one user at a time; growth rate on a tiny base beats a launch.
+- Nadia Eghbal, *Working in Public*: for a solo maintainer, a big undirected
+ community is a **cost**; the failure mode is attention-consuming
+ low-value participation, not obscurity. Optimize for users, not
+ contributors — which cuts against investing in marketplace/community
+ mechanics before demand exists.
+
+---
+
+## Key Findings
+
+1. **The decision was already made; the evidence now confirms it.** July's
+ pillar order (daily driver → cloud → commons) matches what the lane
+ research independently concludes: product-with-devoted-users before
+ ecosystem, demand before cloud, content before commons. The overwhelm is
+ a positioning and sequencing problem, not a strategy vacuum.
+
+2. **The entry vector already exists and is unmarketed.** `xnet connect
+ claude-code` is one command, on npm, benchmarked, differentiated, safe by
+ default (read-only until `--writes`), and aligned with every current
+ trend (agents everywhere, MCP fatigue, token cost pressure). Its own
+ README doesn't mention it. The Supabase lesson says fixing *that* — the
+ sentence and the door — is the highest-leverage cheap work in the repo.
+
+3. **"Self-improving xNet" is three PRs away, not a moonshot.** The
+ pie-in-the-sky feeling comes from mislabeling: 0331 built the sandboxed
+ plugin runtime (iframe host, builder, store RPC, hot reloader — 7 test
+ files); 0455 diagnosed the single missing wire (`extraTools` never
+ passed) and wrote the checklist (effect scopes → service registry →
+ wire three hosts → mount the dev surface). Hot module reloading isn't
+ the far future — it's the shelf.
+
+4. **The Lego-brick instinct is right; the audience is wrong-sized.** Bricks
+ compound only when someone is building. This quarter the builders are the
+ founder and their agents. Every brick should be judged by one test: *does
+ it make my own agent measurably better at doing my real work inside xNet
+ this week?* That test kills marketplace mechanics, community
+ infrastructure, and SDK marketing for now — and green-lights exactly the
+ 0455/0447 wiring.
+
+5. **Buzz's existence is clarifying, not threatening.** It validates the
+ quadrant (someone at Block believed enough to ship it) and leaves xNet's
+ two differentiators intact: local-first CRDT ownership (Buzz is
+ relay-centric) and the in-workspace plugin loop with a signed audit
+ trail (Buzz drives external harnesses). But it moves faster than a solo
+ founder on breadth — which argues for depth on the loop no one else has,
+ not breadth-matching.
+
+6. **One honest-to-goodness blocker gates all marketing:** the deterministic
+ Electron signing key (0335 #1). "Own your keys" cannot be the pitch while
+ the shipped desktop key is derivable from source.
+
+```mermaid
+flowchart TD
+ subgraph LANES["The four lanes, scored"]
+ A["Open-source Notion ~1% star→user, Logseq ☠"]
+ B["Local-first SDK zero breakouts"]
+ C["Cloud / self-host works only WITH a wedge"]
+ D["Agent substrate crowding, one quadrant open"]
+ end
+ D -->|"the open quadrant"| Q["local-first + user-owned + agents extend from WITHIN"]
+ Q --> V["Entry vector: xnet connect claude-code"]
+ V --> LOOP["The loop: agent builds sandboxed plugins inside your workspace, every change signed & visible"]
+ LOOP -->|"creates pull"| C
+ LOOP -->|"creates content"| COMMONS["Commons / Index (last)"]
+ A -.->|"not the entry"| V
+ B -.->|"supporting surface"| V
+```
+
+---
+
+## Options And Tradeoffs
+
+### Option A — Open-source Notion: market the app to end users
+
+Polish the web/desktop app, launch on HN/Product Hunt as the local-first
+Notion alternative.
+
+- ✅ The demo is genuinely good (2 steps, auto-seeded); the lane has proven
+ *star* demand.
+- ❌ ~1% star→daily-user conversion; crowded (AppFlowy, AFFiNE, Anytype all
+ better-funded); a solo founder competing on end-user polish against teams
+ loses on breadth; and stars would flood a solo maintainer with exactly the
+ low-value participation Eghbal warns about.
+- ❌ Desktop can't be marketed at all until the 0335 key blocker is fixed.
+
+### Option B — The framework: market the React SDK
+
+Lead with `@xnetjs/react`, publish the UI packages, build `create-xnet`.
+
+- ✅ `examples/minimal-app` is real; hooks are stable at 3.0.0.
+- ❌ The lane has produced zero breakouts in five years; the UI layer is
+ private so the differentiating half isn't installable; "local-first
+ framework" is an architecture pitch, and architecture pitches don't
+ convert (Electric's pivot is the proof).
+
+### Option C — Cloud first: finish and launch xNet Cloud
+
+Turn on `deploy-cloud.yml`, swap in real providers, launch pricing.
+
+- ✅ The code is closer to done than it looks; Supabase/n8n prove the
+ self-host+cloud model.
+- ❌ Wrong order: every self-host winner rode existing demand for a named
+ job. Standing up billing/provisioning/support for zero pulled users is
+ pure operational drag on a solo founder. Cloud is pillar 2 for a reason —
+ *"cloud = amplifier not landlord"* and amplifiers need a signal.
+
+### Option D — The agent door ⭐
+
+One entry vector: **connect your coding agent to a workspace you own.**
+Close the self-improving loop for an audience of one (founder + agents),
+fix the key blocker, then say one sentence loudly.
+
+- ✅ Ships this quarter from existing checklists (0335, 0455, 0447); the
+ only lane quadrant still open; the only surface already reduced to one
+ command; matches the dogfood gate exactly; produces the demo no one else
+ can record (agent builds a sandboxed plugin inside the workspace, every
+ change signed, hot-reloaded live).
+- ✅ Pulls the other lanes behind it: agent users need sync → cloud demand;
+ agent-built plugins need publishing → commons content; devs who see the
+ loop want the SDK.
+- ❌ n=1 risk: the gate is subjective (the founder's own weeks-of-use) and
+ there is deliberately no telemetry to contradict self-report. Mitigation:
+ the validation checklist requires at least one outside person completing
+ the flow, recruited manually, PG-style.
+- ❌ Lane risk: Notion/Buzz could ship the in-workspace loop. Watch, don't
+ match — depth on ownership + visibility is the moat they'd have to
+ rebuild their foundations to copy.
+
+### Option E — Status quo: keep all three doors open
+
+- ✅ No decision required.
+- ❌ This *is* the overwhelm. Three unranked doors on the hero, a stale CLI
+ README, an inert cloud, and +85 unstarted docs/month is what "no entry
+ vector" looks like from the inside.
+
+> [!NOTE]
+> No new revenue lane is proposed (existing cloud pricing stands), so the
+> Charter §6 ground-rent tests are not triggered. The focus choice is
+> two-way: if the quarter disproves the agent door, options A–C remain
+> exactly where they are, minus nothing.
+
+---
+
+## Recommendation
+
+**Option D — the agent door, as a strict sequence.** The quarter's rule:
+every week's work must serve the sentence *"point your coding agent at a
+workspace you own, and watch it build you tools inside it."*
+
+**The focus stack** (order matters; each unblocks the next):
+
+1. **Safety before speech** — fix 0335 blocker #1: Electron boots on
+ `secure-seed.ts` (safeStorage-backed), `makeTestKey` dies or becomes
+ test-only. Nothing gets marketed while the key story is false.
+2. **Close the loop** — execute the 0455 checklist (effect scopes → service
+ registry → `extraTools` wired in all three hosts → `plugin_*` +
+ `WorkspaceAgentModule` tools live → workspace-plugin host and hot
+ reloader mounted behind a dev surface). This is 0447's "wire the loop,"
+ now with a mechanism-level plan. Exit criterion: *from a Claude Code
+ session, an agent scaffolds, previews, and installs a sandboxed plugin
+ into the founder's real workspace, and the change log shows every step.*
+3. **Dogfood ruthlessly** — the roadmap gate, made legible: a running
+ dogfood ledger (a page *in xNet*) logging each week the founder's real
+ work happened inside it, and what forced a fallback to other tools. Each
+ fallback is the next week's highest-priority fix. This is the
+ self-improvement flywheel at n=1 — the system improving because its user
+ lives in it, before any HMR mysticism.
+4. **Say one sentence, everywhere** — reposition around the door:
+ `packages/cli/README.md` rewritten around `connect`/`checkout`/`mcp`
+ (it currently sells 2024's schema tooling); the hero's primary CTA
+ becomes the agent quickstart with app/SDK demoted to secondary doors;
+ one launch-register blog post (the corpus has 24 essays and zero
+ launches) with the recorded loop demo; the 0.11x-tokens benchmark made
+ reproducible (script + methodology in-repo) since it will be challenged
+ the moment it's quoted.
+5. **Recruit manually** — PG-style: personally onboard 3–10 people who
+ already live in Claude Code/Codex, watch them run `xnet connect`, fix
+ what snags them. No waitlist, no community infrastructure, no Discord
+ moderation surface. (Eghbal: contributors are a cost; users are the
+ asset.)
+
+**Explicitly parked this quarter** (each keeps its exploration; none is
+withdrawn): cloud GTM (tripwire to un-park: an outside user asks for hosted
+sync twice), UI packages on npm, `create-xnet`, marketplace/community
+mechanics, Index/commons build-out, mobile, matching Buzz features,
+open-source-Notion positioning.
+
+```mermaid
+sequenceDiagram
+ participant U as Founder (n=1 user)
+ participant CC as Claude Code
+ participant X as xNet workspace
+ participant P as Sandboxed plugin
+ U->>CC: xnet connect claude-code
+ CC->>X: read/query/edit via CLI lanes (0.11x tokens)
+ U->>CC: "build me a tool for this workflow"
+ CC->>X: plugin_scaffold / plugin_build / plugin_preview (0331 tools, wired by 0455)
+ X->>P: iframe host mounts, hot reloader watches
+ P-->>U: new capability inside the workspace
+ X-->>U: signed change log — every step visible
+ Note over U,P: the loop = the demo = the pitch. Cloud, SDK, commons all inherit demand from it.
+```
+
+## Risks And Open Questions
+
+- **The n=1 gate can self-deceive.** With telemetry charter-banned, "I used
+ it all week" is unauditable. Mitigation: the dogfood ledger lives in the
+ workspace itself (its change log is signed and dated), and validation
+ requires outside humans completing the flow.
+- **Incumbent speed.** Notion's agent hub or Buzz could ship an
+ in-workspace plugin loop. The response is written into the choice: depth
+ on the two things structurally hard for them (local-first ownership;
+ signed total visibility), not breadth. Revisit at review if either ships
+ the loop.
+- **The agent door might onboard users into a thin room.** Someone arrives
+ via `xnet connect` with no existing xNet content — what does their agent
+ act *on*? The demo-seed path (`demo-seed.ts`) and vault checkout of
+ existing files partially answer this; the manual-onboarding step (5) is
+ where the real answer gets discovered. Open question to resolve during
+ the quarter.
+- **Benchmark fragility.** The 0.11x claim rides 15 tasks; once public it
+ invites adversarial replication. Publishing methodology (step 4) converts
+ the risk into credibility.
+- **Focus decay.** The repo's measured failure mode is accumulation
+ (0421). This doc itself must not become doc #525-that-nothing-closes: it
+ has a 90-day review, one decider, and a checklist whose items are
+ pointers into four existing checklists rather than new scope.
+
+## Implementation Checklist
+
+**Status:** ░░░░░░░░░░ 0/9 items
+
+- [ ] **0335 #1**: Electron uses `secure-seed.ts`; `makeTestKey` removed
+ from the production boot path (`apps/electron/src/renderer/main.tsx:887`)
+- [ ] **0455 items 1–7**: effect scopes + service registry landed;
+ `extraTools` resolved (not hand-threaded) in all three hosts
+- [ ] **0455 items 8–9 / 0447**: `plugin_*` tools + `WorkspaceAgentModule`
+ tools reach live sessions; workspace-plugin host + hot reloader
+ mounted behind a dev surface
+- [ ] **Loop demo recorded**: one take, unedited — agent scaffolds → builds
+ → previews → installs a plugin in the founder's real workspace;
+ change-log view shown
+- [ ] **Dogfood ledger** created *as an xNet page*; weekly entries; every
+ fallback-to-other-tools logged with a cause
+- [ ] **`packages/cli/README.md` rewritten** around
+ `connect`/`checkout`/`commit`/`mcp` (npm-facing)
+- [ ] **Hero repositioned**: one primary CTA (agent quickstart);
+ App/SDK demoted to secondary (`site/src/components/sections/Hero.astro`,
+ `GetStarted.astro`)
+- [ ] **Launch post** published (first launch-register post in the blog) +
+ benchmark methodology committed and reproducible
+- [ ] **3+ manual onboardings** of outside Claude Code/Codex users
+ completed, snags filed as issues/explorations
+
+## Validation Checklist
+
+- [ ] The recorded loop demo exists and required no code outside `main`
+- [ ] Roadmap gate: ≥4 consecutive ledger weeks of the founder's real work
+ in xNet, with fallback count trending down
+- [ ] At least one outside person completes `xnet connect` → agent reads
+ and writes their workspace, without founder intervention mid-flow
+- [ ] `npm view @xnetjs/cli` README mentions `connect`; site hero has
+ exactly one primary door
+- [ ] At review (2026-11-19): re-score the four lanes; if the agent door
+ produced zero outside pull after honest execution, re-open Options
+ A–C with this doc's evidence tables as the baseline
+
+## References
+
+- Repo: `docs/ROADMAP.md` (the three pillars, dogfood gate),
+ `docs/CHARTER.md`,
+ [0455](./0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md) (the
+ loop's mechanism-level plan),
+ [0447](./0447_[_]_LEARNING_FROM_MACRO_WIRE_THE_LOOP_BEFORE_WIDENING_THE_SUITE.md),
+ [0452](./0452_[_]_HOW_FAR_TO_PLUGINIZE_THE_KERNEL_THE_SHELL_AND_THE_LIFT_OUT_LADDER.md),
+ [0331](./0331_[x]_DEVELOPING_XNET_FROM_INSIDE_XNET_SPEC_TO_PLUGIN_LOOP.md),
+ [0416](./0416_[-]_AGENT_HARNESS_OR_AGENT_SUBSTRATE.md) (ADR-29;
+ Buzz prediction),
+ [0421](./0421_[-]_FAST_WHAT_COLLISONS_LIST_MEASURES_AND_WHAT_XNET_LACKS.md)
+ (accumulation metrics),
+ [0335](./0335_[_]_RELEASE_READINESS_AUDIT_WHAT_STANDS_BETWEEN_XNET_AND_A_WELL_RECEIVED_LAUNCH.md)
+ (key blocker), [0391](./0391_[x]_XNET_AS_THE_DAILY_DRIVER_AI_INTERFACE.md),
+ [0393](./0393_[_]_XNET_FROM_INSIDE_THE_CODING_AGENT.md) (`xnet connect`)
+- External (as-of dates in text): Supabase origin story
+ (stacksync.com) + Series F (CNBC, 2026-06); n8n/SAP (Bloomberg, 2026-05);
+ Obsidian stats (obsidianstats.com); AppFlowy/AFFiNE/Anytype trackers
+ (third-party, soft numbers, flagged in research); Logseq stall coverage;
+ Notion agent hub (TechCrunch, 2026-05-13); Anthropic Cowork (Forbes,
+ 2026-02-25); Block's Buzz (opensourceforu.com, digitalapplied.com,
+ 2026-07); DeepSeek Harness (The Register, 2026-08-14); ChatGPT plugins
+ shutdown retrospectives; Paul Graham, *Do Things That Don't Scale*;
+ Nadia Eghbal, *Working in Public*
diff --git a/docs/explorations/0457_[-]_AGENT_FIRST_SITE_REARCHITECTURE.md b/docs/explorations/0457_[-]_AGENT_FIRST_SITE_REARCHITECTURE.md
new file mode 100644
index 000000000..e9f136743
--- /dev/null
+++ b/docs/explorations/0457_[-]_AGENT_FIRST_SITE_REARCHITECTURE.md
@@ -0,0 +1,525 @@
+---
+title: Agent-first site re-architecture — every page converts one door
+status: draft
+last_updated: 2026-08-21
+review: 2026-11-19 # same date as 0456, deliberately — this is 0456 step 4 specified; if the entry-vector bet is re-scored, this doc re-scores with it
+decider: Chris Smothers
+door: two-way # copy, information architecture, and one new route; no wire format, no public API, no pricing change. Every demoted page keeps its URL.
+tags: [site, marketing, agents, positioning, conversion, docs-ia]
+---
+
+# Agent-first site re-architecture — every page converts one door
+
+> [!TIP]
+> **TL;DR** — Rebuild the site's conversion spine around one action:
+> `xnet connect claude-code`. Hero becomes a copyable command
+> with per-agent tabs (the Bun / Claude Code pattern), a new `/agents` page
+> becomes the conversion hub (per-client installs, safety model, the 0.11x
+> benchmark), "Connect your agent" becomes the header button, the
+> coding-agents guide moves from _item 9 of a collapsed accordion_ into
+> **Start Here**, and llms.txt finally mentions `xnet connect` — today the
+> string appears in exactly **one** file on the whole site
+> (`coding-agents.mdx`) and in none of: nav, footer, hero, GetStarted,
+> README, llms.txt. Nothing is deleted: App, SDK, Cloud, Why all keep their
+> URLs and their depth pages, demoted one rank. Ship in two phases —
+> **Phase A now** (repositioning what already works: connect, checkout,
+> MCP, the benchmark), **Phase B when 0456's loop demo exists** (the
+> agent-builds-plugins section). Everything a human reads gets a twin the
+> _agent_ reads, because for this product the agent is present at the
+> moment of conversion.
+
+## Problem Statement
+
+[0456](./0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md) chose the entry
+vector: the agent door, `xnet connect claude-code|codex`. Its step 4 said
+"say one sentence, everywhere" and reserved one checklist line for the
+hero. This exploration is that step, fully specified: what does the landing
+page, the site IA, the docs, the README, and the agent-readable layer look
+like when **everything converts toward the agent connection** — while the
+app, the React SDK, xNet Cloud, and the movement pages all remain, one rank
+down?
+
+The gap is stark. The site survey found:
+
+- `xnet connect` appears in **one** source file
+ (`site/src/content/docs/docs/guides/coding-agents.mdx`) — nowhere in
+ `Nav.astro`, `Footer.astro`, `Hero.astro`, `GetStarted.astro`, or the
+ root `README.md`.
+- `public/llms.txt` — the file coding agents actually fetch — **omits the
+ coding-agents guide entirely** while listing 40+ other docs.
+- The landing's agent section (`BuiltForAgents.astro`) is 4th of 7,
+ ~6 viewports down, and demos `xnet checkout` / `xnet query` — not the
+ one command we want typed.
+- The docs landing (`docs/index.mdx`) opens "xNet is a local-first React
+ framework" and never links either agent guide.
+- There is no `/agents` route, no agent data file, no OG images, no
+ sitemap, no site-wide robots.txt.
+
+The site is not wrong — it is even-handed. Even-handed is the problem
+(0456, Option E).
+
+## Executive Summary
+
+| Question | Answer |
+| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| The one sentence | **"Give your coding agent a workspace you own."** Sub-sentence: docs, databases, and canvases your agent can read, query, and build in — every change signed, synced, and yours. |
+| The one action | A copy-button command in the hero, per-agent tabs: Claude Code · Codex · Cursor · VS Code · anything (MCP). Primary everywhere: header button, hero, GetStarted path 1, README section 1, docs Start Here. |
+| What happens to the app/SDK/cloud? | Kept, demoted one rank. App = "the workspace behind the agent" (section + `/app` untouched); SDK = the developer depth pages (`/react`, `/build-with`); Cloud = a whisper ("Free to start · Pricing") per the Ollama pattern. No URL dies. |
+| What's new? | One route: **`/agents`** (conversion hub + `src/data/agents.ts`); an agent-readable layer (llms.txt fix, `/agents.md`, install snippets/deeplinks); OG meta while we're in `Base.astro`. |
+| What's honest to ship _today_? | Phase A: connect, three lanes, read-only default, agent passports, the 0.11x benchmark (with methodology published — a 0456 item). All shipped and true now. |
+| What waits? | Phase B: the "agent builds tools inside your workspace" section and demo — gated on 0456's loop wiring (0455 checklist). The site must not market the loop before it's recordable. |
+| Biggest execution risk | Build gates: `build-llms-full.ts` fails if a docs page isn't in `sidebar.mjs`; `validate-dist.ts` asserts route outputs (read before renaming anything); `pricing-claims.test.ts` regex-reads `pricing.ts` as text — don't reformat it. |
+
+---
+
+## Current State In The Repository
+
+### The conversion spine today
+
+```text
+Nav: [xNet] App Developers Open | Why Build Demos Blog [Docs] [Try the App]
+ │
+Hero: "Your data. Your devices. Your rules." ▼
+ [Try the app — free, no account] [Read the docs] /app
+ doors: App(emerald) · SDK(indigo) · Protocol(purple)
+ "The app is built on the SDK. The SDK implements the protocol.
+ Start anywhere." ← three equal doors
+
+Sections: Hero → TheApp → ForDevelopers → BuiltForAgents → NoBlackBoxes
+ → HumaneByDesign → GetStarted (App / SDK / Movement — no agent path)
+```
+
+Everything routes to `/app?demo=1` or `/docs/quickstart/` (SDK). The agent
+story is mid-scroll (`BuiltForAgents.astro`, showing `checkout`/`query`),
+and its docs are behind a collapsed accordion: `coding-agents` is item 9 of
+15 in **Guides**, three levels below Start Here.
+
+### Assets the restructure can reuse (no new machinery needed)
+
+| Asset | Path | Why it matters |
+| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
+| Terminal chrome component | `site/src/components/ui/CodeBlock.astro` — macOS traffic lights, `filename="terminal"`, hover copy button | The hero command block already exists as a component |
+| Tab strip component | `site/src/components/ui/CodeTabs.astro` — dependency-free, group-synced, localStorage-persisted, no-JS fallback | Per-agent tabs (Claude Code/Codex/Cursor/VS Code) for free |
+| The content itself | `docs/guides/coding-agents.mdx` (the `xnet connect` guide), `docs/guides/agent-interfaces.mdx` (three lanes + 0.11x benchmark), `docs/ai/understanding-xnet.mdx` | The `/agents` page is 80% assembly of existing prose |
+| Changelog receipts | 12+ agent fragments in `site/src/data/changelog/` (e.g. `2026-07-24-use-xnet-from-claude-code-and-codex.json`, `2026-08-01-verify-what-your-agent-did.json`) | Social-proof strip: real dated receipts, no invented testimonials |
+| llms-full pipeline | `site/scripts/build-llms-full.ts` + `site/src/sidebar.mjs` (single source of truth for docs order **and** llms-full order) | Reordering the sidebar reorders the agent-readable corpus too |
+| Data-file pattern | `site/src/data/*.ts` driving every page | `agents.ts` slots in beside `pricing.ts`/`compare.ts` |
+
+### Constraints that will bite (from the survey)
+
+> [!WARNING]
+> Four build gates constrain this work. (1) `build-llms-full.ts` **fails
+> the build** if a docs content file is in neither `sidebar.mjs` nor its
+> exclusion list — every new docs page needs a sidebar entry in the same
+> PR. (2) `scripts/validate-dist.ts` was added after a half-built deploy
+> wiped the homepage for ~30 min (2026-07-18); **read it before renaming
+> or deleting any route** — it asserts route outputs exist. (3)
+> `apps/cloud/src/pricing-claims.test.ts` reads `site/src/data/pricing.ts`
+> as **text with a whitespace-sensitive regex** — do not reformat that file
+> while touching Cloud copy. (4) `validate-metrics.ts` fails on overstated
+> or >25%-stale figures — any new stat on the hero must come from
+> `siteMetrics.ts`'s conservative-floor pattern.
+
+Also: `site/` installs `--ignore-workspace` and cannot import `@xnetjs/*`
+(root `AGENTS.md`); the established workarounds are repo-root JSON imports
+(`plugins.ts` → `registry/registry.json`) and committed snapshots — an
+`agents.ts` data file follows the same pattern. Deploys ride
+`deploy-site.yml` (site + `/app` + `/play` assembled onto `gh-pages`; the
+"~9 min to live" figure from memory is not written anywhere in-repo —
+verify empirically before quoting it in launch-day plans).
+
+---
+
+## External Research
+
+### The command-first hero is a solved pattern
+
+From the 2026 survey of dev-tool landers:
+
+- **Bun**: headline + copyable `curl … | bash` with OS tabs + versioned
+ install button + a _replayable_ benchmark race above the fold. Its trophy
+ logos are agent products (Claude Code, Cursor, Midjourney, Railway).
+- **Claude Code itself**: name + "Work with Claude directly in your
+ codebase…" + download button **and** install one-liner. MCP integrations
+ are a late section — the mirror image of xNet, which _is_ the
+ integration and should lead with the connect command.
+- **Homebrew**: the page essentially is the command. **Ollama**: one
+ action; cloud reduced to "Free to start. See pricing."
+- **Aider**: proof by quantified usage ("88% of new code in the latest
+ release written by Aider itself") — the dogfood-metric pattern 0456's
+ ledger can eventually feed.
+- **Evil Martians' 100-lander study**: exactly **two** CTAs (one dominant,
+ one subordinate); specific verb copy over "Get started"; for
+ libraries/infra a code snippet _is_ the right hero visual; pricing on
+ its own page.
+
+### The "add to your agent" affordance stack (mid-2026 table stakes)
+
+- Per-client tab strip with copy buttons: `claude mcp add …` (Claude Code
+ convention — the snippet is the affordance), `~/.codex/config.toml` TOML
+ block (Codex), **Cursor deeplink** (`cursor://anysphere.cursor-deeplink/mcp/install?name=…&config=`,
+ official button assets; pair with a JSON fallback — deeplinks are
+ reported flaky), **VS Code badge** (`vscode:mcp/install?`).
+- Directory distribution (Smithery ~16.8k MCPs listed; mcp.so ~20k
+ secondhand) is marketing reach, not a substitute for the page.
+- Docs sites are now expected to _be_ agent-consumable: Mintlify ships
+ `/llms.txt`, `/llms-full.txt`, per-page "copy as Markdown"; GitBook
+ auto-exposes an MCP endpoint per docs site.
+
+### Marketing to the agent, not just the human
+
+Netlify named the category — "Agent Experience (AX)" — and in April 2026
+launched **netlify.ai, a site built for agents rather than humans**
+(onboarding and build context for the agent itself). The nuance from the
+llms.txt adoption data: no major AI vendor commits to llms.txt for
+_search/training_, but **coding agents do fetch `/llms.txt` when pointed
+at a docs site** — which is precisely xNet's conversion moment: an agent
+is _running_ `xnet connect` while its human watches. xNet's llms.txt
+currently forgets to mention the connect flow at all.
+
+One more external fact that shapes copy: **Continue.dev's lander is now an
+acquisition notice and Goose's is a redirect stub.** Harness brands churn.
+The site should anchor on "your coding agent" generically, with named
+clients as tabs — never as the headline.
+
+---
+
+## Key Findings
+
+1. **This is a repositioning, not a rebuild.** The components (terminal
+ chrome, tab strip), the content (two mature agent guides), and the
+ receipts (12 dated changelog fragments) all exist. What's missing is
+ rank: the agent story is mid-scroll on the homepage, item 9 of a
+ collapsed accordion in docs, and absent from nav, README, and llms.txt.
+
+2. **The conversion moment is a two-reader moment.** Uniquely for this
+ product, at the instant of conversion there are two readers: the human
+ deciding, and the agent about to execute `xnet connect` (and likely
+ fetching `/llms.txt` mid-run). Every conversion surface therefore needs
+ a human face and an agent twin. No competitor in the workspace lane
+ does this; Netlify proved the pattern in the deploy lane.
+
+3. **Honesty gates the section order.** The three lanes, read-only
+ default, passports, signed log, and the benchmark are shipped and true
+ — Phase A can say them loudly today. The loop ("your agent builds tools
+ _inside_ the workspace") is 0455/0456 wiring away; the site must not
+ promise it before the demo records. The Charter's own rule (every
+ promise ships with a receipt or is labeled not-yet) applies to
+ marketing exactly as to docs.
+
+4. **The benchmark is the single most quotable asset and the most
+ fragile.** "~9x cheaper than MCP tools" already appears on the landing
+ page; the hero will amplify it. 0456 already requires the methodology
+ to be published and reproducible — that item becomes a _prerequisite_
+ of Phase A launch, not a follow-up.
+
+5. **Demotion must be visible-but-cheap.** The lesson from Ollama ("Free
+ to start. See pricing") and Evil Martians (pricing on its own page):
+ the app and cloud don't vanish — they compress to one honest line each
+ with a route. That satisfies "keep all the other features" without
+ re-splitting the funnel.
+
+```mermaid
+flowchart TD
+ subgraph HUMAN["Human reader"]
+ H1[Hero: one sentence + command] --> H2["/agents — what your agent gets, safety model, benchmark"]
+ H2 --> H3[Copies command / clicks deeplink]
+ end
+ subgraph AGENT["Agent reader"]
+ A1["/llms.txt — names connect flow"] --> A2["/agents.md — what am I connecting to, which tools"]
+ A2 --> A3["SKILL.md installed by connect (~500 tokens, exists today)"]
+ end
+ H3 --> C[xnet connect claude-code]
+ A3 --> C
+ C --> W[Agent reads/writes the workspace three lanes, signed log]
+ W -->|"wants sync"| CLOUD["/cloud (demoted, intact)"]
+ W -->|"wants the UI"| APP["/app (demoted, intact)"]
+ W -->|"Phase B"| LOOP[Agent builds plugins inside — gated on 0456 loop demo]
+```
+
+---
+
+## Options And Tradeoffs
+
+### Option A — Copy-only touch-up
+
+Rewrite `Hero.astro` copy and promote `BuiltForAgents` to section 2; change
+nothing else.
+
+- ✅ One PR, zero risk to build gates.
+- ❌ Leaves the funnel broken where it actually converts: no `/agents`
+ page to send traffic to, docs still SDK-first, llms.txt still silent on
+ connect, README untouched. The header button still says "Try the App."
+ Half a repositioning reads as indecision — the current site's disease.
+
+### Option B — Full-stack repositioning in two phases ⭐
+
+Phase A (now): hero + nav + `/agents` route + GetStarted + docs IA +
+llms.txt/agents.md + README, all around what ships today. Phase B (gated
+on 0456's loop demo): the plugin-loop section, the recorded demo, and the
+dogfood-metric proof strip.
+
+- ✅ Converts the whole spine while every claim stays true; the two-phase
+ gate keeps marketing behind reality; touches no revenue mechanics.
+- ✅ Each Phase A item is small and independently shippable (see
+ checklist) — no big-bang redesign, `--delete` rsync deploys stay safe.
+- ❌ ~8–10 PRs across site, docs, README; sidebar/llms-full/validate-dist
+ gates need care; OG/meta work tempts scope creep (kept optional).
+
+### Option C — Separate agent microsite (agents.xnet.fyi or netlify.ai-style twin)
+
+- ✅ Maximum focus; the main site stays even-handed.
+- ❌ Splits authority and maintenance for a solo founder; the survey shows
+ the main site's traffic surfaces (README, llms.txt, docs) are exactly
+ where the fix is needed; a microsite duplicates the Starlight/llms
+ pipeline. The agent-twin _pages_ (Option B) capture the netlify.ai idea
+ without a second property.
+
+### Option D — Docs-as-landing (Tailwind posture)
+
+Make `/docs` the homepage; kill the marketing site's hero.
+
+- ❌ Throws away the `/why`/Charter/blog narrative layer that is xNet's
+ actual differentiation vs Buzz/Notion, and the 25-essay corpus that
+ earns trust. Rejected without much agony.
+
+> [!NOTE]
+> No revenue lane changes: Cloud pricing, plans, and CTAs are untouched
+> except in rank. Charter §6 tests not triggered.
+
+---
+
+## Recommendation
+
+**Option B.** The spec, surface by surface:
+
+### 1. Hero (`site/src/components/sections/Hero.astro`)
+
+```text
+┌────────────────────────────────────────────────────────────────────┐
+│ [Alpha — shipping, and still moving fast] │
+│ │
+│ Give your coding agent a workspace you own. │
+│ │
+│ Documents, databases, and canvases your agent can read, query, │
+│ and build in — local-first, synced, every change signed. │
+│ │
+│ ┌ Claude Code ┊ Codex ┊ Cursor ┊ VS Code ┊ Any agent ─────────┐ │
+│ │ ● ● ● terminal [copy] │ │
+│ │ $ npx @xnetjs/cli connect claude-code │ │
+│ │ ✓ skill installed · mcp registered · read-only by default │ │
+│ └─────────────────────────────────────────────────────────────┘ │
+│ │
+│ [What your agent gets → /agents] [Try the app] (2nd CTA) │
+│ │
+│ ~9x cheaper than MCP toolsets* · read-only until you say so · │
+│ works offline · MIT *methodology → /agents │
+└────────────────────────────────────────────────────────────────────┘
+```
+
+- Tabs via existing `CodeTabs.astro`; terminal via existing
+ `CodeBlock.astro` (`filename="terminal"`). Claude Code/Codex tabs show
+ `xnet connect …`; Cursor/VS Code tabs show the MCP deeplink button +
+ copyable JSON fallback; "Any agent" shows `xnet mcp serve`.
+- Exactly two CTAs (Evil Martians): primary → `/agents`, secondary →
+ `/app?demo=1`. The three equal doors **go away**; the closing line
+ becomes "There's a full workspace app behind this — and an SDK under
+ both. [App] · [SDK] · [Protocol]" as small links.
+- Verify the exact zero-install one-liner before shipping (`npx
+@xnetjs/cli …` vs `npm i -g` — whichever `packages/cli` actually
+ supports; the checklist carries this).
+- Static command block first; a typed-replay animation is a Phase B
+ nicety, not a blocker (Deno converts with no animation at all).
+
+### 2. New route: `/agents` (+ `site/src/data/agents.ts`)
+
+The conversion hub, assembled from existing content:
+
+1. Per-client install (the hero tabs, expanded — including
+ `claude mcp add` and Codex TOML for people who prefer raw MCP).
+2. **What your agent gets**: the three lanes from `agent-interfaces.mdx`
+ (CLI verbs → vault checkout → MCP fallback), with the token benchmark
+ and a link to the published methodology.
+3. **The safety model**: read-only by default, `--writes` opt-in, agent
+ passports, every change signed into the log — "verify what your agent
+ did" (reuse the changelog fragment's framing).
+4. **Receipts strip**: the dated agent changelog fragments as cards (real
+ receipts instead of invented testimonials).
+5. One-line demotions: "Prefer a UI? [Try the app]. Building your own?
+ [React SDK]. Want managed sync? [Cloud — free to start]."
+6. Phase B slot: the recorded loop demo replaces a "what's next" teaser.
+
+`agents.ts` holds the per-client commands/deeplinks/labels so the hero
+tabs, `/agents`, README snippets, and docs quickstart all render from one
+source (same pattern as `pricing.ts`).
+
+### 3. Nav + footer (`Nav.astro`, `Footer.astro`)
+
+- Header: add **Agents** as the first page link; the filled conversion
+ button becomes **"Connect your agent" → `/agents`**; "Try the App"
+ moves to a plain link. Everything else stays.
+- Footer: new first column **Agents** (Connect guide, Agent interfaces,
+ /agents, llms.txt, MCP/registry listings), then Product/Cloud/Develop/
+ Resources/Community as today.
+
+### 4. Docs IA (`site/src/sidebar.mjs`, `docs/index.mdx`)
+
+- **Start Here** becomes: Introduction → **Connect your agent**
+ (`coding-agents.mdx`, retitled) → Quickstart (SDK) → Core Concepts.
+ `agent-interfaces` moves up alongside it or into a new "Agents" group
+ right under Start Here — either way, out of the collapsed accordion.
+- `docs/index.mdx` opens with two cards — "Connect your coding agent" and
+ "Build with React" — replacing the SDK-only lede.
+- Sidebar reorder automatically reorders `llms-full.txt` (same source of
+ truth); regenerate and commit in the same PR (`pnpm check:llms-full`).
+
+### 5. The agent-readable layer
+
+- `public/llms.txt`: add the connect flow at the **top** ("If you are a
+ coding agent: your human can connect you with `xnet connect `;
+ after connect you get these tools/lanes…"), plus the missing
+ coding-agents entry.
+- New `public/agents.md` (the netlify.ai move, one page not a microsite):
+ what xNet is _to an agent_, the three lanes, tool list, safety
+ contract, where the SKILL.md comes from. Linked from llms.txt and
+ `/agents`.
+- Optional same-PR cheap wins while in `Base.astro`: `og:title`/
+ `og:description`/`twitter:card` (site has **zero** OG meta today),
+ `@astrojs/sitemap`, site-wide `robots.txt`.
+
+### 6. README (root)
+
+Mirror the site's new order: after the one-liner and screenshot, **Try it**
+gains "Connect your coding agent" as the _first_ bullet (`npx @xnetjs/cli
+connect claude-code`), before demo/download/hub; a short "Your agent,
+your workspace" section (three lanes + benchmark + safety line) lands
+above "Build with it". Zero agent mentions today → the second landing
+surface gets the same spine.
+
+### 7. Explicitly unchanged
+
+`/why`, `/commitments`, `/blog`, `/compare`, `/open`, `/status`,
+`/roadmap`, all legal pages, `/cloud` + pricing (rank only), `/plugins`,
+`/download`, `/mobile`, `/demos`, `/react`, `/build-with`, `/devtool` —
+URLs, content, and validators untouched.
+
+### Phase gate
+
+> [!IMPORTANT]
+> **Phase A ships now** — every claim above is true of today's shipped
+> `@xnetjs/cli@0.4.0`. **Phase B** (the loop section: "ask your agent for
+> a tool; it builds a sandboxed plugin inside your workspace; watch every
+> change in the signed log" + recorded demo + Aider-style dogfood metric)
+> **is gated on 0456's checklist item "loop demo recorded"** — the site
+> never gets ahead of the repo. The 0.11x benchmark methodology
+> publication (a 0456 item) is a **Phase A prerequisite**, because the
+> hero quotes it.
+
+## Risks And Open Questions
+
+- **The command must work flawlessly for strangers.** The hero promotes a
+ path so far run mostly by its author. 0456's manual-onboarding item is
+ the mitigation; sequence at least one outside run before the hero
+ flips. Also confirm `npx @xnetjs/cli connect` works without global
+ install (and without a pre-existing workspace — the "thin room" question
+ from 0456: what does a fresh agent connect _to_? The `/agents` page
+ should answer with the demo-seed or `xnet vault init` story).
+- **Cursor/VS Code deeplinks are flaky** (documented forum failures) —
+ always render the copyable JSON beside the button; treat the deeplink as
+ progressive enhancement.
+- **Benchmark exposure.** Quoting 0.11x in the hero invites replication
+ attempts; methodology must be in-repo and reproducible first
+ (prerequisite above).
+- **validate-dist and route assembly.** Read `scripts/validate-dist.ts`
+ before the nav/route PR; add `/agents` to whatever it asserts. The
+ gh-pages rsync `--delete` means a bad build can blank pages — the
+ validator exists because it already happened once; keep it updated rather
+ than bypassed.
+- **Alpha honesty vs conversion.** The alpha badge stays in the hero. The
+ 0335 key blocker (0456 item 1) must land before any launch push drives
+ desktop downloads.
+- **Open question — the name of the door.** "Agents" vs "AI" vs "Connect"
+ in nav copy; "Agents" is assumed here (matches `/agents`,
+ survives harness churn), but test on the manual onboardings.
+- **Open question — Plausible goals.** Cookieless Plausible is already
+ gated in; whether to define custom events (copy-click, tab-select) or
+ keep zero-measurement is a Charter-flavored decision left to the
+ decider.
+
+## Implementation Checklist
+
+**Status:** ░░░░░░░░░░ 0/12 items (Phase A: 1–10; Phase B: 11–12)
+
+- [x] Verify + document the canonical zero-install command (`npx
+@xnetjs/cli connect claude-code` or equivalent) against
+ `packages/cli` as published; fix `packages/cli` if npx flow has
+ gaps
+- [x] Publish the 0.11x benchmark methodology in-repo (0456 item, now a
+ Phase A prerequisite) and link target for the hero footnote
+- [x] `site/src/data/agents.ts` — per-client commands, deeplinks, labels
+ (single source for hero tabs, `/agents`, README, docs)
+- [x] `Hero.astro` rewrite: new headline/sub, `CodeTabs` + `CodeBlock`
+ command block, two CTAs, doors → small links
+- [x] New `site/src/pages/agents.astro` per the section spec; update
+ `scripts/validate-dist.ts` expectations if route-asserting
+- [x] `Nav.astro` (+Agents link; button → "Connect your agent") and
+ `Footer.astro` (+Agents column)
+- [x] `GetStarted.astro`: path 1 becomes "Connect your agent" (command
+ block), App and SDK follow
+- [x] Docs IA: `sidebar.mjs` — coding-agents into Start Here (retitled
+ "Connect your agent"), agent-interfaces promoted; `docs/index.mdx`
+ two-card lede; regenerate `llms-full.txt` (`pnpm check:llms-full`)
+- [x] Agent-readable layer: `public/llms.txt` top section + coding-agents
+ entry; new `public/agents.md`; (optional, same PR: OG meta in
+ `Base.astro`, `@astrojs/sitemap`, `robots.txt`)
+- [x] Root `README.md`: connect-first Try-it bullet + "Your agent, your
+ workspace" section above Build-with
+- [ ] **Phase B**: loop demo section on `/` and `/agents` once 0456's
+ "loop demo recorded" item is checked; typed-replay animation of the
+ connect+session terminal
+- [ ] **Phase B**: dogfood proof strip (ledger-derived metric, Aider
+ pattern) once the 0456 ledger has ≥4 weeks of data
+
+## Validation Checklist
+
+- [x] `cd site && pnpm build` green (all validators incl. llms-full check
+ and validate-dist) with the new route and reordered sidebar
+- [ ] A fresh machine + `npx` run of the hero command succeeds verbatim
+ as printed, against the published npm package (not the repo)
+- [ ] An agent given only `https://xnet.fyi` (via llms.txt/agents.md) can
+ explain what `xnet connect` will do and which tools it gets —
+ tested by actually asking Claude Code with a clean context
+- [ ] Cursor deeplink and VS Code badge each install the MCP server on a
+ clean profile; JSON fallback verified when the deeplink fails
+- [x] Every demoted page still reachable within two clicks of `/`
+ (nav or footer); no URL removed (`check:exploration-links`-style
+ manual sweep of site nav)
+- [ ] At least one 0456 manual onboarding completed **through the new
+ site** without founder intervention — the site was the only guide
+- [ ] Phase B additions appear only after the referenced 0456 items are
+ verifiably checked
+
+## References
+
+- Repo: [0456](./0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md) (the
+ strategy this specifies),
+ [0455](./0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md) (loop
+ wiring behind Phase B),
+ [0384](./0384_[x]_TIGHTENING_THE_LANDING_PAGE_FROM_28_VIEWPORTS_TO_A_FOCUSED_FUNNEL.md) (the
+ teaser→route rule this doc obeys: teasers link depth pages, never
+ re-argue them), `site/src/components/sections/Hero.astro`,
+ `site/src/sidebar.mjs`, `site/scripts/build-llms-full.ts`,
+ `scripts/validate-dist.ts` (via `site/package.json` build),
+ `apps/cloud/src/pricing-claims.test.ts`,
+ `site/src/content/docs/docs/guides/coding-agents.mdx`,
+ `public/llms.txt`
+- External: Bun (bun.sh) hero pattern; Claude Code product page
+ (claude.com/product/claude-code); Ollama (ollama.com) one-action page;
+ Aider (aider.chat) dogfood metric; Evil Martians "We studied 100 dev
+ tool landing pages" (2025) + LaunchKit; Cursor MCP install-links docs
+ (deeplink + button assets); VS Code "Agent mode meets MCP" (May 2025,
+ `vscode:mcp/install` badges); modelcontextprotocol/mcpb bundles;
+ Mintlify contextual menu / llms.txt tooling; GitBook docs-MCP
+ endpoints; Netlify Agent Experience + netlify.ai (Apr 2026); Smithery
+ (smithery.ai); llms.txt adoption surveys (secondary sources, directional
+ only); Continue.dev acquisition page + Goose redirect (harness churn)
diff --git a/packages/cli/src/__tests__/connect-command.test.ts b/packages/cli/src/__tests__/connect-command.test.ts
index 177e393f1..ba86c1672 100644
--- a/packages/cli/src/__tests__/connect-command.test.ts
+++ b/packages/cli/src/__tests__/connect-command.test.ts
@@ -13,9 +13,12 @@ import {
MANAGED_BEGIN,
MANAGED_END,
mergeManagedBlock,
+ NPX_LAUNCHER,
+ resolveServerLauncher,
runConnect,
writeCodexConfig,
writeMcpJson,
+ XNET_PATH_LAUNCHER,
type ConnectOptions
} from '../commands/connect.js'
@@ -36,8 +39,22 @@ describe('xnet connect', () => {
expect(buildServerEntry({ dir, db: '/d.db' }).args).toEqual(['mcp', 'serve', '--db', '/d.db'])
})
+ it('registers an npx launcher when xnet is not on PATH (zero-install connect)', async () => {
+ // A PATH with no xnet bin anywhere → the npx fallback, so the registered
+ // server survives after the `npx @xnetjs/cli connect …` cache is gone.
+ expect(resolveServerLauncher({ PATH: dir })).toEqual(NPX_LAUNCHER)
+
+ // A PATH dir that does hold an xnet bin → register the real thing.
+ await writeFile(join(dir, 'xnet'), '#!/bin/sh\n')
+ expect(resolveServerLauncher({ PATH: `${dir}` })).toEqual(XNET_PATH_LAUNCHER)
+
+ const entry = buildServerEntry({ dir, db: '/d.db' }, NPX_LAUNCHER)
+ expect(entry.command).toBe('npx')
+ expect(entry.args).toEqual(['-y', '@xnetjs/cli', 'mcp', 'serve', '--db', '/d.db'])
+ })
+
it('claude-code writes skill, .mcp.json, and CLAUDE.md; is idempotent', async () => {
- const changes = await runConnect('claude-code', { ...base, dir })
+ const changes = await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER)
const byPath = Object.fromEntries(changes.map((c) => [c.path.replace(dir, ''), c.status]))
expect(byPath['/.claude/skills/xnet/SKILL.md']).toBe('created')
expect(byPath['/.mcp.json']).toBe('created')
@@ -48,12 +65,12 @@ describe('xnet connect', () => {
expect(mcp.mcpServers.xnet.env).toEqual({ XNET_READONLY: '1' })
// Re-run: everything unchanged.
- const again = await runConnect('claude-code', { ...base, dir })
+ const again = await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER)
expect(again.every((c) => c.status === 'unchanged')).toBe(true)
})
it('codex writes AGENTS.md and .codex/config.toml with a valid server block', async () => {
- const changes = await runConnect('codex', { ...base, dir, writes: true })
+ const changes = await runConnect('codex', { ...base, dir, writes: true }, XNET_PATH_LAUNCHER)
const byPath = Object.fromEntries(changes.map((c) => [c.path.replace(dir, ''), c.status]))
expect(byPath['/AGENTS.md']).toBe('created')
expect(byPath['/.codex/config.toml']).toBe('created')
@@ -84,7 +101,7 @@ describe('xnet connect', () => {
const original = '# My project\n\n@AGENTS.md\n\nHouse rules that took months.\n'
await writeFile(join(dir, 'CLAUDE.md'), original)
- await runConnect('claude-code', { ...base, dir })
+ await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER)
const merged = await readFile(join(dir, 'CLAUDE.md'), 'utf8')
expect(merged).toContain('# My project')
expect(merged).toContain('House rules that took months.')
@@ -94,11 +111,11 @@ describe('xnet connect', () => {
})
it('rewrites only the managed block on a re-run, leaving edits outside it', async () => {
- await runConnect('claude-code', { ...base, dir })
+ await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER)
const first = await readFile(join(dir, 'CLAUDE.md'), 'utf8')
await writeFile(join(dir, 'CLAUDE.md'), `${first}\n## My own section\n\nKeep me.\n`)
- const again = await runConnect('claude-code', { ...base, dir })
+ const again = await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER)
const merged = await readFile(join(dir, 'CLAUDE.md'), 'utf8')
expect(merged).toContain('## My own section')
expect(merged).toContain('Keep me.')
@@ -109,7 +126,7 @@ describe('xnet connect', () => {
it('preserves an existing AGENTS.md on the codex path', async () => {
await writeFile(join(dir, 'AGENTS.md'), '# Existing agent rules\n')
- await runConnect('codex', { ...base, dir })
+ await runConnect('codex', { ...base, dir }, XNET_PATH_LAUNCHER)
const merged = await readFile(join(dir, 'AGENTS.md'), 'utf8')
expect(merged).toContain('# Existing agent rules')
expect(merged).toContain(MANAGED_BEGIN)
diff --git a/packages/cli/src/commands/connect.ts b/packages/cli/src/commands/connect.ts
index 8215f462b..aa737456f 100644
--- a/packages/cli/src/commands/connect.ts
+++ b/packages/cli/src/commands/connect.ts
@@ -18,9 +18,10 @@
* shell-less clients.
*/
+import { existsSync } from 'node:fs'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { homedir } from 'node:os'
-import { dirname, join, resolve } from 'node:path'
+import { delimiter, dirname, join, resolve } from 'node:path'
import { XNET_AGENT_SKILL_MD } from '@xnetjs/plugins/node'
import { Command } from 'commander'
import { parse as parseToml, stringify as stringifyToml } from 'smol-toml'
@@ -52,14 +53,44 @@ export type McpServerEntry = { command: string; args: string[]; env?: Record {
const dir = resolve(options.dir)
const changes: ConnectChange[] = []
- const entry = buildServerEntry(options)
+ const entry = buildServerEntry(options, launcher)
if (harness === 'claude-code') {
changes.push(
diff --git a/site/public/agents.md b/site/public/agents.md
new file mode 100644
index 000000000..da147daa7
--- /dev/null
+++ b/site/public/agents.md
@@ -0,0 +1,64 @@
+# xNet, for the agent reading this
+
+You are (or are about to be) connected to a user's **xNet workspace**: their
+documents, databases, and canvases, stored locally on their device in a
+SQLite-backed, CRDT-synced store. The store is the source of truth. Every
+write is signed and lands in a hash-chained change log the user can audit —
+including yours.
+
+## How you were (or will be) connected
+
+```
+npx @xnetjs/cli connect claude-code # or: connect codex
+```
+
+That command installed a ~500-token `SKILL.md` (the full workflow contract),
+registered the xNet MCP server as a fallback lane, and wrote a short section
+into `CLAUDE.md` / `AGENTS.md`. It is idempotent and read-only by default;
+write access exists only if your human passed `--writes`.
+
+## The three lanes, cheapest first
+
+1. **CLI.** `xnet search ""`, `xnet query --where field=value`,
+ `xnet db get ` — plain TSV/stdout you can grep and pipe. Reach here
+ first; it costs far fewer tokens than tool schemas.
+2. **Vault checkout.** `xnet checkout --query ""` materializes a
+ _scoped_ slice of the workspace as Markdown + JSONL files. Read and Edit
+ them like any files, then `xnet commit` turns your edits into
+ schema-validated mutation plans. Never check out the whole workspace.
+3. **MCP.** The registered `xnet` server, for when you have no shell. Slim by
+ design; read-only unless connected with `--writes`.
+
+## The safety contract (structural, not policy)
+
+- **You cannot corrupt the store.** Every write path — file edit, CLI, MCP —
+ funnels through the same plan → validate → apply core. Malformed or
+ stale writes are quarantined as conflict notes in `.xnet/conflicts/`, never
+ silently applied.
+- **You sign as yourself.** Writes require an enrolled agent passport or an
+ explicit key. The CLI refuses to persist a write under a throwaway
+ identity.
+- **Everything you do is auditable.** Your changes land in the signed change
+ log next to everyone else's. The user can verify what you did without
+ trusting you — behave accordingly.
+
+## Key CLI verbs
+
+| Command | Purpose |
+| ----------------------------- | ------------------------------------------- |
+| `xnet checkout` | Materialize / extend a scoped checkout |
+| `xnet status` | Pending plans and conflicts |
+| `xnet commit [--apply]` | Turn file edits into applied mutations |
+| `xnet search ""` | Ranked full-text search |
+| `xnet query --where f=v` | Query tables (TSV default) |
+| `xnet db get/set` | Single-cell reads and writes |
+| `xnet run ` | Sandboxed bulk work, write _proposals_ only |
+| `xnet skill` | Print the SKILL.md contract |
+
+## Read more
+
+- Full docs, one file: https://xnet.fyi/llms-full.txt
+- Connect guide: https://xnet.fyi/docs/guides/coding-agents/
+- Agent interfaces (checkout layout, benchmark methodology):
+ https://xnet.fyi/docs/guides/agent-interfaces/
+- Building _apps_ on xNet instead? https://xnet.fyi/docs/ai/understanding-xnet/
diff --git a/site/public/llms-full.txt b/site/public/llms-full.txt
index 5e7626bb9..fb3aac9ce 100644
--- a/site/public/llms-full.txt
+++ b/site/public/llms-full.txt
@@ -15,8 +15,11 @@ Before reading this documentation, understand that xNet works differently:
## Table of Contents
- What is xNet?
+ - Connect Your Agent
- Quick Start
- Core Concepts
+ - Agent Interfaces
+- Understanding xNet (For AI Assistants)
- Hooks Overview
- useQuery
- useMutate
@@ -55,8 +58,6 @@ Before reading this documentation, understand that xNet works differently:
- Identity & Keys
- Real-time Collaboration
- Plugin Development
- - Agent Interfaces
- - Use xNet from Claude Code / Codex
- Hub Setup
- Your Own Server
- Use xNet From Any Framework
@@ -76,7 +77,6 @@ Before reading this documentation, understand that xNet works differently:
- Getting Started
- Code Style
- Testing Guide
-- Understanding xNet (For AI Assistants)
---
@@ -168,6 +168,107 @@ For most apps, you only install `@xnetjs/react` and `@xnetjs/data`. Everything e
---
+## Connect Your Agent
+
+**You will learn**
+
+- How to connect a coding agent to your workspace with one command
+- The three lanes an agent uses, cheapest first
+- How writes stay safe when the app and the agent touch the same data
+
+
+The same way people point Claude Code at an Obsidian vault, you can point it at
+your **xNet workspace** — and read, search, query, and edit your pages,
+databases, and canvases without leaving the terminal. The
+[Agent Interfaces](/docs/guides/agent-interfaces/) guide covers the surfaces in
+depth; this one is the quick on-ramp.
+
+## One-step connect
+
+No install needed — `npx` runs the published CLI directly:
+
+```bash
+npx @xnetjs/cli connect claude-code # zero-install, project scope, read-only
+```
+
+Or, with the CLI installed (`npm install -g @xnetjs/cli`):
+
+```bash
+xnet connect claude-code # project scope, read-only
+xnet connect claude-code --user # also install the skill for all projects
+xnet connect claude-code --writes # register the MCP server with write access
+xnet connect codex # Codex: AGENTS.md + .codex/config.toml
+```
+
+`xnet connect` is **idempotent** — it installs the `SKILL.md`, registers the
+xNet MCP server, writes a `CLAUDE.md`/`AGENTS.md` contract, and (with
+`--vault `) bootstraps a scoped checkout with an index. Re-running it
+reports what is already in place and changes nothing. It finishes by running
+`xnet doctor --agent-access`, which confirms the backend, full-text search, and
+signing identity are all reachable.
+
+The registered server launches however the CLI is actually available: as
+`xnet` when the bin is on your PATH, or as `npx -y @xnetjs/cli` after a
+zero-install connect — so the registration keeps working either way.
+
+## Works whether or not the app is running
+
+The agent verbs resolve a backend automatically:
+
+1. the desktop app's local API (`:31415`) when it is running, otherwise
+2. a standalone SQLite store — discovered from the app's data directory, or
+ pointed at explicitly with `--db ` (or `$XNET_DB`).
+
+So an agent can work against your data even with the app closed, and web-only
+users can point at any local store. Writes made this way are signed by a key you
+provide (`--key`, `$XNET_SIGNING_KEY`) or by an
+[enrolled agent passport](/docs/guides/identity/) (`--agent `); the CLI
+refuses to persist a write under a silent throwaway identity.
+
+## Three lanes, cheapest first
+
+```mermaid
+flowchart LR
+ A[Agent in Claude Code / Codex] -->|Bash| CLI[1 · xnet CLI]
+ A -->|Read / Edit / Grep| V[2 · Vault checkout]
+ A -->|MCP client| M[3 · xnet mcp serve]
+ CLI --> Core[(xNet store · signed change log)]
+ V <-->|checkout / commit| CLI
+ M --> Core
+```
+
+1. **CLI** — `xnet search`/`query`/`db get` print plain stdout. Reach here
+ first: it costs far fewer tokens than loading a wall of MCP tool schemas.
+2. **Vault** — `xnet checkout --query "…"` materializes a **scoped** slice as
+ Markdown you edit, then `xnet commit` lifts the edits back through validated
+ mutation plans. Never check out the whole workspace.
+3. **MCP** — the registered `xnet` server is the fallback for shell-less
+ clients (Claude Desktop, browser). It is **read-only** unless you connected
+ with `--writes`.
+
+## Safe when the app and the agent collide
+
+The store stays the source of truth, and every edit becomes a validated plan —
+never a blind file-to-database sync. If the app writes a node after you checked
+it out, committing your now-stale vault edit surfaces a **conflict** instead of
+overwriting the newer value:
+
+```bash
+$ xnet status
+conflict Pages/shared-page.md stale-export based on updatedAt:10, live is updatedAt:11
+```
+
+Fix the file against the current value and commit again. Stale edits never
+silently win.
+
+## Further reading
+
+- [Agent Interfaces](/docs/guides/agent-interfaces/) — the checkout layout, the
+ full CLI, `SKILL.md`, and the slim MCP surface
+- [Identity](/docs/guides/identity/) — agent passports and signing
+
+---
+
## Quick Start
**You will learn**
@@ -507,60 +608,398 @@ When you create or update a node, xNet:
3. Stores the signed change locally
4. Syncs the signed change to peers
-When a peer receives a change, they **verify the hybrid signature** before applying it. This means:
+When a peer receives a change, they **verify the hybrid signature** before applying it. This means:
+
+- You can prove who made every change
+- Tampered data is rejected
+- No server is needed to enforce trust
+- Changes are protected against quantum forgery attacks
+
+The default **Level 1 (Hybrid)** signature carries both an Ed25519 component (64 bytes) and an ML-DSA-65 component (~3.3 KB). Both must verify — if either is invalid, the change is rejected. Level 0 (Ed25519 only) is available for high-frequency paths where the smaller signature size matters more than quantum resistance.
+
+## Identity is a key pair
+
+Your identity in xNet is a **DID:key** — a decentralized identifier derived from an Ed25519 public key:
+
+```
+did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
+```
+
+No accounts. No passwords. No auth server. Your key pair is your identity. You can delegate permissions to other keys using **UCAN tokens** (capability-based authorization).
+
+## Authorization is encryption
+
+xNet enforces read access cryptographically. Every private node is encrypted with a **per-node content key**. Only users in the **recipients list** can unwrap the key and read the node — the hub never decrypts anything.
+
+You define access control in the schema itself:
+
+```ts
+const TaskSchema = defineSchema({
+ // ...
+ authorization: {
+ roles: {
+ owner: role.creator(),
+ editor: role.property('editors')
+ },
+ actions: {
+ read: allow('owner', 'editor'),
+ write: allow('owner', 'editor'),
+ share: allow('owner')
+ }
+ }
+})
+```
+
+Then check permissions in components:
+
+```tsx
+const { canWrite, canShare } = useCan(taskId)
+const { grants, grant, revoke } = useGrants(taskId)
+```
+
+Grants are stored as ordinary nodes — they sync, replicate, and inherit the same CRDT conflict resolution as all your data.
+
+See the [Authorization Guide](/docs/guides/authorization/) for the full model including delegation, key recovery, and offline policy.
+
+## Next steps
+
+---
+
+## Agent Interfaces
+
+**You will learn**
+
+- Why the filesystem is the primary agent surface
+- The checkout layout and how edits flow back safely
+- The `xnet` CLI verbs and the ~500-token SKILL.md
+- When to fall back to MCP
+
+
+## Files first
+
+Coding agents are already excellent at reading, grepping, and editing files —
+so xNet's primary agent interface is a **filesystem projection of the
+workspace**, not a wall of tool definitions. The store stays the source of
+truth; the checkout is a working tree. Every surface (files, CLI, MCP) funnels
+into the same plan → validate → apply core, so an agent can never corrupt the
+workspace with a malformed write.
+
+Measured on a 15-task benchmark, the files+CLI surface completes the same work
+at **~0.11x the tokens** of a traditional MCP toolset (0.05x on synthesis
+tasks), with equal task success. The
+[methodology and how to reproduce it](#benchmark-methodology) are below.
+
+## The checkout
+
+`xnet checkout` materializes a scoped slice of the workspace into a folder:
+
+```
+Pages/q3-planning.md # Markdown + YAML frontmatter (xnet.id, revision)
+Databases/tasks.schema.json # the schema
+Databases/tasks.rows.jsonl # one JSON object per line — edit/append/delete
+Databases/tasks.tsv # read-only fast-read sidecar (large tables)
+Canvases/roadmap.canvas # JSON Canvas projection
+SKILL.md # the agent contract (~500 tokens)
+.xnet/ # manifest, conflict notes
+```
+
+Filenames are semantic slugs; identity lives in frontmatter and the manifest,
+never in filenames. Wikilinks (`[[Title]]`), `xnet-database` blocks, and
+`{{xnet-ref}}` directives are live references and survive round-trips.
+
+Checkouts are **scoped and incremental** — by query, schema, kind, or node
+(`xnet checkout --query "roadmap"`); repeated checkouts merge. Agents never
+need (and are told not to) export the whole workspace.
+
+### Edits become validated plans
+
+File edits are diffed into **mutation plans**, validated against schemas, and
+applied to the store — automatically with `xnet daemon` running, or on
+`xnet commit`. Anything that doesn't validate, or collides with a newer
+revision, is **quarantined as a conflict**: a Markdown note in
+`.xnet/conflicts/` explains what happened and how to resolve it. Stale edits
+never silently overwrite newer data.
+
+## The xnet CLI
+
+| Command | Purpose |
+| --- | --- |
+| `xnet checkout` | Materialize / extend a scoped checkout |
+| `xnet status` | Pending plans and conflicts |
+| `xnet commit [--apply]` | Turn file edits into applied mutations |
+| `xnet search ""` | Ranked full-text search (TSV: id, slug, title, snippet) |
+| `xnet query --where field=value` | Query tables (TSV default; `jsonl`/`json`/`md`) |
+| `xnet db get/set` | Single-cell reads and writes |
+| `xnet run ` | Sandboxed bulk work over a bounded `api` object |
+| `xnet daemon` | Watch the checkout and auto-apply valid edits |
+| `xnet skill` | Print SKILL.md |
+
+Output is TSV-first and concise by default (`--detailed` to opt out) — built
+for being read by a model, not a human.
+
+`xnet run` executes scripts in a sandbox with bounded reads and **write
+proposals** (`api.proposeUpdate`, `api.proposeCreate`) that flow through the
+same validated plan pipeline as file edits.
+
+## SKILL.md
+
+Every checkout ships a cross-harness `SKILL.md` (~500 tokens) that teaches any
+agent — Claude Code, Codex, Gemini CLI, Cursor — the layout and workflow above.
+That's the entire standing context cost, versus ~5k tokens of always-loaded
+tool definitions for a typical MCP server. Print it any time with
+`xnet skill`.
+
+## Slim MCP
+
+MCP remains as the **no-shell fallback** for harnesses that can't run a CLI.
+It's deliberately slim: five stable core tools are loaded eagerly and
+everything else defers (the non-deferred payload is test-guarded at ≤1.5k
+tokens). Responses are compact JSON by default with
+`response_format: concise | detailed` on every tool.
+
+The doctrine, in order: **files for reading and editing, the CLI for what
+files can't express, MCP only when there's no shell.**
+
+## Benchmark methodology
+
+The "~0.11x the tokens" claim comes from an in-repo, reproducible benchmark —
+not a one-off measurement. What it is, precisely:
+
+- **The suite**: 15 tasks (read a page, edit a page, query a database, bulk
+ update, cross-node synthesis) run against a seeded workspace fixture. Every
+ task really executes against the same plan → validate → apply core the
+ product uses; **success is measured, not assumed**.
+- **Three surfaces**: `files-cli` (vault checkout + file tools + `xnet` CLI),
+ `mcp-legacy` (all tool definitions standing, pretty-printed JSON), and
+ `mcp-slim` (core tools standing, compact JSON — today's MCP fallback).
+- **What's counted**: a token cost model (~4 chars/token) over the *actual
+ bytes* each surface moves through model context — standing tool/skill
+ definitions, request arguments, tool responses, file contents read, edit
+ diffs, CLI commands and their outputs.
+- **What's not counted**: model reasoning tokens. This is an **interface cost
+ model** — it measures what each surface forces into context, not how a
+ specific model thinks. Live harness runs with pinned versions are tracked
+ separately (exploration 0161).
+- **Result**: files+CLI at 0.111x the total tokens of the legacy MCP surface
+ overall, 0.050x on synthesis tasks, 15/15 task success on every surface.
+
+Reproduce it from a repo checkout:
+
+```bash
+pnpm bench:agent-surfaces
+```
+
+Source: `packages/plugins/src/benchmarks/agent-surface-benchmark.ts`. The
+ratios are **regression-guarded in CI** by
+`agent-surface-benchmark.test.ts`, so the numbers quoted here fail the build
+if the surfaces drift.
+
+## Further reading
+
+- [For AI Assistants](/docs/ai/understanding-xnet/) — the mental model for
+ agents writing *app code* against xNet
+- [Plugins](/docs/guides/plugins/) — the integration layer MCP belongs to
+
+---
+
+## Understanding xNet (For AI Assistants)
+
+# Understanding xNet: A Guide for AI Assistants
+
+If you're an AI assistant helping a developer build with xNet, this guide will help you understand the fundamental paradigm differences.
+
+**Working inside a user's workspace?**
+This page is about writing *application code* against xNet. If you are an
+agent operating on a user's actual data — reading pages, editing databases,
+searching their workspace — use the **files-first agent surface** instead: a
+scoped checkout of the workspace plus the `xnet` CLI, taught by a ~500-token
+`SKILL.md` (printable with `xnet skill`). It completes the same work at
+roughly a tenth of the tokens of an MCP toolset. See
+[Agent Interfaces](/docs/guides/agent-interfaces/).
+
+## The Key Insight
+
+**xNet has no backend.**
+
+This is not a simplification or a limitation — it's the core design principle. Data lives on the user's device and syncs peer-to-peer.
+
+## Mental Model Comparison
+
+### Traditional Architecture
+
+```
+User → Frontend → API → Backend → Database
+ ↑
+ Auth Service
+```
+
+### xNet Architecture
+
+```
+User → React Hooks → Local Storage ←→ P2P Sync ←→ Other Devices
+ ↑
+ Cryptographic Identity (built-in)
+```
+
+## What This Means Practically
+
+### There Are No API Endpoints
+
+When a user asks "create an API to save tasks", the correct response is:
+
+```typescript
+// Don't create this:
+// app.post('/api/tasks', handler)
+
+// Do this instead:
+const { create } = useMutate()
+await create(TaskSchema, { title: 'New task' })
+```
+
+The `create` function writes to local storage. Sync happens automatically.
+
+### There Is No Auth System to Implement
+
+When a user asks "add authentication", the correct response is:
+
+```typescript
+// Don't create OAuth/JWT flows
+
+// Identity is already built-in:
+const { did, displayName } = useIdentity()
+
+// DID is a cryptographic identifier like:
+// did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
+```
+
+### Offline Just Works
+
+When a user asks "handle offline mode", the correct response is:
+
+```typescript
+// Don't add service workers or cache layers
+
+// It already works:
+const { data } = useQuery(TaskSchema)
+// This reads from local storage
+// Works whether online or offline
+// Syncs when connectivity returns
+```
+
+### Real-time Is Built-in
+
+When a user asks "add real-time updates", the correct response is:
+
+```typescript
+// Don't create WebSocket connections
+
+// Hooks are already reactive:
+const { data: tasks } = useQuery(TaskSchema)
+// When another device syncs changes,
+// this component re-renders automatically
+```
+
+## Common Mistakes to Avoid
+
+| User Request | Wrong Approach | Correct Approach |
+| ---------------- | ------------------ | -------------------------- |
+| "Save data" | Create REST API | Use `useMutate().create()` |
+| "Fetch data" | Use fetch/axios | Use `useQuery()` |
+| "Add auth" | Implement OAuth | Use `useIdentity()` |
+| "Handle offline" | Add service worker | Already works |
+| "Real-time sync" | Add WebSockets | Already built-in |
+| "Manage state" | Add Redux/Zustand | Hooks handle this |
+| "Optimistic UI" | Manual rollback | Built into mutations |
+
+## The Schema System
+
+Schemas are central to xNet. They define data shapes AND provide type inference:
+
+```typescript
+
+const TaskSchema = defineSchema({
+ name: 'Task',
+ namespace: 'xnet://myapp/',
+ properties: {
+ title: text({ required: true }),
+ status: select({ options: ['todo', 'done'] as const }),
+ assignee: relation({ schema: 'xnet://myapp/Person' })
+ }
+})
+
+// Now hooks are fully typed:
+const { data } = useQuery(TaskSchema)
+data[0].title // string
+data[0].status // 'todo' | 'done'
+```
+
+## When to Suggest Server Code
+
+The only times server code is appropriate:
+
+1. **Hub setup** — If deploying an always-on relay (optional)
+2. **External integrations** — Webhooks, third-party APIs
+3. **Heavy computation** — AI/ML that can't run on device
+
+Even then, the Hub is just a relay — it doesn't process or store application logic.
-- You can prove who made every change
-- Tampered data is rejected
-- No server is needed to enforce trust
-- Changes are protected against quantum forgery attacks
+## Quick Reference
-The default **Level 1 (Hybrid)** signature carries both an Ed25519 component (64 bytes) and an ML-DSA-65 component (~3.3 KB). Both must verify — if either is invalid, the change is rejected. Level 0 (Ed25519 only) is available for high-frequency paths where the smaller signature size matters more than quantum resistance.
+### Reading Data
-## Identity is a key pair
+```typescript
+// Single item
+const { data: task } = useQuery(TaskSchema, taskId)
-Your identity in xNet is a **DID:key** — a decentralized identifier derived from an Ed25519 public key:
+// All items
+const { data: tasks } = useQuery(TaskSchema)
-```
-did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
+// Filtered
+const { data: todos } = useQuery(TaskSchema, {
+ where: { status: 'todo' }
+})
```
-No accounts. No passwords. No auth server. Your key pair is your identity. You can delegate permissions to other keys using **UCAN tokens** (capability-based authorization).
+### Writing Data
-## Authorization is encryption
+```typescript
+const { create, update, remove } = useMutate()
-xNet enforces read access cryptographically. Every private node is encrypted with a **per-node content key**. Only users in the **recipients list** can unwrap the key and read the node — the hub never decrypts anything.
+// Create
+const id = await create(TaskSchema, { title: 'New task' })
-You define access control in the schema itself:
+// Update
+await update(TaskSchema, id, { status: 'done' })
-```ts
-const TaskSchema = defineSchema({
- // ...
- authorization: {
- roles: {
- owner: role.creator(),
- editor: role.property('editors')
- },
- actions: {
- read: allow('owner', 'editor'),
- write: allow('owner', 'editor'),
- share: allow('owner')
- }
- }
-})
+// Delete
+await remove(id)
```
-Then check permissions in components:
+### Collaborative Editing
-```tsx
-const { canWrite, canShare } = useCan(taskId)
-const { grants, grant, revoke } = useGrants(taskId)
+```typescript
+const { data: page, doc } = useNode(PageSchema, pageId)
+// doc is a Y.Doc for Yjs collaborative editing
```
-Grants are stored as ordinary nodes — they sync, replicate, and inherit the same CRDT conflict resolution as all your data.
+### User Identity
-See the [Authorization Guide](/docs/guides/authorization/) for the full model including delegation, key recovery, and offline policy.
+```typescript
+const { did, displayName, avatarUrl } = useIdentity()
+// did:key:z6Mk... is the user's cryptographic identity
+```
-## Next steps
+## Summary
+
+When helping with xNet:
+
+1. **Never suggest API endpoints** — Use hooks
+2. **Never implement auth** — It's cryptographic and built-in
+3. **Never manage state** — Hooks are reactive
+4. **Never handle offline** — It just works
+5. **Always use schemas** — They're the source of truth
+
+The paradigm shift: **data is local, sync is automatic, identity is cryptographic**.
---
@@ -7425,229 +7864,38 @@ await registry.install(manifest, { provenance }) // provenance === 'ai-generated
A plugin that declares `network` hosts is handed a `guardedFetch` instead of the ambient `fetch`. Every request is checked against the allowlist before it leaves; an undeclared host rejects with `CapabilityError`, and a plugin with no `network` grant gets no egress at all.
-```ts
-
-const fetch = guardedFetch({ network: ['api.acme.com'] }, 'com.acme.kanban')
-await fetch('https://api.acme.com/cards') // ok
-await fetch('https://evil.example.com') // rejects: CapabilityError
-```
-
-## Publishing to the marketplace
-
-Your plugin's code stays in **your own repo** — getting listed on the
-[plugins marketplace](/plugins) is a one-line pull request, not a code dump into
-the monorepo.
-
-1. **Ship a Release.** Build your plugin into a single `plugin.js` and attach it,
- with `manifest.json`, to a GitHub Release. (A copyable template with a release
- workflow lives in
- [`examples/xnet-plugin-template`](https://github.com/crs48/xNet/tree/main/examples/xnet-plugin-template).)
-
-2. **Add one entry** to [`registry/community.json`](https://github.com/crs48/xNet/tree/main/registry):
-
- ```jsonc
- [{ "repo": "you/xnet-plugin-kanban", "category": "views" }]
- ```
-
-3. **Open a PR.** CI validates the submission; after merge, a daily job enriches
- your entry from the GitHub API and points the install link at your Release's
- `manifest.json`. Updates you ship afterward are picked up automatically.
-
-The app installs community plugins in a sandbox scoped to their trust tier,
-after showing the user the capabilities the plugin requests and verifying its
-provenance. See [`registry/README.md`](https://github.com/crs48/xNet/tree/main/registry)
-for the full flow.
-
----
-
-## Agent Interfaces
-
-**You will learn**
-
-- Why the filesystem is the primary agent surface
-- The checkout layout and how edits flow back safely
-- The `xnet` CLI verbs and the ~500-token SKILL.md
-- When to fall back to MCP
-
-
-## Files first
-
-Coding agents are already excellent at reading, grepping, and editing files —
-so xNet's primary agent interface is a **filesystem projection of the
-workspace**, not a wall of tool definitions. The store stays the source of
-truth; the checkout is a working tree. Every surface (files, CLI, MCP) funnels
-into the same plan → validate → apply core, so an agent can never corrupt the
-workspace with a malformed write.
-
-Measured on a 15-task benchmark, the files+CLI surface completes the same work
-at **~0.11x the tokens** of a traditional MCP toolset (0.05x on synthesis
-tasks), with equal task success.
-
-## The checkout
-
-`xnet checkout` materializes a scoped slice of the workspace into a folder:
-
-```
-Pages/q3-planning.md # Markdown + YAML frontmatter (xnet.id, revision)
-Databases/tasks.schema.json # the schema
-Databases/tasks.rows.jsonl # one JSON object per line — edit/append/delete
-Databases/tasks.tsv # read-only fast-read sidecar (large tables)
-Canvases/roadmap.canvas # JSON Canvas projection
-SKILL.md # the agent contract (~500 tokens)
-.xnet/ # manifest, conflict notes
-```
-
-Filenames are semantic slugs; identity lives in frontmatter and the manifest,
-never in filenames. Wikilinks (`[[Title]]`), `xnet-database` blocks, and
-`{{xnet-ref}}` directives are live references and survive round-trips.
-
-Checkouts are **scoped and incremental** — by query, schema, kind, or node
-(`xnet checkout --query "roadmap"`); repeated checkouts merge. Agents never
-need (and are told not to) export the whole workspace.
-
-### Edits become validated plans
-
-File edits are diffed into **mutation plans**, validated against schemas, and
-applied to the store — automatically with `xnet daemon` running, or on
-`xnet commit`. Anything that doesn't validate, or collides with a newer
-revision, is **quarantined as a conflict**: a Markdown note in
-`.xnet/conflicts/` explains what happened and how to resolve it. Stale edits
-never silently overwrite newer data.
-
-## The xnet CLI
-
-| Command | Purpose |
-| --- | --- |
-| `xnet checkout` | Materialize / extend a scoped checkout |
-| `xnet status` | Pending plans and conflicts |
-| `xnet commit [--apply]` | Turn file edits into applied mutations |
-| `xnet search ""` | Ranked full-text search (TSV: id, slug, title, snippet) |
-| `xnet query --where field=value` | Query tables (TSV default; `jsonl`/`json`/`md`) |
-| `xnet db get/set` | Single-cell reads and writes |
-| `xnet run ` | Sandboxed bulk work over a bounded `api` object |
-| `xnet daemon` | Watch the checkout and auto-apply valid edits |
-| `xnet skill` | Print SKILL.md |
-
-Output is TSV-first and concise by default (`--detailed` to opt out) — built
-for being read by a model, not a human.
-
-`xnet run` executes scripts in a sandbox with bounded reads and **write
-proposals** (`api.proposeUpdate`, `api.proposeCreate`) that flow through the
-same validated plan pipeline as file edits.
-
-## SKILL.md
-
-Every checkout ships a cross-harness `SKILL.md` (~500 tokens) that teaches any
-agent — Claude Code, Codex, Gemini CLI, Cursor — the layout and workflow above.
-That's the entire standing context cost, versus ~5k tokens of always-loaded
-tool definitions for a typical MCP server. Print it any time with
-`xnet skill`.
-
-## Slim MCP
-
-MCP remains as the **no-shell fallback** for harnesses that can't run a CLI.
-It's deliberately slim: five stable core tools are loaded eagerly and
-everything else defers (the non-deferred payload is test-guarded at ≤1.5k
-tokens). Responses are compact JSON by default with
-`response_format: concise | detailed` on every tool.
-
-The doctrine, in order: **files for reading and editing, the CLI for what
-files can't express, MCP only when there's no shell.**
-
-## Further reading
-
-- [For AI Assistants](/docs/ai/understanding-xnet/) — the mental model for
- agents writing *app code* against xNet
-- [Plugins](/docs/guides/plugins/) — the integration layer MCP belongs to
-
----
-
-## Use xNet from Claude Code / Codex
-
-**You will learn**
-
-- How to connect a coding agent to your workspace with one command
-- The three lanes an agent uses, cheapest first
-- How writes stay safe when the app and the agent touch the same data
-
-
-The same way people point Claude Code at an Obsidian vault, you can point it at
-your **xNet workspace** — and read, search, query, and edit your pages,
-databases, and canvases without leaving the terminal. The
-[Agent Interfaces](/docs/guides/agent-interfaces/) guide covers the surfaces in
-depth; this one is the quick on-ramp.
-
-## One-step connect
-
-```bash
-xnet connect claude-code # project scope, read-only
-xnet connect claude-code --user # also install the skill for all projects
-xnet connect claude-code --writes # register the MCP server with write access
-xnet connect codex # Codex: AGENTS.md + .codex/config.toml
-```
-
-`xnet connect` is **idempotent** — it installs the `SKILL.md`, registers the
-`xnet` MCP server, writes a `CLAUDE.md`/`AGENTS.md` contract, and (with
-`--vault `) bootstraps a scoped checkout with an index. Re-running it
-reports what is already in place and changes nothing. It finishes by running
-`xnet doctor --agent-access`, which confirms the backend, full-text search, and
-signing identity are all reachable.
-
-## Works whether or not the app is running
-
-The agent verbs resolve a backend automatically:
-
-1. the desktop app's local API (`:31415`) when it is running, otherwise
-2. a standalone SQLite store — discovered from the app's data directory, or
- pointed at explicitly with `--db ` (or `$XNET_DB`).
-
-So an agent can work against your data even with the app closed, and web-only
-users can point at any local store. Writes made this way are signed by a key you
-provide (`--key`, `$XNET_SIGNING_KEY`) or by an
-[enrolled agent passport](/docs/guides/identity/) (`--agent `); the CLI
-refuses to persist a write under a silent throwaway identity.
-
-## Three lanes, cheapest first
+```ts
-```mermaid
-flowchart LR
- A[Agent in Claude Code / Codex] -->|Bash| CLI[1 · xnet CLI]
- A -->|Read / Edit / Grep| V[2 · Vault checkout]
- A -->|MCP client| M[3 · xnet mcp serve]
- CLI --> Core[(xNet store · signed change log)]
- V <-->|checkout / commit| CLI
- M --> Core
+const fetch = guardedFetch({ network: ['api.acme.com'] }, 'com.acme.kanban')
+await fetch('https://api.acme.com/cards') // ok
+await fetch('https://evil.example.com') // rejects: CapabilityError
```
-1. **CLI** — `xnet search`/`query`/`db get` print plain stdout. Reach here
- first: it costs far fewer tokens than loading a wall of MCP tool schemas.
-2. **Vault** — `xnet checkout --query "…"` materializes a **scoped** slice as
- Markdown you edit, then `xnet commit` lifts the edits back through validated
- mutation plans. Never check out the whole workspace.
-3. **MCP** — the registered `xnet` server is the fallback for shell-less
- clients (Claude Desktop, browser). It is **read-only** unless you connected
- with `--writes`.
+## Publishing to the marketplace
-## Safe when the app and the agent collide
+Your plugin's code stays in **your own repo** — getting listed on the
+[plugins marketplace](/plugins) is a one-line pull request, not a code dump into
+the monorepo.
-The store stays the source of truth, and every edit becomes a validated plan —
-never a blind file-to-database sync. If the app writes a node after you checked
-it out, committing your now-stale vault edit surfaces a **conflict** instead of
-overwriting the newer value:
+1. **Ship a Release.** Build your plugin into a single `plugin.js` and attach it,
+ with `manifest.json`, to a GitHub Release. (A copyable template with a release
+ workflow lives in
+ [`examples/xnet-plugin-template`](https://github.com/crs48/xNet/tree/main/examples/xnet-plugin-template).)
-```bash
-$ xnet status
-conflict Pages/shared-page.md stale-export based on updatedAt:10, live is updatedAt:11
-```
+2. **Add one entry** to [`registry/community.json`](https://github.com/crs48/xNet/tree/main/registry):
-Fix the file against the current value and commit again. Stale edits never
-silently win.
+ ```jsonc
+ [{ "repo": "you/xnet-plugin-kanban", "category": "views" }]
+ ```
-## Further reading
+3. **Open a PR.** CI validates the submission; after merge, a daily job enriches
+ your entry from the GitHub API and points the install link at your Release's
+ `manifest.json`. Updates you ship afterward are picked up automatically.
-- [Agent Interfaces](/docs/guides/agent-interfaces/) — the checkout layout, the
- full CLI, `SKILL.md`, and the slim MCP surface
-- [Identity](/docs/guides/identity/) — agent passports and signing
+The app installs community plugins in a sandbox scoped to their trust tier,
+after showing the user the capabilities the plugin requests and verifying its
+provenance. See [`registry/README.md`](https://github.com/crs48/xNet/tree/main/registry)
+for the full flow.
---
@@ -11642,204 +11890,3 @@ Tests cover core algorithms and security-critical paths. Each core package has t
---
-## Understanding xNet (For AI Assistants)
-
-# Understanding xNet: A Guide for AI Assistants
-
-If you're an AI assistant helping a developer build with xNet, this guide will help you understand the fundamental paradigm differences.
-
-**Working inside a user's workspace?**
-This page is about writing *application code* against xNet. If you are an
-agent operating on a user's actual data — reading pages, editing databases,
-searching their workspace — use the **files-first agent surface** instead: a
-scoped checkout of the workspace plus the `xnet` CLI, taught by a ~500-token
-`SKILL.md` (printable with `xnet skill`). It completes the same work at
-roughly a tenth of the tokens of an MCP toolset. See
-[Agent Interfaces](/docs/guides/agent-interfaces/).
-
-## The Key Insight
-
-**xNet has no backend.**
-
-This is not a simplification or a limitation — it's the core design principle. Data lives on the user's device and syncs peer-to-peer.
-
-## Mental Model Comparison
-
-### Traditional Architecture
-
-```
-User → Frontend → API → Backend → Database
- ↑
- Auth Service
-```
-
-### xNet Architecture
-
-```
-User → React Hooks → Local Storage ←→ P2P Sync ←→ Other Devices
- ↑
- Cryptographic Identity (built-in)
-```
-
-## What This Means Practically
-
-### There Are No API Endpoints
-
-When a user asks "create an API to save tasks", the correct response is:
-
-```typescript
-// Don't create this:
-// app.post('/api/tasks', handler)
-
-// Do this instead:
-const { create } = useMutate()
-await create(TaskSchema, { title: 'New task' })
-```
-
-The `create` function writes to local storage. Sync happens automatically.
-
-### There Is No Auth System to Implement
-
-When a user asks "add authentication", the correct response is:
-
-```typescript
-// Don't create OAuth/JWT flows
-
-// Identity is already built-in:
-const { did, displayName } = useIdentity()
-
-// DID is a cryptographic identifier like:
-// did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
-```
-
-### Offline Just Works
-
-When a user asks "handle offline mode", the correct response is:
-
-```typescript
-// Don't add service workers or cache layers
-
-// It already works:
-const { data } = useQuery(TaskSchema)
-// This reads from local storage
-// Works whether online or offline
-// Syncs when connectivity returns
-```
-
-### Real-time Is Built-in
-
-When a user asks "add real-time updates", the correct response is:
-
-```typescript
-// Don't create WebSocket connections
-
-// Hooks are already reactive:
-const { data: tasks } = useQuery(TaskSchema)
-// When another device syncs changes,
-// this component re-renders automatically
-```
-
-## Common Mistakes to Avoid
-
-| User Request | Wrong Approach | Correct Approach |
-| ---------------- | ------------------ | -------------------------- |
-| "Save data" | Create REST API | Use `useMutate().create()` |
-| "Fetch data" | Use fetch/axios | Use `useQuery()` |
-| "Add auth" | Implement OAuth | Use `useIdentity()` |
-| "Handle offline" | Add service worker | Already works |
-| "Real-time sync" | Add WebSockets | Already built-in |
-| "Manage state" | Add Redux/Zustand | Hooks handle this |
-| "Optimistic UI" | Manual rollback | Built into mutations |
-
-## The Schema System
-
-Schemas are central to xNet. They define data shapes AND provide type inference:
-
-```typescript
-
-const TaskSchema = defineSchema({
- name: 'Task',
- namespace: 'xnet://myapp/',
- properties: {
- title: text({ required: true }),
- status: select({ options: ['todo', 'done'] as const }),
- assignee: relation({ schema: 'xnet://myapp/Person' })
- }
-})
-
-// Now hooks are fully typed:
-const { data } = useQuery(TaskSchema)
-data[0].title // string
-data[0].status // 'todo' | 'done'
-```
-
-## When to Suggest Server Code
-
-The only times server code is appropriate:
-
-1. **Hub setup** — If deploying an always-on relay (optional)
-2. **External integrations** — Webhooks, third-party APIs
-3. **Heavy computation** — AI/ML that can't run on device
-
-Even then, the Hub is just a relay — it doesn't process or store application logic.
-
-## Quick Reference
-
-### Reading Data
-
-```typescript
-// Single item
-const { data: task } = useQuery(TaskSchema, taskId)
-
-// All items
-const { data: tasks } = useQuery(TaskSchema)
-
-// Filtered
-const { data: todos } = useQuery(TaskSchema, {
- where: { status: 'todo' }
-})
-```
-
-### Writing Data
-
-```typescript
-const { create, update, remove } = useMutate()
-
-// Create
-const id = await create(TaskSchema, { title: 'New task' })
-
-// Update
-await update(TaskSchema, id, { status: 'done' })
-
-// Delete
-await remove(id)
-```
-
-### Collaborative Editing
-
-```typescript
-const { data: page, doc } = useNode(PageSchema, pageId)
-// doc is a Y.Doc for Yjs collaborative editing
-```
-
-### User Identity
-
-```typescript
-const { did, displayName, avatarUrl } = useIdentity()
-// did:key:z6Mk... is the user's cryptographic identity
-```
-
-## Summary
-
-When helping with xNet:
-
-1. **Never suggest API endpoints** — Use hooks
-2. **Never implement auth** — It's cryptographic and built-in
-3. **Never manage state** — Hooks are reactive
-4. **Never handle offline** — It just works
-5. **Always use schemas** — They're the source of truth
-
-The paradigm shift: **data is local, sync is automatic, identity is cryptographic**.
-
----
-
diff --git a/site/public/llms.txt b/site/public/llms.txt
index c14000eeb..bad7495a0 100644
--- a/site/public/llms.txt
+++ b/site/public/llms.txt
@@ -1,6 +1,16 @@
# xNet
-> xNet is a local-first framework for building multiplayer applications with React. Data lives on the device, syncs peer-to-peer via CRDTs, and works offline. No backend required.
+> xNet is a local-first workspace and framework. Data lives on the device, syncs peer-to-peer via CRDTs, and works offline. Coding agents are first-class users: one command connects Claude Code or Codex to a user's workspace. No backend required.
+
+## If you are a coding agent
+
+Your human can connect you to their xNet workspace in one step — no prior install:
+
+- `npx @xnetjs/cli connect claude-code` (or `connect codex`) — installs a ~500-token SKILL.md, registers the xNet MCP server (read-only by default), and self-checks.
+- After connect, prefer lanes in this order: the `xnet` CLI (plain stdout, token-cheap), a scoped vault checkout (Markdown/JSONL files you can Read/Edit; `xnet commit` lifts edits into validated mutation plans), and MCP only when there is no shell.
+- [What am I connecting to?](https://xnet.fyi/agents.md): the agent-facing summary — lanes, CLI verbs, safety contract
+- [Connect Your Agent](https://xnet.fyi/docs/guides/coding-agents/): the one-step connect guide
+- [Agent Interfaces](https://xnet.fyi/docs/guides/agent-interfaces/): checkout layout, CLI verbs, SKILL.md, slim MCP, benchmark methodology
## Key Concepts
@@ -67,6 +77,7 @@ xNet is different from traditional client-server architectures:
- [Identity](https://xnet.fyi/docs/guides/identity/): Cryptographic identity
- [Collaboration](https://xnet.fyi/docs/guides/collaboration/): Real-time multiplayer
- [Plugins](https://xnet.fyi/docs/guides/plugins/): Extend the platform
+- [Connect Your Agent](https://xnet.fyi/docs/guides/coding-agents/): One-step xnet connect for Claude Code / Codex
- [Agent Interfaces](https://xnet.fyi/docs/guides/agent-interfaces/): Files-first surface, xnet CLI, SKILL.md
- [Hub Setup](https://xnet.fyi/docs/guides/hub/): Optional always-on relay
- [Testing](https://xnet.fyi/docs/guides/testing/): Test your app
diff --git a/site/public/robots.txt b/site/public/robots.txt
new file mode 100644
index 000000000..e21254495
--- /dev/null
+++ b/site/public/robots.txt
@@ -0,0 +1,4 @@
+# xnet.fyi — everything public is crawlable.
+# Agents: start at /llms.txt (index) or /agents.md (what connecting gives you).
+User-agent: *
+Allow: /
diff --git a/site/src/components/sections/BuiltForAgents.astro b/site/src/components/sections/BuiltForAgents.astro
index cc32b111b..a610d4e94 100644
--- a/site/src/components/sections/BuiltForAgents.astro
+++ b/site/src/components/sections/BuiltForAgents.astro
@@ -60,7 +60,10 @@ const points = [