Seed Protocol
Overview

How Seed Protocol works

The architecture in one page: a local-first app on top, public permanent networks underneath, and an account the user controls signing the record of who made what.

You build an ordinary local-first app, and publishing copies local data out to two public networks: Ethereum Attestation Service (EAS) attestations for the signed record of who published what, and Arweave for files and long-form content. The attestations are made by an account the user controls, even when someone else pays the fees. The Seed contracts run on OP Sepolia, an Optimism testnet, which is the SDK's default chain; there's no mainnet deployment yet (Deployments).

The shape of it

The top half of the diagram runs in your app and feels like any modern data layer. The bottom half is public infrastructure that anyone, including an app you didn't write, can read from.

Everything inside the app is fast, private, and works offline. Everything on the public networks is slower, public, and meant to be permanent. Publishing is the act of moving a record from one side to the other.

Your app talks to @seedprotocol/sdk and, for React, @seedprotocol/react, and a build plugin (@seedprotocol/vite, or the Next.js config helper in @seedprotocol/next) makes the SDK run in the browser. @seedprotocol/publish writes to the public networks, and @seedprotocol/query and @seedprotocol/feed read from them; The Seed toolkit covers every package.

The SDK and the local database

You write against the SDK as you would any data layer: define schemas, declare models, create and edit items, and subscribe to live queries that re-render your UI when data changes. None of that touches a blockchain. The SDK reads and writes a local SQLite database (SQLite compiled to WebAssembly and stored in the browser's origin-private file system (OPFS), or a libsql file on Node), with files and images kept alongside it.

This is a deliberate choice. Reads are fast because they hit local storage, not a network. Writes don't wait on a chain. Creating and editing items works offline (what needs the network), and nothing leaves the device until the user decides to publish.

Creating an item checks its values before anything is written, and assigning a new value to a property saves it to the local database, with validation as part of that save. Edits are checked against ownership: once you tell the client which addresses the user owns, an item with a publisher can be changed only while that publisher is one of them. A draft that has never had a publisher, such as one made before any wallet connected, is the user's whatever the owned addresses are, and @seedprotocol/publish gives it to the next wallet that connects. Saving a new schema version is checked against the database first, so a stale version fails instead of silently overwriting a newer one. The mechanics are covered in Local-first and offline, Ownership and drafts and Conflict detection.

What "publish" actually does

The core SDK prepares a publish: it works out what needs uploading and builds the attestation payload. Sending it to the networks is done by @seedprotocol/publish, which item.publish() hands the work to once the publish package is set up. It happens in two steps.

First, content goes to Arweave. Images, files, HTML bodies, and text properties configured for file storage are uploaded to Arweave, which is built for permanent storage. Each upload is tagged with a hash of its content (Content-SHA-256) so readers can check they got the right bytes. The SDK checks it when it downloads a file; see Permanence. The result is an Arweave transaction ID that the attestations can point at.

Then the record goes to EAS, as attestations. A published item becomes a small hierarchy of attestations:

  • A Seed attestation is the stable identity of the item, the thing that stays constant across every edit.
  • A Version attestation references the Seed and represents a snapshot of the item.
  • Property attestations reference the Version and carry the field values: small values inline, and file-backed values as references whose attestations record the Arweave transaction ID.

From the default smart account, the Seed contracts' multiPublish call creates the Seed and Version if they don't exist yet and attests the properties against the Version. The item and the related drafts it points to go in one transaction, so if it fails, nothing is attested (One transaction for the whole publish). A plain wallet attests to EAS directly instead, over several transactions, so a failure can leave earlier items published; Which path the publish package takes lists the routes.

Attestations are never edited in place, so earlier ones stay on-chain. By default (patch), publishing an edit attests only the changed properties against the item's current Version; new_version records a full snapshot as a new Version. Edits add attestations explains how readers pick the current value.

Unpublishing revokes the item's attestations; EAS keeps them, marked revoked (Revoking marks, it doesn't erase).

Where the keys live

What makes "user-owned" more than a slogan is that the on-chain record is made by an account the user controls, not by the app or by Seed.

The user's account is the attester of record for every Seed, Version and property: by default a thirdweb ERC-4337 smart account, and the publish package asks thirdweb's paymaster to pay its gas, so the user doesn't need ETH if the app's thirdweb project sponsors it. An app can be given a session key that publishes on the account's behalf but can't revoke. On the bundler path, files go to Arweave as data items signed by the user's own key, and the upload API pays for the storage. Identity and signing covers who signs what, Costs and who pays the fees, and How bytes get to Arweave every upload path.

The result is the arrangement the protocol is built around: the user's account signs the record, someone else can absorb the cost, and the public record attributes the content to the account that published it. If the party paying the fees goes away, already-published content stays where it is.

How publishing feels in the app

Permanence is slow, so the slow part shouldn't block the user. Local writes are immediate, and the draft is usable straight away. Publishing runs as a background process that steps through upload and attestation. Its progress is saved to the local database, so most publishes interrupted by a reload resume, and @seedprotocol/publish gives you hooks to show the user where it's up to. The gateways and upload infrastructure behind this are covered in the Infrastructure tab.

Reading it back

Because the record is public, reading isn't limited to the app that wrote it. There are three ways in:

  • The SDK syncs from EAS: it pulls attestations for the addresses the user owns or watches from an EAS indexer into the local database, so items published from another device or another app show up in the same queries as local drafts.
  • @seedprotocol/query reads published items straight from EAS and Arweave as plain JSON, for a server, a static site, or any app without a local database.
  • @seedprotocol/feed turns published items into RSS, Atom and JSON Feed.

Where to go next

On this page