
Turns natural language food descriptions into content-addressed blocks with six base types: actors, places, substances, transforms, transfers, and observations. Call fb() with "Sourdough bread, $4.50, organic, contains gluten" and get back structured JSON with type, state, and refs. Every block gets a SHA-256 hash derived from its content. Built for provenance chains across farm, processing, retail, and hospitality operations. The programmatic API lets you create blocks, sign them with keypairs, walk update chains, and resolve references across federated servers. Includes templates for common patterns like supply chains and a sandbox server that accepts both structured POST requests and plaintext descriptions. No schemas to configure, runs locally, pattern matching only.
The smallest language strangers in food need to agree on, with no boss. Everything else is data.
A block is three fields, signed by its author on their own device:
{
"type": "promise",
"state": {
"terms": { "price": { "minor": 450, "currency": "GBP" }, "quantity": { "value": "30", "unit": "{egg}" } },
"at": "2026-10-15T09:00:00.000Z",
"audience": "public",
"seq": 4
},
"refs": { "author": "a1b2…", "prev": "c3d4…", "from": "a1b2…", "what": "e5f6…" }
}
This is an offer: it is signed by its from side. Its id is the hash of its canonical form (one exact way of writing
it as text: RFC 8785). The author signs the same bytes with Ed25519 (the default suite; p256 and sm are two more). Nobody's clock decides who has authority. The
other side can check that authority. Nothing can be lost or forged without it showing.
spec/protocol.md, FoodBlock 1.6. It is normative: where anything else here
disagrees, it governs. Beside it: protocol.json (every list, for code to load), the
canonical form, transport (email is the envelope, FoodBlock the
letter) and the conformance vectors. There is no earlier version in the protocol: 1.6 is
year zero. Old data enters once, through a converter that is not part of the protocol.spec/whitepaper.md (PDF), the published v1.5
whitepaper: why the protocol exists. Where it and the spec differ, the spec is current.Seven types, in three moods:
| Type | Meaning | |
|---|---|---|
| What exists | actor | A person, an agent or machine, a circle, or a contact |
thing | Anything held, found or written: food, tools, places, recipes, shares, schemas | |
| Is | observe | A claim that something is so |
transform | A thing changes: inputs become outputs, or a thing is kept and stays itself | |
transfer | A thing changes owner, holder or place | |
| Must | promise | A commitment, offered or asked |
| May | grant | Authority given to someone |
Seven verbs, the refs that link blocks: updates, answers (the only way to consent), confirms, defeats,
for, derives_from, same_as. prev and after say what came before.
Strict. Only four types carry authority and are closed (actor, promise, transfer, grant); any other word
is an open type that a node stores and passes on. A node writes and reads only 1.6 shapes: no nulls, no floats
(money is { minor, currency }, an amount { value, unit } with a decimal string), exact UTC times, sets written
sorted, every block in its key's chain (state.seq, refs.prev). It never repairs a block: it refuses it, and the
author signs a new one.
Every peer speaks the protocol itself: a phone, a robot kitchen, a delivery van, a drone, a smart fridge, a
sensor. Machines are peers like people. Any food machine can take an SDK, make its own identity, and act within the
grants it holds. Each peer is one of three sizes (protocol.json peers):
Only people and machines hold nodes. A business, a household or a community is a circle of them: it has an id and a record, kept by its members' nodes, never a node of its own.
| Folder | What it is |
|---|---|
spec/ | The protocol, its lists, the canonical form, transport, profiles, vectors, the oracle and the paper |
sdk/javascript | The reference SDK |
sdk/rust | The one core: every rule, as a Rust crate, a C library and WebAssembly |
sdk/python, sdk/go, sdk/swift, sdk/kotlin | The same protocol in other languages, thin layers over the core; each runs every vector section |
server/ | A node: Postgres for the blocks, HTTP for signed pull, push and .well-known/foodblock |
mcp/ | An MCP server, so any AI agent can write and read signed blocks |
cli/ | fb: make an identity and device keys, write blocks in its key chain, check, validate, verify, and convert old data once |
sql/schema.sql | The node's store: append-only, each block kept exactly as it arrived |
test/ | Cross-language canonical vectors, and the script that copies the spec into each SDK |
Each SDK's README says which profile it covers.
FoodBlock is built for people, agents and machines to use together. An agent can read and write it directly:
mcp/, for Claude, Cursor, Windsurf, Gemini CLI and any MCP-compatible agentspec/protocol.md, with every list machine-readable in
spec/protocol.jsonconst fb = require('@foodxdev/foodblock') // in this repository: require('./sdk/javascript/src/index.cjs')
const keys = fb.generateKeypair() // one key is the whole ceremony
const me = fb.identity.genesis(keys) // who you are: its hash is your id
const eggs = fb.write('thing', { kind: 'eggs', name: 'Free-range eggs' }, {}, { author: me.hash, key: keys, held: [me] })
fb.valid(eggs, [me]) // [] : sound, signed by a key of its author, in its chain
From a terminal:
cd cli && npm install
node bin/fb.js init me.json
node bin/fb.js create thing eggs --state '{"name":"Free-range eggs"}' --identity me.json
Anyone can leave a node with everything they signed. Peers swap blocks with two messages (spec/modules/nodes.md):
{ since, limit, filter, heads }: every match since an arrival number, oldest first, never ranked.
heads: true gives only the latest blocks. A signed pull also proves who is asking, so it gets the copies sealed
to them.{ messages, for }: the node keeps what its authors signed and what the owner's admission rule lets in.
A block whose previous block is missing waits. Two blocks of one key naming one prev prove a lie.counter or a challenge (GET /challenge) and no time..well-known/foodblock says who the node is, what it holds, who it speaks for and its admission
rule. On a local network it can be announced in a shorter form.Run one with docker compose -f server/docker-compose.yml up, or cd server && npm install && npm start with a
Postgres at DATABASE_URL and a key file from fb key at NODE_KEY.
| What | Command |
|---|---|
| Conformance vectors | node spec/run-vectors.cjs |
| The SDKs carry the spec exactly | node test/sync-spec.cjs --check |
| JS SDK | cd sdk/javascript && npm test |
| Python SDK | cd sdk/python && python3 -m unittest discover -s tests |
| Go SDK | cd sdk/go && CGO_ENABLED=0 go test ./... |
| Swift SDK | sdk/swift/scripts/build-xcframework.sh --macos && swift test (at the root) |
| Rust core | cd sdk/rust && cargo test |
| Kotlin SDK | cd sdk/kotlin && ./gradlew :foodblock:test (needs a JDK 17); the bundled-library check: scripts/build-natives.sh jvm && ./gradlew :foodblock:testBundled |
| The oracle (written from the spec text alone) | node spec/oracle/run.cjs |
| Node | cd server && npm test (in-memory Postgres; set TEST_DATABASE_URL for a real one) |
| MCP server | cd mcp && npm test |
| CLI | cd cli && npm test |
MIT
To cite FoodBlock, see CITATION.cff.
FOODBLOCK_URLOptional FoodBlock server URL. Leave empty for standalone mode.