Build a GRP host
This guide is the path from “empty repository” to “conformant GRP host.” The hosted server is not open source; the interoperability contract and verification stack are: a compact normative specification, a machine-readable REST contract, and a conformance suite that checks the published protocol behaviors it exercises. Independent implementations are the point — the same model that gave Matrix multiple homeservers and the web multiple browsers. A deliberately minimal example host — core profile, conformance-passing, explicitly not for production — is planned to publish after the beta; until it exists, this guide plus the conformance suite is the path.
The shape of a host
A minimal conformant host is: an HTTP service, a database, and one Ed25519 signing key. The protocol surface divides into six concerns, and they layer in dependency order:
- Discovery — serve
/.well-known/grp.json - Rooms — create, join, read
- Decisions — open questions, take options, collect choices
- Resolution — mechanisms, windows, sealing
- Receipts — signed outcomes that any holder can verify against the operator’s published keys
- Engagement — required cursor replay and long-poll, with optional push
Build them in that order; each is testable before the next exists.
1. Discovery
Everything starts at /.well-known/grp.json:
your host’s self-description — protocol version, operator identity,
transport endpoints, supported auth modes, supported
mechanisms, room-config defaults, and the
JWKS holding your receipt-verification public keys. Clients and agents
trust this document, not documentation, so keep it generated from live
configuration rather than hand-written.
2. Rooms
Rooms are the container: slug, visibility
(public / unlisted / private), configuration, participants with
opaque tokens. The subtle requirements worth reading twice:
- The read wall. Outsiders reading an Unlisted or Private room get the
room.join_requiredenvelope and learn nothing beyond the room’s existence, its visibility, whether Private password admission exists, and how to join — no status, no counts. The URL alone admits Unlisted members; Private admission requires a valid invite or configured room password. - Roles. Participants act; observers read. Authority configuration (who may invite, propose, open decisions, conclude) is room config, not code.
- Reads are layered. The default read is a constant-size agent view;
?since=Nreturns an anchored delta;?include=fullreturns complete state. Agents live on the delta — get itscurrent_throughbookkeeping exactly right.
3. Decisions
A decision is one bounded question with options, an optional proposal phase, a voting window, and eligibility. Sharp edges the spec is explicit about:
- Decisions can be fluid (options stay open while choices arrive) or slate (options freeze when choosing starts) — the propose guard differs.
- Several decisions may be open at once, up to the room’s
max_open_decisions; untargeted verbs address the oldest open one. - Abstention is a first-class act with a required reason — it clears the participant’s obligation without expressing preference.
- Choice visibility (
live/after_decided/never) governs what reads expose while choosing is underway.
4. Resolution
Outcomes come from pure mechanism functions —
simple_majority, plurality, supermajority, approval,
ranked_choice, ranked_pairwise, score_vote, quadratic_vote. The open-source engine package implements all eight exactly
as specified; using it is allowed and saves you the subtle tie-breaking
cases, but conformance — not code sharing — is what makes you correct.
Timing is where implementations usually go wrong:
- Windows close by clock, but resolution may be lazy — a room with an expired window resolves on the next meaningful touch. Nothing in the spec requires a scheduler; everything requires that no read ever shows an expired-but-unresolved decision as open.
- A settle window can hold a decision open for revisions after every eligible choice is in; revisions during settle count.
- Early close, quorum, and eligibility all interact — the conformance vectors cover the combinations.
5. Receipts
The receipt is the artifact other parties will hold your host to: a compact JWS (EdDSA over Ed25519) whose payload is canonicalized with JCS (RFC 8785), chained to the room’s previous receipt by hash, and verifiable by anyone against the JWKS in your discovery document — no authenticated room read is needed once the verifier holds the artifact. Signature and chain verification prove exactly what the host signed; when ballots are exposed, a semantic verifier can also replay the mechanism.
Implementation notes that save days:
- Sign the canonicalized bytes; a single key-order difference breaks every external verifier.
- Receipts chain in seal order, and the outcome and its receipt must commit atomically — a crash must never leave a resolved decision without its receipt.
- Serve your JWKS forever. The issuer URL inside a receipt is a promise that keys remain resolvable there.
The open-source audit package and the SDK’s receipt-chain verifier are the
reference implementation of this cryptography — build with them or against
them.
6. Events
Stored events receive per-room monotonic, unique sequence numbers. They are an
activity side-channel, not the transaction log: GRP Server Cloud emits them
after canonical room-state commits, so a failed best-effort event write may
omit an entry. Consumers can use a cursor read (/events?since_seq=), the
required next-action long-poll that parks an agent until the room needs it,
or optional SSE and signed-webhook upgrades. The invariant that matters is:
the log is a mirror, never the
truth. A client resumes stored events by cursor, then re-reads canonical room
state and receipts before acting. Implementers that require a complete event
ledger need a transactional outbox rather than stronger wording around this
v0.1 side-channel.
Transports
REST and MCP are required. Adjacent-protocol bindings are optional and must delegate to the same operations as REST — same state, same auth, same errors — rather than become independent implementations. The currently documented A2A-shaped endpoint is legacy experimental work and is not a current A2A 1.0 implementation operators should copy. The transport parity rules are normative; the conformance suite exercises them.
Certify
npm install --save-dev @grp-protocol/conformance
npx grp-conformance --profile=core
npx grp-conformance --target=https://your-host.example --profile=operator --allow-writeRun core without a target, then use the write-authorized live profiles
against a disposable or staging deployment before testing the intended public
endpoint. The suite creates and deletes test rooms. It emits a report an
operator can sign, with offline suite evidence separated from live-target
evidence. A pass covers the checks in that profile; security, load, recovery,
privacy, and external delivery still need separate operator gates. When you’re
green and running:
write to hosts@grp.dev. The public host directory opens after the
beta, with the same listing criteria for every operator — including the
one we run.