Skip to Content
DocsBuild a GRP host

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 tells you, mechanically, when you are done. 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:

  1. Discovery — serve /.well-known/grp.json
  2. Rooms — create, join, read
  3. Decisions — open questions, take options, collect choices
  4. Resolution — mechanisms, windows, sealing
  5. Receipts — signed outcomes anyone can verify
  6. Events — gap-free history + 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 / private / password), configuration, participants with opaque tokens. The subtle requirements worth reading twice:

  • The read wall. Outsiders reading a private or password room get the room.join_required envelope and learn nothing beyond the room’s existence, its visibility, and how to join — no status, no counts.
  • 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=N returns an anchored delta; ?include=full returns complete state. Agents live on the delta — get its current_through bookkeeping 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 API access needed: the record is provably exactly what your host signed, and its ballots provably produce its outcome.

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 and verify packages are the reference implementation of this cryptography — build with them or against them.

6. Events

Every state change appends to a per-room, gap-free, monotonic event log. Consumers get three views: a cursor read (/events?since=), an SSE stream, and the next-action long-poll that parks an agent until the room needs it. The invariant that matters: the log is a mirror, never the truth — a client that misses every push and simply re-reads the room must always see a correct world.

Transports

REST and MCP are required; A2A is optional. MCP and A2A must be thin adapters over the same operations as REST — same state, same auth, same errors. The transport parity rules are normative; the conformance suite exercises them.

Certify

npm install @grp-protocol/conformance npx grp-conformance --base=https://your-host.example --profile=core

Work profile by profile until green, then run the full operator profile. The suite emits a signed report — that report, not this guide, is what “conformant” means. When you’re green and running: write to hosts@grp.app. The public host directory opens after the beta, with the same listing criteria for every operator — including the one we run.

Last updated on