Skip to content

Federation step 1: peers + strut/run-workflow (dispatch-through first, for a local strut calling a cloud explorer agent) - #123

Merged
Evanfeenstra merged 2 commits into
mainfrom
claude/strut-federation-linking-57e920
Oct 8, 2026
Merged

Evanfeenstra merged 2 commits into
mainfrom
claude/strut-federation-linking-57e920

Conversation

@Evanfeenstra

@Evanfeenstra Evanfeenstra commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Two commits: the plan amendment, then the build of its step 1.

1. plans/federation.md — the 2026-10-08 rulings

The use case in hand is a local (desktop) strut asking a swarm's strut to walk that swarm's knowledge graph and return what is relevant. That flips the plan's order: peers + strut/run-workflow + @slug in the builder come first; read-through follows on the same peer record.

  • The step waits on the peer's SSE tail, reattaching with ?skip=N, never a callback: a strut behind NAT can reach a cloud strut but cannot receive a POST, and no strut knows its own public URL.
  • A peer is named by the hive workspace slug, what a person types as @slug.
  • A local strut gets its peers through a paste door (STRUT_PEERS, a Peers dialog), since hive cannot push to it. New Local tier row.
  • job is an explicit field on the step, never forwarded from ctx.job: no shared directory across struts.
  • The pushed scope is lab:peer (read, launch, control what it launched); lab:read stays for a central that only reads.
  • Billing across the call and actor secrets are deferred. The org fan-out of plans/org-gateway.md §3 is not built in hive, and an explorer needs neither.

2. Step 1 built

  • src/peers.ts. A peer is { id, baseUrl, token, label? } in a fourth encrypted file, peers.json, behind PUT / DELETE / GET /peers (the GET returns ids, labels and base URLs, never a token). STRUT_PEERS and createStrut({ peers }) are the paste door. ctx.services.peers names a peer and makes a request with the token injected; the token is readable by nothing. The client the step and the builder share: launchOnPeer, tailPeerRun (reattach with ?skip=N, backoff, abort), cancelOnPeer, runOnPeer.
  • strut/run-workflow. POST …/run on the peer with this run's principal as x-strut-actor, then the tail. Cancelling the caller's run POSTs cancel to the peer. wait: false returns the handle. job is explicit only. Output: { peer, workflow, runId, status, output?, error?, durationMs }; an artifact path in it is the peer's.
  • The builder. list_peers, and peer on list_workflows / get_workflow / run_workflow; the prompt says @<id> names a peer and a peer run is awaited, never detached.
  • Docs. specs/API.md §10, AGENTS.md (layout, env row, key concept), the plan's step 1 marked built.

Two calls that differ from the plan text, both written into it: the capability sits on the services bag (the step has to reach the peer somehow; the token stays inside the process, and the token's scope bounds what a step can do through it), and GET /peers includes baseUrl.

Tests: src/peers.test.ts — the store, the capability, launch/tail/cancel against a fake peer (reattach, backoff, abort, refusals), the routes, and two struts over real HTTP (listen(0)): result and actor forwarded, job only when named, wait: false, cancel propagation, unknown peer, refused launch. Three AI tool tests. The route-sweep test covers the new routes behind the key. 1360 tests pass.

Not in this slice: a Peers dialog, pause/resume forwarding to the peer, the lab:peer scope in mcp, read-through. A peer token today is the peer's whole key, so a laptop should not hold one until the scope lands.

…oud explorer agent

Revised 2026-10-08. The first use case is a desktop strut asking a swarm's
strut to walk its knowledge graph, so the order flips: peers +
strut/run-workflow + @slug in the builder come first, read-through follows
on the same record. Rulings: the step waits on the peer's SSE tail with
?skip=N reattach, never a callback (a strut behind NAT cannot receive one);
a peer is named by the hive workspace slug; a local strut gets its peers
through a paste door since hive cannot push to it; `job` is explicit on
the step, never forwarded; the pushed scope is lab:peer (read, launch,
control what it launched). Billing across the call and actor secrets are
deferred — the org fan-out is not built in hive, and an explorer needs
neither. A Local tier row, the step order, validation and open questions
updated to match.
…ion step 1)

A strut knows nothing about other struts. A PEER is a record someone put
here — `PUT /peers/:id { baseUrl, token, label? }` (hive, by workspace
slug) or STRUT_PEERS / createStrut({ peers }) on a strut nobody can push
to — in a fourth encrypted file, peers.json. GET /peers lists ids, labels
and base URLs; a token has no read route. Steps get ctx.services.peers,
a capability that names a peer and makes a request with the token
injected (readable by nothing); the builder gets list_peers and `peer`
on list_workflows / get_workflow / run_workflow, with @<id> in a prompt
naming a peer.

strut/run-workflow launches POST …/run on the peer with this run's
principal as x-strut-actor (billed and secret-bound there, for that
person; nothing crosses) and waits on the peer's SSE tail, reattaching
with ?skip=N after a dropped connection — reader-initiated, so a desktop
strut behind NAT can call a cloud one. Cancelling the caller's run POSTs
cancel to the peer; wait: false returns the handle; job is explicit and
never the caller's own (no shared directory across struts).

Tests: src/peers.test.ts — the store, the capability, launch/tail/cancel
against a fake peer (reattach, backoff, abort, refusals), the routes, and
two struts over real HTTP (result + actor forwarded, job only when named,
wait: false, cancel propagation, unknown peer, refused launch); three AI
tool tests. specs/API.md §10, AGENTS.md, plans/federation.md step 1
marked built. Not in this slice: a Peers dialog, pause/resume forwarding,
the lab:peer scope, read-through.
@Evanfeenstra Evanfeenstra changed the title federation.md: dispatch-through first, for a local strut calling a cloud explorer agent Federation step 1: peers + strut/run-workflow (dispatch-through first, for a local strut calling a cloud explorer agent) Oct 8, 2026
@Evanfeenstra
Evanfeenstra merged commit 335ac32 into main Oct 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant