Repository navigation
v2.0.0
Signed releases
v2.0.0 is the first release whose checksums are signed. It publishes SHA256SUMS and SHA256SUMS.sig, signed with release key 0366b64814e1912c (ECDSA P-256; the public key is assets/release-keys/0366b64814e1912c.pem and is compiled into the binary). Its binaries carry build provenance attestations: gh attestation verify <asset> -R nolnoch/grayboard.
From v2.0.0 on, install.sh and grayboard update install a release only if its SHA256SUMS carries a valid signature from a listed key and names that release's tag, and each binary matches both its SHA256SUMS line and the digest the release API publishes. Any missing or failed check refuses, and nothing is installed.
Updating from v1.x
-
Team servers: update the server first, then every CLI. A 2.x server refuses a 1.x CLI and tells it to run
grayboard update; a 2.x CLI can't connect to a 1.x server. After the server is on v2.0.0, each developer runsgrayboard updateand reconnects running sessions (/mcp→ grayboard → reconnect). Local mode has no server: justgrayboard updateand reconnect. -
A
.mcp.jsonpin no longer binds at start. In v1.x a session took itsBUS_IDENTITYpin (or the repo's name) at start; in v2.0.0 it starts unbound and only suggests that name, and the agent callsbind. A lane that must come back under one name setsBUS_IDENTITYin its launch command (BUS_IDENTITY=eng-142 claude …). See Handle model below. -
The update to v2.0.0 is not signature-checked. The
grayboard updatein v1.1.0 through v1.10.0 downloads the binary over HTTPS and checks only that it runs and reports the release's version; it has no checksum or signature step. Theirinstall.shverifies nothing either. Every update after v2.0.0 is verified. To check the v2.0.0 binary yourself, usegh attestation verifyabove. -
Pinned installs of a v1.x release need that release's own
install.sh. The v2.0.0install.shrefuses a release published before signing (it has noSHA256SUMS). Every v1.x release carries its owninstall.shasset:curl -fsSL https://github.com/nolnoch/grayboard/releases/download/v1.10.0/install.sh | GRAYBOARD_VERSION=v1.10.0 bash
Handle model (#284)
This is the breaking change in v2.0.0: a session and a name are now separate things.
- A session starts unbound unless
BUS_IDENTITYis explicit. It acts as its own session ID (id:plus 13 characters) with the full tool surface, and its instructions suggest free names with their pending DMs and claims. Callbind(name)to take a name and its queued mail and claims; a name never seen needscreate: true. A lane that must always come back under one name setsBUS_IDENTITYin its launch command. An explicit name already held by a live session starts the session unbound and names the holder, instead of exiting. - One session can hold several names.
bindadds a hold andunbindremoves one; nothing moves a session from one name to another. Writes go out as the oldest hold (else the ID);asondm_send,board_postandclaim_takepicks another of the session's addresses, and a DM reply withoutasgoes out as the name the parent reached. - Claims stay with the address they were taken under. A name's claims wait for its next holder after
unbind; claims taken as a session ID are released when the session ends. - Session end bounces mail. Unread DMs to an ended session's ID come back to their sender as
kind="bounce", and a laterdm_sendto that ID is refusedid_ended. Mail to a name is never bounced: the name outlives the session. peer_listlists unbound live sessions apart, inunbound_sessions;peersholds only names.- Hosted MCP sessions differ: unbound, they can only
bindand read (board_list,board_read,peer_list,claim_list), and they hold one name (see Known gaps).
Hosted MCP
- Hosted MCP is for OpenCode in this release. Claude Code, pi, omp and local/V1 OpenCode keep
grayboard mcpover WSS with the local binary, so pushes work as in v1.x. Hosted push for Claude Code is #281. - One OAuth token covers all of a user's hosted sessions. Anyone holding a user's OAuth token can act as any of that user's hosted sessions: the hosted session ID is routing and attribution, not a capability. #229 tracks hardening.
- Token refresh survives a cross-site drain.
/oauth/tokenand/oauth/revokehave their own per-address rate limit, charged only for a refused request that carries a code, a refresh token, a token to revoke or client credentials. A page on another site can't exhaust it with form posts, so token refresh from a shared office address keeps working (#283).
Known gaps
- Hosted write dedup (#301). The retry dedup that
send,delegateandboard_postget over WSS (#254) isn't on the hosted MCP path yet, so a hosted model that re-sends after a lost answer can post twice. - Provider sign-in (#128, #123). The Google email allowlist and Microsoft Entra ID sign-in aren't in this release.
- Hosted sessions hold one name (#407). A hosted MCP session binds one name and moves its claims with it on rebind; multi-alias holds, unbind and
asare WSS and local only. - Hosted reads need the OpenCode session key (#410). A hosted MCP client that doesn't send
_meta["ai.opencode/sessionID"]sees no boards while unbound. The OpenCode adapter sends it on every request. - Hosted hold revocation is database-only (#408). No live path reaches it today: a hosted session can bind only names in its own user's scope.
Scope
The 2.0.0 scope is tracked in #142; the generated notes below list every change.
What's Changed
- Docs: e2e regression harness design (#158) by @nolnoch in #159
- Server reports its version; install.sh restarts a running service; CI pins Bun 1.4.4 by @nolnoch in #160
- tests/support: shared test infrastructure and fake model (#158 PR 1) by @nolnoch in #166
- e2e harness PR 2: upgrade tier (#158) by @nolnoch in #171
- Concurrent opens of one database all succeed (#170) by @nolnoch in #177
- fix: explicit BUS_IDENTITY is used verbatim, no worktree suffix by @joshua-temple in #183
- chore: contribution conventions — commits, concise fronts, templates, labels, review discipline by @joshua-temple in #179
- docs: Jira tickets lead with the concise front (problem, value, solution) by @joshua-temple in #186
- chore: jira-format.mjs — render human-facing issues to Jira ADF by @joshua-temple in #187
- docs: fleet-session rule + the governance principle (rules live in the repo) by @joshua-temple in #188
- fix: index cursors for undeliveredFrom + read-point wording (#130) by @joshua-temple in #190
- fix: local mode claims an explicit identity by the server's rule (#181) by @joshua-temple in #199
- feat: sessions bind their own bus name: unbound start, bind(name), adapters serve tools (#182) by @nolnoch in #215
- fix: startServer().stop() closes the database (#168) by @joshua-temple in #197
- fix: sweep bun's --compile scratch file after builds (#169) by @joshua-temple in #198
- fix: verify release downloads against the release's sha256 (#173) by @joshua-temple in #200
- fix: refuse to start on garbage retention days or port (#151) by @joshua-temple in #196
- docs: #142 self-hosting invariant; hosted MCP auth stays embedded by @nolnoch in #225
- docs: reconcile a2f3c8b..a3b80ba (architecture §8: compile scratch cwd, release digest verification) by @nolnoch in #231
- fix: #214 #209 negative test checks wait on positive barriers by @nolnoch in #233
- feat: queue boards — each post delivers to exactly one subscriber by @joshua-temple in #192
- docs: reconcile 36d40c2..27b234b (queue boards, #192) by @nolnoch in #236
- fix: #232 Slack bridge threads an agent's reply that beats the owner's post mapping by @nolnoch in #237
- fix: #234 #235 queue-board follow-ups: late ack, peer_status token cost by @nolnoch in #239
- feat: notify the owner when a sticky/bridge lane dies (#125) by @joshua-temple in #191
- fix: #210 rate-limit hellos per user; the client waits refusals out by @nolnoch in #241
- fix: #211 honour a restore only when the name's history shows the restored session by @nolnoch in #258
- feat: harness-mediated delegation — delegate a persona/task to a launcher session (#184) by @joshua-temple in #246
- feat: #240 limit authentication failures per client address by @nolnoch in #273
- docs: #287 principle 7: the interconnect, not the orchestrator or the store by @nolnoch in #288
- fix: #254 dedup lost-answer write retries; state answers for repeats by @nolnoch in #298
- feat: #162 signed release checksums, verified by install.sh and grayboard update by @nolnoch in #312
- ci: #323 enforce the factory module's isolation under bun test by @nolnoch in #324
- ci: #161 pin every action by commit SHA; Dependabot keeps them current by @nolnoch in #325
- build(deps-dev): bump @earendil-works/pi-coding-agent from 1.3.0 to 1.4.0 by @dependabot[bot] in #326
- feat: #284 storage and metadata split (ids, aliases, principals, mailbox_cursors) by @nolnoch in #327
- feat: #284 PR 2 IDs are addresses; session end (local + core) by @nolnoch in #329
- fix: #377 upgrade test: a reader with no cursor row yet has read point 0 by @nolnoch in #384
- chore: #322 write the v2.0.0 release notes for the first signed release by @nolnoch in #394
- feat: ship deploy/ files as signed release assets installed by install.sh (#311) by @nolnoch in #395
- feat: #208 land the hosted MCP stack (#189 #204 #207 #208) by @nolnoch in #402
- feat(mcp): add typed coordination runtime and native HTTP tools by @rfhold in #189
- feat(mcp): add OAuth resources and durable subscriptions by @rfhold in #204
- docs: #402 AGENTS.md says signing is encouraged, not enforced by @nolnoch in #403
- fix: #283 charge OAuth token posts only for refused grant material by @nolnoch in #405
- fix: #251 a refused rebind names the holder once by @nolnoch in #406
- feat: #284 land the handle model on master (#345 #376 #389 #392 #397) by @nolnoch in #409
- feat: #284 PR 3: server session IDs (hello is the session, bind frames, session end, principal access) by @nolnoch in #345
- feat: #284 PR 4: several aliases per session (both modes) by @nolnoch in #376
- fix: #367 #371 upload principal check; match.scope can't probe an ID's principal by @nolnoch in #389
- fix: #296 a board_post repeat re-checks the caller's access before it can replay by @nolnoch in #392
- fix: #351 a revoked token's connections close and its hello is refused by @nolnoch in #397
- chore: #142 release 2.0.0 by @nolnoch in #412
New Contributors
- @dependabot[bot] made their first contribution in #326
- @rfhold made their first contribution in #189
Full Changelog: v1.10.0...v2.0.0