Seed Protocol
Concepts

Permanence

What becomes permanent when you publish with Seed Protocol, what doesn't, and what 'permanent' means while the protocol runs on a testnet.

Publishing copies an item from the local database to two public networks: its files and long-form content go to Arweave, and the signed record of who published what goes to Ethereum Attestation Service (EAS). Both are append-only: you can add to them, and on EAS you can mark a record revoked, but you can't edit or erase what's there.

Seed Protocol is testnet-only today. Its contracts and the attestations they create are on OP Sepolia, an Optimism testnet, and there's no mainnet deployment yet. See What "permanent" means today and Deployments.

Three places your data lives

An item's data can be in three places, and only two of them are permanent.

The local database holds drafts, unpublished edits and local copies of published items. It lives in the browser's private file system (OPFS) or on disk, and it's as durable as that device. Clearing site data, calling client.resetLocalData() in the browser, or deleting the .seed folder loses every draft on it. item.destroy() and client.removeLocalCopiesForAddresses() remove local rows only; neither touches anything on-chain or on Arweave. Nothing in the local database is permanent until it's published. Local-first and offline covers how the local side works.

Arweave holds the bytes that are too big for an attestation: images, files, Html bodies and text properties stored as files. Arweave is a storage network paid for once, up front, and designed to keep data for the long term.

EAS holds the record. A published item is a Seed attestation (its identity), one or more Version attestations, and property attestations for its field values. Small values, such as a title or a number, sit inline in the property attestation. A file-backed value is stored as its own small item whose storage_transaction_id attestation records the Arweave ID, and the property points at that item. How Seed Protocol works walks through the hierarchy.

So the record on EAS says who published what and where the content is, and Arweave holds the content.

How bytes get to Arweave

@seedprotocol/publish packs each file into an ANS-104 data item, signed by a key of the user's own, and sends the data items to an upload API, whose bundler posts them to Arweave inside a bundle (one regular Arweave transaction holding many data items) and pays the fee. So publishing files depends on an upload API being up, reachable from your app and willing to pay. Seed's own gateway is private (Seed's gateway). With payment: { storage: 'user' }, the same data items go straight to ArDrive's Turbo service instead, charged to the signer's Turbo credits. Uploads and ANS-104 data items covers the upload flow, and Costs and who pays covers the money.

Every upload is tagged with Content-Type and a Content-SHA-256 hash of its bytes, plus any tags you configure. The hash is SHA-256, and Content-Type comes from the file's own type, then its extension, then its first bytes.

When the SDK downloads a file in the browser, it checks the bytes against that tag. A gateway that serves bytes that don't match is skipped for the next one, and if none matches, the file isn't saved and is tried again on a later run. Files uploaded without the tag download unverified.

Uploads made on the L1 path by 0.7.2 and earlier carry a SHA3-256 digest under the Content-SHA-256 name. The SDK still accepts those. To check bytes yourself, use contentSha256TagMatches(bytes, tagValue) from @seedprotocol/sdk, which accepts either digest.

An Arweave ID (a data item's or a transaction's) identifies a signed upload, and the signature covers the data, so the bytes behind an ID can't change. It isn't a content address, though: upload the same file twice and you get two IDs. Seed points at content by that ID, through the storage_transaction_id attestation.

Gateways are how you read it

Nobody reads Arweave directly; you read it through a gateway, an HTTP server that fetches and caches Arweave data. The SDK's default list starts with arweave.net, falls back to other public gateways, and ends with Seed's gateway, ar.seedprotocol.io; Arweave and gateways gives the order and how reads move down it. You can change the primary gateway and the fallback list in Client and configuration.

Because any gateway can serve any transaction, the content doesn't depend on any one gateway, but your app's reads depend on the ones in its list. A just-published file may only be on the gateway it was uploaded through until the rest of the network has it, which is why the SDK tries your preferred gateways first.

Links are different from IDs. A link to an Arweave file inside published Html names a host, by default arweave.net, and that host is part of the uploaded bytes for good. Seed readers re-host those links, moving them onto their own first read gateway when they show the Html, so they don't depend on the host either; RSS clients, browsers and other readers outside Seed follow the link as written. See Links in published content.

Reading EAS works the same way. The SDK and @seedprotocol/query read attestations from an EAS indexer's GraphQL API, by default EAS Scan's hosted endpoint for OP Sepolia (optimism-sepolia.easscan.org). The attestations themselves are in the EAS contract on-chain; the indexer is a convenience that anyone can run. See EAS indexing.

Edits add attestations

Attestations are never edited in place. Publishing an edit adds new attestations, and the old ones stay on-chain.

How much gets added depends on the publish mode:

  • patch (the default) attests only the properties you've changed since the last publish, against the item's current Version.
  • new_version creates a new Version attestation and attests every property that has a value against it: a full snapshot. Use it for a deliberate release. It needs the item to be published already.

Either way, readers pick the newest non-revoked attestation for each property, counting only attestations from the account that published the item, so the item reads as its latest state and no one else can add a newer value (Conflict detection). The full history stays on-chain for anyone who queries for it. When the SDK syncs from EAS, it keeps only the newest attestation for each property on each Version, so for full history, query EAS rather than the local database.

Files work the same way. Replacing a file uploads it again and attests the new ID; the old file stays on Arweave under its old ID.

Removing a value does too. clear() publishes the property's empty value, and reads of earlier Versions keep the old one. clear({ retract: true }) revokes the property's attestations on the next publish instead, which marks them as withdrawn rather than erasing them (below).

Revoking marks, it doesn't erase

Unpublishing an item (item.unpublish()) revokes its Seed first, then the property attestations the local database knows about, then its Versions, one EAS transaction per schema. EAS doesn't delete revoked attestations. It marks them with a revocation time, and readers that leave out revoked attestations, such as the SDK and @seedprotocol/query, stop showing the item.

What revoking doesn't do matters as much:

  • The attestation data stays readable. Inline values like a title are still in the revoked attestation, and anyone who queries for revoked attestations can see them.
  • The Arweave content stays put. Revocation is an EAS operation. The files and Html the item pointed at are still on Arweave, and anyone with a transaction ID can still fetch them.
  • Related items stay published. Images, files and Html bodies are separate items with Seeds of their own, and so are other items the post points at. Unpublishing the post doesn't revoke them.
  • It can't be undone. EAS has no unrevoke. Publishing the item again creates a new Seed with a new UID; the old Seed stays revoked.
  • Only the publisher's account can do it. Every attestation the Seed contracts create is revocable, and only the attesting account's owner can revoke. See Identity and signing.

So revocation means "this account no longer stands behind this content". It isn't deletion. Tell your users that before they publish anything they might want gone.

What "permanent" means today

The vision is that published content outlives every organization involved. If everyone who works on Seed Protocol walked away tomorrow, content already published would stay readable, editable by its owners and discoverable by anyone who wanted to build a new client. Ethereum is the network most likely to still be running in fifty years, which is why the canonical record is meant to live there.

Here's where that stands today:

  • The record is on a testnet. Attestations live in EAS on OP Sepolia. Testnets exist for testing, with no economic security behind them, and there's no mainnet deployment of the Seed contracts yet.
  • The record doesn't depend on Seed's contracts. Seeds, Versions and properties are plain EAS attestations, not entries in a Seed registry. If the Seed contracts were replaced, existing attestations would stay in EAS, readable by anyone.
  • The defaults depend on services. Uploads go through an upload API, which pays for Arweave storage, unless users pay through Turbo. File reads go through public gateways, starting with arweave.net (the order). Attestation reads go to EAS Scan, and gas is sponsored through thirdweb's paymaster when your thirdweb project has sponsorship turned on. Content already published doesn't need any of them to survive, but new publishes and your app's reads use them unless you configure alternatives.
  • Uploads go to Arweave mainnet. Arweave has no testnet, so files you publish are stored permanently on the main Arweave network and cost real AR, even while the attestations pointing at them are on a testnet. Don't upload anything during testing that you wouldn't want public forever.

What this means for your code

  • Nothing is safe until it's published. Treat the local database as a working copy. If losing a draft would hurt, publish it or back it up.
  • Confirm before publishing and before revoking. Publishing can't be taken back, and revoking leaves the content readable. Say so in your UI.
  • Don't publish secrets or personal data you might need to remove. There's no way to delete it later.
  • Choose a publish mode on purpose. Use the default patch for routine edits and new_version for snapshots you want to point at later.
  • Make gateways and endpoints configurable if your app needs to outlive Seed's hosted services.
  • Export what you need to keep yourself. exportArchive(item) writes a published item, its files and its attested values as a BagIt bag that standard BagIt tools can validate. See Item: Export a published item.
  • Check isRevoked before showing an item as published. getSeedPublishState() still reports 'onchain' for a revoked item, because its attestations are still on-chain. See Item.

On this page