The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Foodblock MCP listing page.
A content-addressable protocol for universal food data.
One axiom. Three fields. Six base types. Every food industry operation.
id = SHA-256(canonical(type + state + refs))
The food industry spans 14 sectors — farming, processing, distribution, retail, hospitality, regulation, sustainability, and more. Every sector models food data differently. There is no shared primitive.
FoodBlock is that primitive. One data structure that can represent a farm harvest, a restaurant menu item, a food safety certification, a cold chain reading, a grocery order, or a consumer review. Same three fields. Same hashing. Same protocol.
Every FoodBlock has exactly three fields:
| Field | Type | Description |
|---|---|---|
type | string | What kind of block (dot-notated subtypes) |
state | object | The block's data (schemaless, any valid JSON) |
refs | object | Named references to other blocks by hash |
Identity is derived from content: SHA-256(canonical(type + state + refs)). Same content always produces the same hash, regardless of where or when the block is created.
Entities — things that exist:
Actions — things that happen:
Subtypes via dot notation: actor.producer, substance.product, observe.review, transfer.order.
fb()The fastest way to use FoodBlock. Describe food in plain English, get structured blocks back.
No types to memorize. No schemas to configure. No API calls — fb() is pure pattern matching, runs locally, costs nothing.
Try it locally with zero setup:
The sandbox ships preloaded with 47 blocks modelling a complete bakery supply chain — from farm to consumer, including certifications, shipments, cold chain readings, reviews, and operational vocabularies.
fb(text) → { blocks, primary, type, state, text }The natural language entry point. Pass any food-related text, get FoodBlocks back. Detects intent (product, review, farm, order, certification, reading, process, venue, ingredient), extracts quantities (price, weight, volume, temperature, rating), flags (organic, gluten-free, kosher, etc.), and relationships ("from X", "at Y", "by Z"). No LLM — pure regex pattern matching against built-in vocabularies.
create(type, state, refs) → blockCreate a new FoodBlock. Returns { hash, type, state, refs }.
update(previousHash, type, state, refs) → blockCreate an update block that supersedes a previous version. Automatically adds refs.updates.
hash(type, state, refs) → stringCompute the SHA-256 hash without creating a block object.
chain(hash, resolve, opts) → block[]Follow the update chain backwards. resolve is async (hash) => block | null.
tree(hash, resolve, opts) → { block, ancestors }Follow ALL refs recursively to build the full provenance tree.
head(hash, resolveForward) → stringFind the latest version in an update chain.
sign(block, authorHash, privateKey) → wrapperSign a block with Ed25519. Returns { foodblock, author_hash, signature, protocol_version }.
verify(wrapper, publicKey) → booleanVerify a signed block wrapper.
generateKeypair() → { publicKey, privateKey }Generate a new Ed25519 keypair for signing.
encrypt(value, recipientPublicKeys) → envelopeEncrypt a value for multiple recipients using envelope encryption (Section 7.2).
decrypt(envelope, privateKey, publicKey) → valueDecrypt an encryption envelope.
validate(block, schema?) → string[]Validate a block against its declared schema or a provided schema. Returns an array of error messages (empty = valid).
tombstone(targetHash, requestedBy, opts?) → blockCreate a tombstone block for content erasure (Section 5.4).
offlineQueue() → QueueCreate an offline queue for local-first block creation with batch sync.
query(resolve) → QueryFluent query builder:
registry() → RegistryAlias registry for human-readable references. Use @name in refs instead of hashes.
parse(line) → { alias, type, state, refs }Parse a single line of FoodBlock Notation (FBN).
parseAll(text) → block[]Parse multiple lines of FBN.
format(block, opts?) → stringFormat a block as FBN text.
explain(hash, resolve) → stringGenerate a human-readable narrative from a block's provenance graph.
toURI(block, opts?) → stringConvert a block to a fb: URI. toURI(block) → fb:<hash>, toURI(block, { alias: 'name' }) → fb:<type>/<alias>.
fromURI(uri) → objectParse a fb: URI into { hash } or { type, alias }.
createTemplate(name, description, steps, opts?) → blockCreate a template block (observe.template) that defines a reusable workflow pattern.
fromTemplate(template, values) → block[]Instantiate a template into real blocks. values maps step aliases to { state, refs } overrides. @alias references between steps are resolved automatically.
TEMPLATESBuilt-in templates: supply-chain, review, certification.
discover(serverUrl, opts?) → infoFetch a server's /.well-known/foodblock discovery document.
federatedResolver(servers, opts?) → resolveCreate a resolver that tries multiple servers in priority order. Returns async (hash) => block | null with optional caching.
createVocabulary(domain, forTypes, fields, opts?) → blockCreate a vocabulary block (observe.vocabulary) defining canonical field names, types, and natural language aliases for a domain.
mapFields(text, vocabulary) → { matched, unmatched }Extract field values from natural language text using a vocabulary's aliases. Returns matched fields and unmatched terms.
VOCABULARIESBuilt-in vocabulary definitions: bakery, restaurant, farm, retail, lot, units, workflow.
quantity(value, unit, type?) → { value, unit }Create a quantity object. Validates unit against the units vocabulary if type is provided (e.g. 'weight', 'volume', 'temperature').
transition(from, to) → booleanValidate a workflow state transition against the workflow vocabulary's transition map (e.g. draft→order is valid, draft→shipped is not).
nextStatuses(status) → string[]Get valid next statuses for a given workflow status.
localize(block, locale, fallback?) → blockExtract locale-specific text from multilingual state fields. Fields using { en: "...", fr: "..." } nested objects are resolved to the requested locale.
forward(hash, resolveForward) → { referencing, count }Find all blocks that reference a given hash. Returns blocks grouped by ref role.
recall(sourceHash, resolveForward, opts?) → { affected, depth, paths }Trace contamination/recall paths downstream via BFS. Starting from a source block, follows all forward references recursively. Supports types and roles filters.
downstream(ingredientHash, resolveForward) → block[]Find all downstream substance blocks that use a given ingredient (convenience wrapper around recall).
merkleize(state) → { root, leaves, tree }Build a Merkle tree from a state object for selective disclosure.
selectiveDisclose(state, fieldNames) → { disclosed, proof, root }Reveal only specific fields with a Merkle proof that they belong to the block.
verifyProof(disclosed, proof, root) → booleanVerify a selective disclosure proof.
merge(hashA, hashB, resolve, opts?) → blockCreate a merge block resolving a fork between two update chain heads.
attest(targetHash, attestorHash, opts?) → blockCreate an attestation block confirming a claim. opts.confidence: verified, probable, unverified.
dispute(targetHash, disputerHash, reason) → blockCreate a dispute block challenging a claim.
trustScore(hash, allBlocks) → numberCompute net trust score: attestations minus disputes.
createSnapshot(blocks, opts?) → blockSummarize a set of blocks into a snapshot with a Merkle root for archival verification.
A FoodBlock's identity is its content: SHA-256(canonical(type + state + refs)).
Everything follows from this:
Seven operational rules govern the protocol's use:
type, state, refs.{ foodblock, author_hash, signature, protocol_version } using Ed25519._ prefixed keys contain envelope-encrypted values.Deterministic hashing requires deterministic serialization. Aligns with RFC 8785 (JSON Canonicalization Scheme) for number formatting and key ordering:
-0 normalized to 0.refs: sorted lexicographically (set semantics)state: preserve declared order (sequence semantics)true or falseFull schema with indexes, author-scoped head trigger, and tombstone trigger: sql/schema.sql
test/vectors.json contains 30 known inputs and expected hashes — including tombstone blocks, schema references, vocabulary blocks, attestation blocks, merge blocks, RFC 8785 number edge cases, and more. Any SDK in any language must produce identical hashes for these inputs. If JavaScript and Python disagree, the protocol is broken.
The six base types cover all fourteen food industry sectors:
| Sector | Key Types |
|---|---|
| Primary Production | actor.producer, place.farm, transform.harvest |
| Processing | actor.maker, transform.process, observe.inspection |
| Distribution | actor.distributor, transfer.shipment, observe.reading |
| Retail | substance.product, transfer.order |
| Hospitality | actor.venue, transfer.booking, observe.review |
| Food Service | observe.plan, transform.process |
| Waste & Sustainability | actor.sustainer, substance.surplus, transfer.donation |
| Regulation | actor.authority, observe.certification |
| Education & Media | actor.creator, observe.post |
| Community | actor.group, observe.event |
| Health & Nutrition | actor.professional, observe.assessment |
| Finance | transfer.investment, observe.market |
| Cultural Food | observe.certification, place.region |
| Food Technology | actor.innovator, observe.experiment |
MIT — use it however you want.