Multi-Agent Sessions
Let a second agent join an existing session through a signed, single-use, expiring invitation that the host countersigns.
A multi-agent session lets a second agent join a session that another agent already started, with the join recorded as a signed, verifiable fact. Phase 1 of this model ships three CLI subcommands that wire one lifecycle: the host mints an invitation, a second agent joins against it, and the host countersigns the join.
The result is a participant envelope that carries two signatures: the joining agent's assertion of "I am joining" and the host's countersign of "I observed this join." Either signature alone is invalid, so a join cannot be forged by the joiner and cannot be fabricated by the host after the fact.
The lifecycle
host: treeship session invite <session_id> -> prints an invitation blob
joiner: treeship session join --invite <blob> --actor agent://...
host: treeship session countersign <participant_artifact_id>1. Invite
The host runs treeship session invite against the active session. It mints and signs an invitation with the keystore's default key, persists it under the artifact store, and prints an armored bootstrap blob beginning with -----BEGIN TREESHIP INVITATION-----.
Every invitation is restricted. You pick exactly one restriction:
| Restriction | Flag | Who can redeem |
|---|---|---|
| Certificate | --invitee-cert <issuer_pubkey>:<subjects> | A holder whose certificate is issued by the named issuer for one of the allowed subjects. |
| Public key | --invitee-pubkey <fingerprint-or-pubkey> | The single agent that holds the named key. |
| Open | --open | Anyone holding the blob. Opt-in only, because it accepts any holder. |
An invitation also carries the granted capability action types, an expiry, a max_uses of 1, and a random nonce. The default lifetime is 1 hour and the protocol ceiling is 7 days.
2. Join
The second agent runs treeship session join with the blob and its own actor URI. The command:
- Decodes the blob and pins the issuer against the local
SessionHosttrust roots. - Verifies the invitation signature, then checks expiry and restriction.
- Writes the invitation's nonce into the Approval Use Journal with
max_uses=1, so a second join attempt fails through the journal's existing single-use path. - Emits a single-signature, pending-countersign participant envelope and prints its artifact id.
3. Countersign
The host runs treeship session countersign with that participant artifact id. The command confirms the running default key matches the invitation's issuer, attaches the host countersign over the same canonical bytes, self-verifies, and overwrites the storage record with the finalized two-signature envelope.
A participant envelope is only valid once both signatures are present. Verification rejects an envelope that is missing the host countersign, carries too many signatures, has any signature that does not verify, or whose countersign key does not match the invitation's issuer.
Joining from another machine
Everything above assumes the joiner and the host run on the same machine, so the host can countersign straight from the local artifact store. Two agents on different machines -- a joiner's laptop and a host's server, say -- need one more step to get the pending envelope across, plus a liveness check that replaces "the host read it from local storage" with real proof.
host: treeship session invite <session_id> -> invitation blob
joiner: treeship session join --invite <blob> --actor agent://... --out pending.json
-> send pending.json to the host
host: treeship session mint-challenge -> nonce + issued_at
-> send the nonce to the joiner
joiner: treeship session answer-challenge <participant_id> --challenge <nonce> --actor agent://...
-> send the response file back to the host
host: treeship session countersign --pending pending.json \
--challenge <nonce> --challenge-issued-at <issued_at> \
--challenge-response response.jsonsession join --out <file> writes the same pending participant envelope both to the local artifact store and to a file, so it can be copied (scp, a shared drive, pasted through chat, whatever channel you already trust for moving files) to the host's machine. The host then countersigns with --pending <file> instead of a participant id.
Across machines the challenge is not optional: countersign --pending without --challenge and --challenge-response is refused. The joiner's signed answer to a nonce the host just minted is the only evidence, at countersign time, that the joiner still controls its key -- there's no local artifact store to trust instead. --challenge-issued-at is required too, so countersign has a window to judge the answer against; without it there's no way to tell a fresh challenge from a stale, replayed one.
On the same machine the challenge is still optional today: omit it and countersign proceeds with a warning that the join wasn't liveness-checked. This becomes mandatory everywhere, same machine or not, in 0.32.
A minted challenge is valid for 10 minutes from --challenge-issued-at by default (TREESHIP_JOIN_CHALLENGE_WINDOW_SECS to change it). Past that window, or if the joiner's answer falls outside it, countersign refuses and tells you to mint a fresh one. Countersign is also single-use per invitation -- reserved in the host's Approval Use Journal before it signs, never released -- so a second countersign attempt against the same invitation is refused (has already been countersigned), and it refuses to finalize a join into a session that isn't open on this host right now, whether that's because the session was closed after the invitation was minted or because the invitation was never for a session started here at all. See treeship session countersign for the exact commands and error text.
The SessionHost trust root
The invitation issuer's public key is embedded in every invitation. Before treeship session join honors an invitation, that key must be pinned locally as a session_host trust root. Without the pin, a self-signed forgery would verify cryptographically but be quarantined by trust-root enforcement.
Confirm the key with the host out of band first -- a channel the invitation itself didn't travel on -- then pin it:
treeship trust add <key_id> ed25519:<pubkey> --kind session_hostsession_host is a separate trust kind from ship, hub_checkpoint, and agent_cert. A machine can trust a hub org's checkpoints without implicitly trusting that org to host multi-agent sessions. The trust kinds do not cross over.
Inspecting sessions locally
treeship dashboard is the read-only, localhost-only way to inspect sessions and their participants. It reads sealed .treeship packages from disk and surfaces trust status, agent membership, receipts, reports, and capabilities without requiring a hub. It also exposes JSON endpoints so the same data is scriptable.
Room Sessions
Shared multi-agent sessions with separate identities, one report, and Merkle-sealed evidence.
Agent Identity
An Agent Identity Certificate is the TLS certificate of an AI agent — a deterministic, signed bundle that says who an agent represents and what it's allowed to do. This page covers the format, how to issue one, and how it binds to session receipts.