InfrastructureHorizon 3 · Enable emergent governance

Local Assembly

Each party interprets its own material and answers questions from other parties’ agents. No central interpreter.

Overview

Status
Designed, partly prototyped
COMMUNITY OWNER · home networkOwner’s vaultstays homeManifest scannertagged files onlyOwner’s AIhome networkMEMBER · own networkMember’s AIvia TailscaleMember’s scannersame jobMember’s vaultundecidedPRIVATEFull manifestscope undecidedMCP ZONE · community server, always onOwner’sredactedmanifestid · dateOwner’s cuts7 · for MemberMCP serveron the hostMember’s cutsfor Owner3 · Asktoken + id4 · Compareold vs new4 · Filterto the asker5 · Answerover MCP6 · BaselinesavedVault Viewerfor bothMember’sredactedmanifestid · dateMCP TOOLSlist_changessince a datelist_principleswith file countsget_principle_activityone over timeTHE EDGERouterto the outsideToken onevery arrow2 · file sync1 · recordshome network3 ask · 5 answer8 · mirror of 1COMMUNITY DATA COMMONSPool of shapesid · time · principle9 · opt in9 · shape onlyCommunity OwnerMemberCommonsInfrastructureSolid arrow: data moves · Dashed arrow: not built · Dashed outline: privateNothing here is built yet.
Redrawn from the MVP 2 architecture board. The numbers follow one round trip; the key sits underneath. Click to open full screen and zoom.
MEMBER’S MACHINECOMMUNITY SERVEROWNER’S MACHINEMember’s agenton their machinewith their own vaultMCP doorchecks token andidentity on every callCommunity agentthe community’s ownedge, on the serverOwner’s agenton their machinewith their own vault1 · connects: token + “I’m a member”2 · checks both3 · which tools are there?list_changes · list_principlesget_principle_activity4 · calls a tool, or asks a question5 · applies visibilityreads manifests, cutsand the commons only6 · asks the owner7 · answers from itsown vault, under itsown visibility8 · scoped report9 · scoped answer, plus tools or cuts it may use10 · composesits own viewWHO DOES WHATMEMBER’S AGENTAsks, calls tools and builds itsown view from the answers. Seesonly what visibility allows.COMMUNITY AGENTApplies the visibility rules, answersfrom manifests and the commons, andhands over tools and cuts. Neveropens a member’s vault.OWNER’S AGENTInterprets its own material andanswers other agents’ questions.Meaning resolves where it lives.Dashed: designed, not built. Steps 6 to 8 are Local Assembly’s third pass. Nothing here is built yet.
Whose agent does what in one request. Steps 6 to 8 run only when the owner’s agent is reachable. Click to open full screen and zoom.
EDGE A · vault + agentPass 1 · selfinterprets its own contributionPass 2 · presencenotes that others contributedPass 3 · meaningasks the edge that can interpretEDGE B · vault + agentIts own vaultinterprets its own materialVisibility applied firstmanifest hand-off, scopedAnswers the queryreturns a scoped reportmanifestcross-edge queryscoped answerRelational spacecomposes the scoped views. No new store.With no reachable agent, assembly stops at Pass 2. Self pass alone runs without federation.
Three passes across two edges. Dashed lines are specified, not built. Click to open full screen and zoom.

How Local Assembly works

Edge
Holds a vault plus an agent
Visibility first
A manifest hand-off exposes a scoped projection
Pass 1, self
The edge interprets its own contribution
Pass 2, presence
The edge notes that others contributed
Pass 3, meaning
A cross-edge query to the edge that can interpret
Scoped report
The answer returns as one; the relational space composes the views

Settled

Meaning resolves where it lives
Not in an aggregate
No new store
It reads existing spec files
Neither fabricates
The witness holds the occurrence; the contributor holds the meaning
Self pass alone
The minimum, and runs without federation
No reachable agent
Assembly stops at presence

Start here

The build guide for the first version of Local Assembly. Nothing below is built yet. Find your role, then work down its checklist.

Host · runs the community server
The Host checklist. Also read Before you start and Build order.
Community Owner · shares their work
The Community Owner checklist.
Member · reads what was shared
The Member checklist, then Client setup.
Everyone
What done looks like is the test the whole build has to pass.

What done looks like

One tagged file, through five parts of the spec.

  1. Manifest mapping

    Your scanner writes each tagged file down as a number and a date. The file stays home.

    See spec
  2. Cuts and publishing

    To share the work itself, cut a page for someone. A separate act from the manifest.

    See spec
  3. Visibility system

    One visibility list per file decides which manifest and which cut it lands in.

    No spec yet
  4. Community Data Commons MCP

    An always-on server answers a member’s AI with only the ids and dates they may see.

    Scroll to section
  5. Activity log data

    Your record and the community’s overlap. The overlap proves you took part.

    See spec
Prompt for your agent
I'm building Local Assembly. Write me an end-to-end acceptance test for it.

The test:
1. A file in the Community Owner's vault gets member-b added to the visibility list in its frontmatter.
2. Their scanner writes member-b-manifest.json and syncs it to the community server.
3. The Member's AI calls list_changes over MCP, sending the community token and the identity member-b.
4. The answer contains that file's file_number and last_updated, and no names, paths or content.

Give me the steps as a script I can run, and tell me what each step prints when it passes.

Before you start

Install these first. Each one has a check.

Tailscale · every machine
The community server and each member’s machine run Tailscale and are signed in. Check: the command below lists the machine as connected.
tailscale status
Node.js 22.19 or newer · the host
The MCP Inspector, used to test the server, needs it. Check: the version prints 22.19 or higher.
node --version
Claude Desktop · each member
The AI client used in Client setup. Another MCP client works if it can send headers.
A port · the host
Pick one port for the MCP server and use it everywhere this page says <port>.
Prompt for your agent
Check that my machine is ready for Local Assembly. I am the <host, Community Owner or Member>.

Confirm:
1. Tailscale is installed and signed in (tailscale status).
2. Node.js is 22.19 or newer (node --version), if I am the host.
3. Claude Desktop is installed, if I am a member.

Tell me exactly what to install for anything that fails.

Build order

Four phases. Each one is a working system on its own.

Phase 1 · Local
Scanner and MCP server on the Community Owner’s machine, over stdio. Done when they query it from their own AI as if they were the Member.
Phase 2 · On the server
Host checklist steps 1 to 4. Done when the Community Owner queries the server over their own network.
Phase 3 · Shared
Host checklist steps 5 and 6. Done when the Member gets the Community Owner’s ids and dates back.
Phase 4 · The Member’s half
The Member runs a scanner on their own vault and shares their node back. Two manifests, two tailnets, never merged. The cross-reference is the dual ledger. find_overlap lands here.
Prompt for your agent
Plan Local Assembly in four phases. Each phase is a working system on its own:
1. Scanner and MCP server, local, over stdio.
2. MCP server on the community server, with manifest sync.
3. Tailscale share and the first remote query.
4. A second member's scanner, and a find_overlap tool.

For the phase I name, list the tasks, who does each (host, the Community Owner or the Member), and the check that proves the phase is done.

Phase: <number>

What runs where

Seven pieces. Each one runs in one place.

Scanner · each member’s machine
Walks the vault, reads frontmatter, keeps the files tagged for the community, and writes the redacted manifest and the full manifest. The only part that touches real files.
Community server · always on
Holds each member’s redacted and full manifest, plus the two cuts folders. Needs a long-running Node.js or Python 3 process and a path for manifests to land.
MCP server · on the community server
Reads the manifest on each request and answers. Never reads a member’s vault. Runs as a service that survives reboots.
Community agent · on the community server
Answers a member’s agent within the visibility rules, and can hand over tools and cuts that member may use. Part of the first build; its contract is still open. See The community agent, below.
Cuts folders · on the community server
One drop folder each way: the Community Owner’s cuts for the Member, the Member’s cuts for the Community Owner. A cut is how readable work travels; see Cuts and lenses.
Tailscale node share · between networks
Shares the server with the Member as a single node. They reach the MCP endpoint and nothing else on the machine or the network.
AI client · each member’s machine
claude_desktop_config.json points at the server’s MCP endpoint and carries the community token and the identity.
Prompt for your agent
Here are the six parts of Local Assembly and where each runs:
- Scanner: each member's machine
- Community server: always on
- MCP server: on the community server
- Cuts folders: on the community server
- Tailscale node share: between networks
- AI client: each member's machine

For the part I name, list what it needs installed, what it reads, what it writes, and how I confirm it is running.

Part: <name>

Host checklist

Do these in order. Each step ends in a check. Angle brackets mark values you choose.

1 · Runtime and folder
Confirm the server can run a long-lived Node.js or Python 3 process. Make one folder for manifests to land in. Check: one of these prints a version.
node --version
python3 --version
2 · The MCP server
Write a server that exposes the three tools under The MCP tools and reads the manifest file fresh on every call. Serve it over Streamable HTTP so other machines can reach it. Reject any call that is missing the token or the identity. Check: the MCP Inspector connects and lists three tools.
npx @modelcontextprotocol/inspector --server-url http://<server>:<port>/mcp --transport http
3 · Run it as a service
It has to survive reboots. On Linux that is a systemd unit. Check: after a reboot, the status shows it active.
[Unit]
Description=Community MCP server
After=network-online.target

[Service]
ExecStart=<command that starts the server>
User=<service user>
Restart=always

[Install]
WantedBy=multi-user.target
4 · Sync target
Give the Community Owner a way to push their manifest in. The v1 default is rsync over ssh: add their ssh key for a user that can write only to the manifest folder. Check: after they save a tagged file, the manifest’s emitted_at changes.
rsync -av member-b-manifest.json <user>@<server>:<manifest folder>/
5 · Share to the Member
In the Tailscale admin console, open Machines, pick the server, choose Share and invite the Member. A shared machine is quarantined: it takes incoming connections and cannot start them. Limit shared users to the MCP port in the policy file. Check: from the Member’s machine, the MCP port answers and nothing else does.
"grants": [
  {
    "src": ["autogroup:shared"],
    "dst": ["*"],
    "ip": ["<port>"]
  }
]
6 · First query
The Member adds the endpoint (see Client setup) and asks what the Community Owner changed. Check: the answer lists ids and dates, and nothing else.
Prompt for your agent
Walk me through standing up the community server for Local Assembly, one step at a time. Wait for my result before moving to the next step.

1. Confirm Node.js or Python 3 is installed, and make one folder for manifests.
2. Write an MCP server that serves three tools (list_changes, list_principles, get_principle_activity) over Streamable HTTP. It reads the manifest JSON fresh on every call and rejects any call missing the community token or the member identity.
3. Run it as a systemd service that survives reboots.
4. Make an ssh user that can write only to the manifest folder, for rsync.
5. Share the machine to the Member in Tailscale, and limit shared users to the MCP port with an autogroup:shared grant.
6. Run the first query.

Before step 1, ask me which OS the server runs and which language I want.

Community Owner checklist

Do these in order, after the host has finished step 4.

1 · Tag a file
Add member-b to the visibility list in a file’s frontmatter. Check: the file’s frontmatter shows the tag.
---
visibility: [local, member-b]
---
2 · Build the scanner
Use the prompt under The manifest file. Check: it writes member-b-manifest.json with version, emitted_at, source, filter and files.
3 · Run it
Run the scanner after you save a tagged file. Check: emitted_at in the manifest is the time you ran it.
4 · Push it to the server
Send the manifest with the ssh key the host set up. Check: the server’s copy has the same emitted_at as yours.
rsync -av member-b-manifest.json <user>@<server>:<manifest folder>/
Prompt for your agent
I am the Community Owner for Local Assembly. Walk me through my half one step at a time, and wait for my result before the next step.

1. Tag a file: add member-b to the visibility list in its frontmatter.
2. Build the manifest scanner. Ask me which language first.
3. Run it and check member-b-manifest.json has version, emitted_at, source, filter and files.
4. Push it to the community server with rsync over ssh, using the key the host set up.
5. Confirm the server copy has the same emitted_at as mine.

Member checklist

Do these in order, after the host has finished step 5.

1 · Accept the share
Accept the Tailscale machine share the host sent. Check: tailscale status lists the community server.
2 · Get the token
Ask the host for the community token and your member name. How tokens are handed out is still open.
3 · Connect your AI client
Follow Client setup, then restart the client. Check: three tools appear: list_changes, list_principles and get_principle_activity.
4 · Ask what changed
Ask your AI what the Community Owner changed in the last 7 days. Check: the answer has ids and dates, and nothing else.
Prompt for your agent
I am a Member joining a Local Assembly community server. Walk me through it one step at a time, and wait for my result before the next step.

1. Accept the Tailscale machine share I was sent, and confirm the server shows in tailscale status.
2. Add the server to Claude Desktop with mcp-remote. I will give you the URL, the token and my member name.
3. Restart, and confirm three tools appear: list_changes, list_principles, get_principle_activity.
4. Ask what changed in the last 7 days, and check the answer has only ids and dates.

The community agent

Part of the first build. Its contract is still open.

Where it runs
On the community server, behind the MCP door.
What it does
Answers a member’s agent within the visibility rules. Reads the redacted manifests, the cuts folders and the commons pool. Hands over tools and cuts the member may use.
What it never does
Open a member’s vault, or interpret a member’s meaning.
When it needs meaning
It asks that member’s own agent: a cross-edge query, Local Assembly’s third pass. Designed, not built.
Prompt for your agent
Help me design the community agent for a Local Assembly server.

It runs on the community server, behind the MCP endpoint. It answers a member's AI within the visibility rules, reads only the redacted manifests, the cuts folders and the commons pool, and can hand the member tools or cuts they are allowed to use. It never opens a member's vault and never interprets a member's meaning. When it needs meaning, it asks that member's own agent.

Draft its contract: the requests it accepts, what it reads, what it may hand over, and what it must refuse. Mark every assumption you make.

The manifest file

One JSON file per visibility tag. Format v0.1.

File name
member-b-manifest.json for the member-b tag. The server reads it from the folder the host made in step 1.
version
Format version, starting at "0.1".
emitted_at
When the scanner wrote the file, as an ISO 8601 timestamp.
source
Whose vault it describes, for example "owner".
filter
The visibility tag used to select files, for example "member-b". The same scanner writes one file per tag.
files[].file_number
SHA-256 of the file’s vault-relative path, first 6 hex characters, prefixed 0x. v1 computes it every run and writes nothing back. A rename makes a new number; v2 stores a fingerprint-id in frontmatter once.
files[].last_updated
The frontmatter last-updated field, or the file’s modified time when that is missing.
files[].design_principles
The design-principles frontmatter field, joined with any design principles found in tags.
What the Member reads by default
The redacted manifest: file_number and last_updated. Anything deeper is granted per relationship.
Prompt for your agent
Write a manifest scanner for Local Assembly in <language>.

It walks a folder of markdown files, parses the YAML frontmatter, and keeps files whose visibility list contains the tag I pass, for example member-b. It writes <tag>-manifest.json with:
- version: "0.1"
- emitted_at: when it ran, ISO 8601
- source: whose vault, for example "owner"
- filter: the tag
- files: one entry per kept file, with
  - file_number: SHA-256 of the vault-relative path, first 6 hex characters, prefixed 0x
  - last_updated: the frontmatter last-updated field, else the file's modified time, ISO 8601
  - design_principles: the design-principles field, joined with any principle slugs found in tags (I'll give you the list)

It never writes back to the files. Include a test with three sample files.

The MCP tools

Three tools. Each one checks the token and the identity first, answers only from the manifest file and never opens a vault.

list_changes
What changed since a date. Input: since (string, ISO 8601). Returns: file_number, last_updated and design_principles for each file whose last_updated is after since. Whether design_principles is returned by default is still open; until it is decided, return only file_number and last_updated.
list_principles
The shape of the work without naming any of it. Input: none. Returns: each principle in the manifest, with its file count.
get_principle_activity
One principle over time. Inputs: principle (string) and window (optional, 30 days by default). Returns: last_updated and file_number for each file with that principle, sorted.
find_overlap
Later, in phase 4. Compares two members’ manifests once the Member’s exists.
Prompt for your agent
Write an MCP server in <language> with the official MCP SDK, served over Streamable HTTP.

Three tools. Each reads <tag>-manifest.json fresh on every call and reads nothing else:
- list_changes(since: ISO 8601 string): file_number, last_updated and design_principles for files whose last_updated is after since.
- list_principles(): each principle in the manifest, with its file count.
- get_principle_activity(principle: string, window: optional, 30 days by default): last_updated and file_number for files with that principle inside the window, sorted.

Before any tool runs, check the community token and the member identity, and reject the call if either is missing or wrong. Show me how to test it with the MCP Inspector.

Every request

Two checks at the door. Both ride on every call, so there is no session to keep.

Community token
Says the caller is in the community. Set in the AI client config.
Identity
Says which member: “I’m member-b”. The server serves only that member’s slice.
Why both
A shared token alone would show everyone everything. The identity does the scoping. Tailscale is the network gate; token and identity are the content gate.
How they travel
Proposed: two HTTP headers on every call, X-Community-Token and X-Member. The names are a suggestion until the host picks them.
Where the token comes from
From the host. How it is made and handed out is still open.
Prompt for your agent
Add request checks to my Local Assembly server.

Every call carries two headers: X-Community-Token (shared by members) and X-Member (which member). Reject a call that is missing either one, or whose token does not match. Use the member to choose which slice to serve. There are no sessions: each call stands on its own. Keep the token out of the logs.

Client setup

How a member points an AI client at the server.

Claude Desktop
Its config file reaches a remote server through the mcp-remote bridge. Keep header values in env: Claude Desktop on Windows mangles spaces inside args.
{
  "mcpServers": {
    "community": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://<server>:<port>/mcp",
        "--allow-http",
        "--header", "X-Community-Token:${COMMUNITY_TOKEN}",
        "--header", "X-Member:${MEMBER}"
      ],
      "env": { "COMMUNITY_TOKEN": "<token>", "MEMBER": "member-b" }
    }
  }
}
Why --allow-http
mcp-remote refuses plain HTTP to anything but the local machine unless this flag is set. It is meant for private networks, and the tailnet is one.
Where the file lives
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json. Windows: %APPDATA%\Claude\claude_desktop_config.json. Restart Claude Desktop after editing it.
Prompt for your agent
Help me connect Claude Desktop to an MCP server at http://<server>:<port>/mcp on my tailnet.

Use mcp-remote with --allow-http, and send two headers, X-Community-Token and X-Member. Keep the header values in env, not inside args. Tell me where claude_desktop_config.json lives on my OS, give me the exact block to paste, and tell me how to confirm the three tools show up after I restart.

The round trip in the diagram

The numbered steps in the first diagram, in words.

Scan
The Community Owner’s scanner sends records for tagged files to the server
Sync
Their live vault syncs to a private copy on the server, outside the MCP zone
Ask
The Member’s AI asks what changed, sending a token and “I’m member-b”
Compare and filter
The server compares the two manifests, then narrows the result to the Member
Answer
The scoped answer returns to the Member’s AI over MCP
Baseline
The redacted manifest becomes the new baseline
Cut
The Community Owner drops a cut in their folder for the Member
Mirror
The reverse runs through the Member’s scanner and their cuts folder
Commons
Either person can add a shape to the commons; nothing else leaves

Troubleshooting

What breaks first, and where to look.

The local stdio server breaks
Something wrote to stdout. In a stdio server, logs go to stderr; anything on stdout corrupts the messages.
The Member cannot connect
Check the share was accepted, the port is in the policy grant, and the URL path matches the one the server serves.
Every answer is empty
No files carry the member’s tag, or the sync has not run. Look at the manifest’s emitted_at.
Answers look old
The server answers from the last manifest it has. The Community Owner’s machine was asleep, or the sync failed.
Prompt for your agent
My Local Assembly setup isn't working. Here is what I see: <describe it>.

The setup: a scanner writes <tag>-manifest.json and rsyncs it to the community server. An MCP server there serves three tools over Streamable HTTP. The Member reaches it through a Tailscale machine share with an autogroup:shared port grant, using Claude Desktop and mcp-remote.

Ask me one question at a time to find which link is broken: the scanner, the sync, the server, the share or the client.

Decided for the build

Decided in the working notes.

The contributor decides
Contributing a shape and granting access to the work are separate acts
Shapes, not content
The work stays on its owner’s machine or in published cuts on the server
Reading takes a second step
MCP, per relationship, through the dual-ledger gate
A token on every arrow
Every arrow into the server carries a community token
Events
Insights drop in with the name stripped; the contributor’s vault keeps the named receipt

Still open for the build

Not decided yet. Each one changes what gets built.

Community agent
Its contract: what it accepts, what it reads, what it may hand over, and how it is built
Community token
Who makes it, how members receive it, and how it is replaced
Full manifest
Content or metadata only
The server
Whose hardware, whose network, who administers
The Member’s view
What they see beyond the redacted table, including whether design principles show by default
The Member’s half
Their setup is undecided
Last active
The frontmatter field, the file’s modified time, or the changelog
Sync cadence
On save, debounced, or batched
Freshness
Whether answers carry an “as of last sync” stamp
Access or sync
Whether the Member reads on demand only, or files also copy to them
Failure modes
What the Member’s client shows when the server, the share or the manifest is down
Header names
Proposed on this page, not agreed
Commons contribution
How a pair’s manifest becomes one is not specified
Status
The technical spec says in progress, the validation map says designed

Also

Out of scope for the paper
The live surface, and the drawing and insight agents.

Read and use