Changelog
What changed in each @seedprotocol release from 0.5.1 through 0.8.0, by package: breaking, changed, added and fixed.
All @seedprotocol/* packages are released together and share one version number, so @seedprotocol/react 0.8.0 goes with @seedprotocol/sdk 0.8.0. Keep them on the same version. @seedprotocol/next starts at 0.7.7 and @seedprotocol/archive at 0.7.10. Dates are the npm publish dates (UTC). A package that isn't listed under a version had no user-facing changes in it. This page lists user-facing changes only; each package's CHANGELOG.md in the SDK repository lists its commits per release.
For upgrade steps, see Upgrading.
0.8.0
Released 2026-10-30. Upgrade steps: Upgrading from 0.7.13 to 0.8.0.
This release goes with seed-protocol contracts fb7ba92, which OP Sepolia has routed since 2026-10-30. The two can't be mixed: 0.8.0 sends the three-phase multiPublish (selector 0xa1263421), and the contracts no longer route the multiPublish that releases from 0.6.9 to 0.7.13 send. Deployments has the addresses. @seedprotocol/next and @seedprotocol/archive have no user-facing changes; the fix listed in next's own changelog touched a test fixture.
@seedprotocol/sdk
- Breaking: a repeat
client.initnever changes the stored addresses, whateveraddressesholds. Only the first init, or the first afterclient.stop(), applies them; once the client is running, useclient.setAddresses. Overlapping inits share one run, and the first call'saddresseswin.INIT_CONFIG_CONFLICT_MESSAGEno longer suggests init for new addresses. See Addresses. - Breaking:
getUnpublishedRelatedItemsis removed.summarizePublishUploads(item, { publishMode })returns the same list asunpublishedRelatedItems, withuploadCount,uploadBytesanduploadSizes, from one read of the publish graph and file sizes only.PublishUploadSummaryis exported. See Other read helpers. - Changed:
Model.createrefuses a model name, or a name inproperties, that can't publish, before it makes a model: it rejects, or throws withwaitForReady: false, withModel "Event" publishes as "event", …orProperty "view" publishes as "view", …. TheModel "X" could not create its properties:prefix is gone for this case, and nothing stays inschema.modelsorModel.getByName. See Reserved Solidity words. - Changed: renaming a ModelProperty to a name that can't publish fails validation:
validate()returns an error onnamewith codereserved_name, the row keeps the old name, and the instance shows the new one until a valid name replaces it. It returned no error. See Reserved Solidity words. - Changed:
getPublishModelsthrowsUnknownModelError, withmodelNameandschemaName, and the message isModel "Nope" not found., orModel "Post" not found in schema "other".. It threw a plainError(No model named Nope). See Other read helpers. - Changed:
client.resetLocalData()in Node throwsResetLocalDataBrowserOnlyError(code: 'RESET_LOCAL_DATA_BROWSER_ONLY', exported from both entries), with the same message. It threw a plainError. See Errors. - Changed: EAS sync skips a relation holding an unfilled reference, a non-zero
bytes32below 2^32, with aconsole.warnnaming the property, drops such members from a list, and doesn't fetch either as a related seed. Only0x…20was skipped. See Reading in: how EAS sync works. - Changed: a schema with no
$schemagetshttps://seedprotocol.io/schemas/data-model/v1in its file (it was.org), and$schemano longer counts toward the content hash. A source that sets$schemakeeps it, and the value isn't validated. After the upgrade each schema's stored hash changes once, with no log and no file rewritten. See Top-level fields. - Changed: a payload's reference into a list names the element it fills, with
listElement: trueandelementIndex, onepropertiesToUpdateentry per position, so a draft listed twice fills both. Attestations carry_easType. - Added:
backReferences(PublishBackReferences:'link' | 'defer', default'defer') onitem.getPublishPayload's options,validateItemForPublishandgetPublishGraph.'link'attests a relation back to an item the publish is still creating. See Other read helpers. - Added:
ItemPublishOptionsdeclaresconfirmUploadandmaxUploadBytes. The typeUpdateVersionUidPropsis exported from the main entry. - Fixed: in the browser, a directory listing (
Schema.load,listCompleteSchemaFiles,fs.promises.readdir) skips a file removed between the listing and its stat. It failed withTypeError: Cannot set property message of … which has only a getter.
@seedprotocol/react
- Breaking:
SeedProviderandSeedClientGateapply a changedaddresseswithclient.setAddresses, after their init settles and in order. A change to[]clears the stored addresses, which it didn't in 0.7.13, and leaving the prop out changes nothing. A remount changes no addresses, whatever the prop holds; in 0.7.13 a remount with a different non-empty set replaced them. A failedsetAddressesis logged withconsole.error. See Changing addresses.
@seedprotocol/publish
- Breaking: a contract publish calls the three-phase
multiPublish, selector0xa1263421, and needs the contracts deployed with this release. Against contracts that don't route it, a publish fails withManagedAccountPublishErrorcodeMULTI_PUBLISH_NOT_ROUTEDbefore simulating, naming both selectors.MULTI_PUBLISH_SELECTORandassertMultiPublishRouted(target, { executorModule })are exported for deploy checks. See Other exports. - Breaking: removed from
/thirdweb:ensureManagedSignerSessionKey,defaultApprovedTargetsForModularPublishandtoThirdwebSessionKeyPermissions. See Session keys without thirdweb. - Breaking:
ensureAutomationSessionKeyrequiresexpiresAtand refuses a time that has passed, with no ten-year default.@seedprotocol/publishnow exports it,removeAutomationSessionKey,isAutomationSessionActive,enrollPublishAutomation,revokePublishAutomationandensureExecutorModuleInstalled, none of which needs thirdweb. ThereadminoruserWalletis required;/thirdweb's versions default them to the connected in-app wallets. Both entries' enroll and revoke refuse auserWalletthat doesn't send as the account, withMANAGED_ACCOUNT_UNAVAILABLE, before sending. See Session keys without thirdweb. - Breaking:
ensureExecutorModuleInstalled(account, getAdmin, config)takes a function that returns the admin EOA as aPublishWallet, called only when an install has to be sent; on/thirdwebit may beundefined. The second argument was a thirdwebAccountit ignored. See ensureExecutorModuleInstalled. - Breaking:
enrollPublishAutomationdefaults to a 90-day key andPublishAuthorization, as doesattestPublishAuthorization'sexpirationTime. Both were 365 days. Keys enrolled before keep their end. See Enroll the key. - Breaking:
enrollPublishAutomationfails withAUTOMATION_ACCOUNT_FUNDED, before sending, when the account's balance plus EntryPoint deposit is above the newmaxAutomationExposureWei(default 0.001 ETH). A failed read of those fails withMODULAR_SIGNER_ACTIVATION_FAILED. An automation publish over the limit logs a warning and goes ahead. See Enroll the key. - Breaking: an automation publish refuses a session key approved for anything besides the configured executor, the zero address included, with
MODULAR_SIGNER_ACTIVATION_FAILED, before sending. It passed the simulation and failed at the bundler. Enrolling again replaces the key's targets. - Breaking: a publish whose uploads add up to more than
confirmUploadAboveBytes(default 25 MiB) fails withUploadNeedsConfirmErrorunless givenconfirmUpload: true, and so does one whose memory peak would pass a quarter ofnavigator.deviceMemory. OvermaxUploadBytes(default 100 MiB) it fails withUploadTooLargeError. Both come inchecking, from file sizes, on Node and with Turbo too, anditem.publish()rejects with the errors themselves.PublishConfiggainsmaxUploadBytes,confirmUploadAboveBytesanduplinkBitsPerSecond, andCreatePublishOptionsgainsmaxUploadBytesandconfirmUpload. See Upload limits. - Changed: a contract publish goes out as one transaction, whatever relations, storage seeds or list members it holds, so a failed publish leaves no related draft attested. It attests both sides of a cycle between drafts in that transaction. Direct EAS still goes request by request and leaves the last side of a cycle for the item's next publish. See Publish related items together.
- Changed: a contract publish is sent with its gas estimate plus 30%, falling back to 5,000,000 when the estimate can't be made. It was always 5,000,000. One that needs more gas than a block holds fails with
PUBLISH_TOO_LARGEbefore sending. See Publish related items together. - Changed:
encodeMultiPublishtakeslistElementandelementIndexon eachpropertiesToUpdateentry, writes the contract's placeholder into each slot a reference fills, setsvalueto the sum of the attestations'value, and refuses before sending what the contract would revert on. An automation publish with a non-zero total fails withAUTOMATION_PREFLIGHT_FAILED. A reverted publish names the new contract errors, andPublishTargetAlreadyAttestedandUnknownPublishLocalIdare gone. See How the SDK builds a batch. - Changed: a publish reads what it created from the publishing account's
Attestedevents on every route, includingattestPublishedBy,attestDomainOwnershipandattestPublishAuthorization.CreatedAttestationandSeedPublishedaren't read, so another UserOp's logs in the same bundle can't be recorded as this publish's. - Changed: enrolling an active key sends a grant whenever its end or targets differ from the request, so renewing moves the key's end. It attested the new end and left the key on its old one. A signed grant can be sent for one hour; it was ten years.
- Changed: with
useModularExecutor, a wallet that sends as itspublisherAddresswhile its signer is another address, with akindother than'eoa', publishes as it is, with no thirdweb prep. See Who signs and who sends. - Changed: a publish with no wallet takes the registered wallet in
checking, so the schema check runs. With none at all, aPublishManager.createPublishrun fails there withPublishWalletMissingError(code: 'PUBLISH_WALLET_MISSING'), before any upload.item.publish()stops earlier, with a plainErrorwith the same text. See item.publish() and publishItem. - Changed: the direct EAS error for a backward reference reads
Direct EAS publish from <address> can't fill a reference backwards: …; the code is stillPUBLISH_PREFLIGHT_FAILED. - Added:
@seedprotocol/publishloads withoutthirdwebinstalled. It needs it only on thirdweb paths, which import it when they run. See Entry points. - Added:
fromViemWalletClient(walletClient, { chain, kind, signAuthorization }), for wagmi and the kits built on it, andfromPrivyWallet(wallet, { chain, kind, signAuthorization }).SeedSignergains optionalsignTypedDataandsignAuthorization. See Adapters. - Added:
createManagedAccountTxSender(options)sends UserOps from a ManagedAccount through any EntryPoint v0.6 ERC-4337 bundler without thirdweb's SDK. It has run only against a local Alto bundler.CALL_GAS_HEADROOM_BPSis exported from the root entry too. See ManagedAccount without thirdweb. - Added:
estimatePublishCostreturnsupload, andestimateUpload(work, limits),DEFAULT_MAX_UPLOAD_BYTES,DEFAULT_CONFIRM_UPLOAD_ABOVE_BYTES,DEFAULT_UPLINK_BITS_PER_SECONDandPUBLISH_MEMORY_MULTIPLIERare exported. See estimateUpload. - Fixed: the EIP-7702 sender works on an EOA's first UserOp: it signs a real authorization once, with
SeedSigner.signAuthorization. No undelegated EOA could send through it. Browser wallets don't expose that call, so the browser adapters take asignAuthorizationoption, and without one the first send fails with an error that says so. It also takes fees from the ERC-4337 bundler only when the bundler answerspimlico_getUserOperationGasPrice. See The EIP-7702 sender. - Fixed:
disconnectPublishWallet()works before publish config exists, andConnectButtonwaits for the disconnect before it stops publishes; a failed disconnect is logged and still stops them. See Signing out.
@seedprotocol/query
- Breaking:
relationsset to anything but'expand'or'uids', such as'urls'from JavaScript, rejects withTypeError: relations must be 'expand' or 'uids', got "urls"before any request. See queryBySchema. - Breaking (types):
QueryBySchemaResultrequireslisted, the number of Seeds the listing returned for the page before Seeds with no Version are left out.listed < limitends the listing;listed === limitdoesn't promise more. See Returns. - Changed: the item cache codes are
e6('expand') ande7('uids'), so records cached by 0.7.13, on disk included, aren't read. See Caching. - Changed: a record leaves out a relation holding an unfilled reference and drops such members from a list; neither is fetched as a related seed. See SeedRecord.
- Added:
initializeQueryPlatform({ models })andsetQueryModels(models)take a schema file'smodels, so List relations come back under their schema keys (co_author_identity_idsasco_authorsandcoAuthors). Without models the key is still the first word of the EAS name. See setQueryModels. - Added:
getSeedAttestations(seedUids, { source, pins })returns each seed's record with the property attestations it's assembled from. See getSeedAttestations. - Fixed: only
bytes32andbytes32[]properties are relations. Astring external_idis back underexternal_idandexternalId, as in 0.7.12, keeps its value under'expand', and isn't fetched as a related seed. 0.7.13 returned it asexternal. The_idand_idsname rule applies only to an attestation with no type. See SeedRecord.
@seedprotocol/eas
- Breaking:
PUBLISH_AUTHORIZATION_DEFAULT_EXPIRATION_SECSis 90 days; it was 365. See Tool attestations. - Added:
isUnfilledReference(value)says whether a relation value is abytes32slot a publish never filled, non-zero and below 2^32. See Reading Versions and properties.
@seedprotocol/arweave
- Breaking:
probeGateway(url, signal)from JavaScript rejects with aTypeError(probeGateway takes options: pass { signal }, not an AbortSignal) before any request. The signal was ignored. See Other read helpers. - Breaking (types):
ArweaveTransactionis thearweavepackage'sTransaction, andcreateTransactionreturnsPromise<ArweaveTransaction>onBaseArweaveClient,IArweaveClientand the default client. Both wereany. Nothing changes at run time. See Transaction methods.
@seedprotocol/feed
- Fixed: in Atom, a present
nullin an item'supdatedorlinkskeeps the default.updated: nullgave an entry with no<updated>. See Field mapping.
@seedprotocol/mapping
- Breaking: the document lookup table is removed:
MappingDocument.lookups,MappingLookups, thelookupsoption ofapplyMappingandapplyMappingAsync,resolveLookup's third parameter, andlookupsonpreviewForEdge's options andbuildPropertyModeRows's argument. Lookups readlookup.entrieson the edge. A saved table is ignored with no warning, and its values count as misses. See Saving and loading a mapping. - Breaking:
lookupsandonLookupsChangeare removed fromFieldMapperanduseFieldMapper. WithoutrenderLookup, the built-in text editor shows on lookup rows;slots.lookups: falsehides it. It neededonLookupsChange. See Built-in editor. - Breaking:
connectSourceToPropertyreturnsFieldMapping[]. It returned{ mappings, clearedLookupProps }. - Fixed:
applyResolveToEdge(edge, 'lookup')keepssplit, souseFieldMapper().setTransform(rowId, 'lookup')on a lookup edge no longer clears it. See Edge helpers.
0.7.13
Released 2026-10-29. Upgrade steps: Upgrading from 0.7.12 to 0.7.13.
@seedprotocol/sdk
- Breaking: a model or property whose EAS name is a word the Solidity ABI reserves (
view,event,error,storage,struct,function,indexed,returnsand the other keywords) is refused. A schema file with one makesclient.initreject withCould not load config.schema: Schema <name>: <Model>.<property>: Property "view" publishes as "view", …, andModelProperty.create, andModel.createwith properties, reject. EAS couldn't register or attest such a schema, so publish failed at its first encode. AListofRelationis exempt. See Reserved Solidity words. - Breaking: an
Image,FileorHtmlproperty whose file isn't found reads asundefined. Its value was the storage seed's ID (Html), the file name (Image) or'No file found'(File). The newvalueStatus('resolved' | 'fetching' | 'missing', also onIItemProperty) says which case applies, andmissingFilegives the path looked for. A missing property reads its file again when a save of that file is announced, by a download or another tab. See valueStatus and missingFile. - Changed:
Item.findrejects with the newClientNotInitializedError(code: 'CLIENT_NOT_INITIALIZED') oncereadyTimeoutpasses without an open database, and client methods that need init throw it before init has finished and afterclient.stop()orclient.unload(). Both threw a plainError; the message is unchanged. Exported from both entries. See Errors. - Changed: a repeat
client.initwith the same config and emptyaddresses([], or{ owned: [], watched: [] }) keeps the stored addresses, so aSeedClientGateremount before a wallet connects doesn't drop them. A different non-empty set still replaces them,client.setAddresses([])clears them, and the first init, or the first afterstop(), with[]still clears them. See Addresses. - Changed: an edit that puts a property back to its latest attested value leaves no draft: when the write is applied, the property's drafts are deleted, so
getPublishPendingDiff, cost estimates and publish leave it out. It was listed with the same current and published value and attested again. Relations compare by the seed they name, lists member by member in order. Going back to an old value after sync stored a newer one is still an edit, and drafts written by 0.7.12 that equal the attested value stay until the next publish.getPublishPendingDiffalso leaves out a pending edit back to the published value before it's applied. See Writing. - Changed: recording a publish gives the seed, version and property rows that were unsealed drafts the attesting address as their publisher. A draft stamped with the EOA and published by the smart account named the EOA. A sealed row's publisher never changes.
- Added:
client.resetLocalData({ otherTabsTimeoutMs, filesDir }), browser only, stops the client from any state, has the database's other tabs stop theirs, deletes the database and every file underfilesDirfrom OPFS, and resolves with{ deleted, failed }; a laterinitstarts empty. Each tab holds the Web Lockseed:open:<db>while its database is open. The client snapshot'scontext.stopReason(ClientStopReason:'stop' | 'reset') says why a stopped client stopped. localStorage isn't touched. In Node it throws.ResetLocalDataOptions,ResetLocalDataResult,ClientStopReasonand the staticBaseFileManager.unmountFileSystem()are exported. See Deleting local data and When another tab resets. - Added:
getPublishModels(modelNames, { schemaName, followRefs })lists the models a publish of items of those models attests, with each property's publish kind, from the loaded definitions. Main entry only. See Other read helpers. - Added:
IItemProperty.savedeclares{ timeoutMs }, so a property fromitem.propertiesoruseItemPropertytakes it without a cast. See save. - Added:
@seedprotocol/sdk/nodeexportsentityLoadError. See Node entry point. - Added: lower-level publish helpers, used by
@seedprotocol/publish:recordPublishRequest, which records a request's version and property uids in one transaction, andpublishRemovesValue.applyPropertyAttestationUidsFromPublishtakespublisher, and its parameters type is exported asApplyPropertyAttestationUidsParams. - Fixed:
client.initafterclient.stop()with anotherfilesDiruses the new directory. It applied the first directory's schema files to the new database, and read and wrote files there. - Fixed: a Schema loaded after a ModelProperty edit or rename, or after a model was created at runtime, starts edited with those changes, so
saveNewVersion()writes them to the next version file. It loaded unedited and returned''. See saveNewVersion. - Fixed: the slow-save and removed-event warnings stay quiet in a browser production build, including one with no
processglobal.
@seedprotocol/react
- Changed:
useGlobalServiceStatus()typesstatusasClientManagerState | 'stopped'andinternalStatusasClientManagerState | 'ready' | 'stopped'(both werestring), and returnsstopReason:'reset'whenclient.resetLocalData(), in this tab or another, stopped the client,'stop'afterclient.stop()orclient.unload(). See useGlobalServiceStatus. - Changed:
useCreateModelProperty().createrejects withClientNotInitializedErrorwhile the database isn't open. It rejected withNo stored schema has the name or schema file id "<id>". See useCreateModelProperty. - Changed:
useItemre-renders when an idle item'sseedUid,latestVersionUid,latestVersionLocalId,lastVersionPublishedAt,attestationCreatedAt,revokedAtorpublisherchanges, from sync, another tab, a late publish record or an unpublish. It re-rendered only on busy, idle and error changes and value changes. See useItem. - Changed:
useCreateItemcompares arrays and plain objects in a call's values and options by contents when it decides whether a call repeats one in flight, so a double click whose values hold an array built in the handler creates one item. AFile,Blobor class instance still has to be the same object. See useCreateItem. - Added:
useItemPropertyreturnsvalueStatus, and re-renders when the property's file is found or missed. See useItemProperty.
@seedprotocol/publish
- Changed:
ensureEasSchemasForItemasks only for the schemas a publish of the item attests: each seed's own model, properties with a value or with an empty value that removes a live attestation, andstring storage_transaction_idfor a seed that uploads. It asked for every property of every model in the publish graph, so an automation run failed on a schema nothing would attest under, and an owner's publish registered schemas no attestation used. It checks every schema before registering any, and returns{ missing, registered }. Attestation steps no longer check schemas a second time. See Model and property schemas. - Changed: an automation key that meets missing schemas throws the new
EasSchemasNotRegisteredError(code: 'EAS_SCHEMAS_NOT_REGISTERED'), whosemissinglists every missing definition, before anything is sent. The plainErrorit replaces named the first one and pointed toensureEasSchemasForItem; the message now points toensureEasSchemasForModels. A publish run'scheckingFailederror is this instance. See Other exports. - Changed:
ConnectButton's disconnect removes the in-app wallet's sign-in token from localStorage, so a reload no longer signs back in a user who chose Disconnect. See Signing out. - Changed:
useItemPublishStatus().isActiveisfalseonce the run's value issuccess,failureorstopping, even while its final save is written. A run waiting forretrystays active. See useItemPublishStatus. - Changed: a contract publish records each request's version uid and property uids in one transaction, so
getPublishPendingDiffread right after a first publish no longer lists every property as pending. - Added:
ensureEasSchemasForModels(modelNames, account, { schemaName, followRefs, dryRun, managedAddress })registers, with no item, every EAS schema a publish of those models can attest under. Run it with the owner's wallet before automation publishes, and again after a schema version adds a property; withdryRunit returns what's missing and sends nothing. See Other exports. - Added:
describePublishRun(value)gives a publish machine value's displayphase, whether the run waits forretry(awaitingRetry) and whether it has ended (terminal), with the typesPublishRunDescriptionandPublishRunPhase. See Display helpers. - Added:
@seedprotocol/publish/thirdwebexportsdisconnectPublishWalletandmarkPublishWalletConnected, whichConnectButtoncalls on disconnect and connect, andclearInAppPublishSession, which removes the stored in-app wallet session for a "delete local data" action besideclient.resetLocalData().disconnectPublishWalletthrows before publish config exists. See Signing out. - Fixed: a Node script that published no longer stays alive up to 15 seconds after the publish succeeds.
- Fixed: the package no longer ships declarations for its tests.
@seedprotocol/query
- Breaking:
relations: 'urls'is removed, andRelationsModeis'expand' | 'uids'. It fetched the same related seeds as'expand'. Untyped code that still passes it gets relations as UIDs, with the related seeds still fetched. See queryBySchema. - Breaking: relations come back under their schema key in both
relationsmodes and in changelog entries:author_idasauthor, a list'sauthor_identity_idsasauthors, with a camelCase copy only when it differs.'uids'records, and'expand'ones whose related seed was missing, kept the EAS name and its camelCase copy (authorId). The key comes from the EAS name: a property whose EAS name ends in_idor_idsis read as a relation and loses the suffix, whatever its type, and a list's key keeps only the first word of its EAS name (co_author_identity_idscomes back ascos). The cache keys changed with the shape (the item codes aree3ande5, weree1,e0ande2), so records cached by 0.7.12 aren't read. See SeedRecord. - Breaking:
queryBySchema,queryBySchemaForMonthandassembleSeedsleave out a Seed with no Version from its attester, asgetSeedandgetSeedshave since 0.7.12. They returned it withversionUid: ''and no properties.limitandskipcount the Seeds listed before that, so a page can hold fewer items thanlimitwhile later pages have more. See queryBySchema. - Changed:
hydrateStoragetreats a gateway's 4xx other than 429, such as a 404 for a body it doesn't have, as a miss: it tries the next read gateway and leaves the first one's circuit closed. A 404 opened the circuit for 60 seconds. It fetches a/raw/<id>link as{gateway}/raw/<id>on each gateway, where it fetched{gateway}/<id>, and caches bodies by link form and ID. - Changed:
enginesdeclares Node 20.18.1 or later, as@seedprotocol/easdoes.
@seedprotocol/eas
- Breaking:
createEasNodeDispatcheris async and returnsPromise<Dispatcher | undefined>, andNodeEasClient's protectedgetDispatcher()returns a promise. It loads undici'sAgentwith a dynamic import that bundlers resolve from@seedprotocol/eas, so an app that bundles eas (Astro withssr.noExternal) no longer fails its first EAS request withCannot find module 'undici/lib/dispatcher/agent.js'.NodeEasClientcreates the dispatcher once and awaits it before each request. See registerNodeEasPlatform. - Added:
createEasGraphQLClient'sdispatcheroption takes a promise, awaited before each request; a request aborted meanwhile isn't sent. See Client classes.
@seedprotocol/arweave
- Breaking:
probeGateway(baseUrl, { signal, timeoutMs, recordCircuit })takes an options object; the signal was the second argument. It times out aftertimeoutMs(default 5,000) with or without a signal. A probe the caller aborts resolvesfalsewithout counting as a failure in the circuit breaker, where it opened the host for 60 seconds.recordCircuit: falseprobes a host whose circuit is open and records nothing. See Other read helpers. - Breaking:
DEFAULT_ARWEAVE_GATEWAYSisarweave.net,zigza.xyz,ar.seedprotocol.io, so reads, probes, status checks and failover (nextArweaveGatewayUrl,SeedHtml) no longer go topermagate.io. Its/<id>links still parse and get re-hosted: it's in the newKNOWN_ARWEAVE_GATEWAYS, whichisKnownArweaveGatewayHostnameand link parsing check. See The default gateway and its fallbacks. - Changed:
resolveSeedGatewayEndpointstakestimeoutMs, one budget for all of a resolve's probes. A probe cut short by it or bysignalisn't cached; it was cached as unreachable for 3 minutes. A failed probe is cached for 60 seconds, a healthy one for 3 minutes. See resolveSeedGatewayEndpoints. - Changed:
ensureReadGatewaySelected(signal)caches nothing whensignalaborts before a gateway passes, and returns the last healthy host, else the current one. It cached the aborted probe as no healthy gateway for 60 seconds. See ensureReadGatewaySelected. - Changed: before endpoints resolve,
getPublicArweaveBaseUrlreturns the read host only when it's a gateway's root. A read host with a path prefix, such as an app proxy, gives the env host orhttps://arweave.net, as a loopback address does; it was returned, and a publish at that point wrote it into Html. - Changed:
parseArweaveTransactionLinkreads{base}/<id>as a link whenbaseis a read base URL with a path prefix, such as an app proxy athttps://app.example/api/seed-gateway, sorehostArweaveLinksInHtml,publishedArweaveLinksInHtmlandSeedHtml's failover handle such links. A base with a path prefix no longer makes/<id>on its host's root a link.nextArweaveGatewayUrlmatches the longest base a URL is on. See Links in Html. - Added:
resolveSidecarOrigin(config)gives the local Hyper sidecar's{ host, port, baseUrl }.cancelGatewayHostHalfOpenProbe, theProbeGatewayOptionstype andARWEAVE_GRAPHQL_TIMEOUT_MS(30000) are exported. See resolveSeedGatewayEndpoints and Constants. - Fixed:
fetchTransactionStatusAcrossGatewayshas its doc comment in the published declarations again.
@seedprotocol/feed
- Breaking: an item with a storage transaction and no link of its own has the RSS
guidar://<txId>, so its Atom entry ID and JSON Feed item ID are that too. Its link is stillhttps://<public host>/raw/<txId>. The guid was that link, so it changed witharweaveDomain; feed readers show these items once more, and a later gateway change doesn't repeat them. Aguidis no longer moved onto the public gateway with an item's other Arweave links. A cached document keeps the old guids until it's rebuilt. See Field mapping. - Changed:
enginesdeclares Node 20.18.1 or later. - Fixed: an Atom entry keeps an item's own
title,updatedandlinks; 0.7.12 replaced them with the defaults. A field that's present butnullreplaces the default too, soupdated: nullgives an entry with no<updated>.
@seedprotocol/feed-hyper
- Changed:
enginesdeclares Node 20.18.1 or later (it was 20).
@seedprotocol/gateway-hyper
- Fixed: a client refused by address gets the
403for every request; sequential requests alternated between the403and a502(Unexpected end of stream). The operator's403carriesconnection: close, and the client opens a new stream after it. Both the operator and the client need 0.7.13. See Restricting who can connect.
@seedprotocol/mapping
- Breaking:
LookupEntryis{ value: string; refs: string[] }, an ordered list of refs, andrefis removed from the type. A stored{ value, ref }still applies asrefs: [ref], andlookupEntriesFromMappingreturns it in the new shape. AListtarget gets every ref, aRelationtarget the first.lookupRefFromEntriesis replaced bylookupRefsFromEntries, which returns the list, andresolveLookupMappedbyresolveLookup, which returns{ refs, unmatched, split }.FieldMapperLookupRowhas a requiredonSplitChange. See LookupEntry. - Changed: lookup entries match on the new
lookupKey(NFC, trimmed, whitespace collapsed, case-folded), so'mira chen'finds the'Mira Chen'entry.normalizeLookupKeyadds NFC and keeps case. The deprecated document-levellookupstable still matches exactly. The fallbackLookupEditorshows one row per key and takes refs separated by commas or spaces. See Relation lookups. - Changed: an empty source value on a lookup edge is no lookup:
applyMappingAsyncdoesn't list it as an error. - Changed:
autoMap(sources, targets, previous)keeps the entries andsplitof a property that stays a lookup inprevious, anduseFieldMapper's Auto-map passes its current mappings, so Auto-map no longer drops every assignment.withPreservedExtraskeeps a lookup's entries andsplitwhen it moves to another source. See autoMap. - Added: a lookup edge takes
split: { separators }('and','&',',';DEFAULT_LOOKUP_SEPARATORSis['and', '&']): a value with no entry of its own is split and each part looked up, with the known parts' refs applied in order. NewsplitLookupValue,withLookupSplit,useFieldMapper().setLookupSplitand the typesLookupSeparator,LookupResolutionandUnmatchedLookup. See Relation lookups. - Added:
applyMappingtakesonUnmatched, called with{ sourceId, propertyName, value, splitFrom? }for each lookup value with no entry; onlyapplyMappingAsyncreported misses.collectLookupValues(items, mapping)lists each distinct value a lookup's source holds across items. See applyMapping. - Fixed: an
extractorfileedge on an empty source passes''as the resolve callback'surl, and lookup samples, previews andLookupEditoruse the source's value only. They fell back to the source's label, so the field's name went to the callback as a URL, and an entry written in the editor for an empty source never matched.
@seedprotocol/vite
- Fixed:
seedVitePlugin's doc comment is on the function in the published declarations again, so editors describe it.
0.7.12
Released 2026-10-27. Upgrade steps: Upgrading from 0.7.11 to 0.7.12.
Every package's .d.ts files resolve under moduleResolution: node16 and nodenext: relative imports carry .js or /index.js. Under those settings, names re-exported with export * were missing (TS2305) and named re-exports were typed any. drizzle-orm's own declarations still fail without skipLibCheck, so keep it on.
@seedprotocol/sdk
- Breaking: validation errors carry named codes, in
ItemCreateValidationError.problems[].code,ItemPropertySaveValidationError.validationErrors[].codeandvalidate()results:typefor a wrong type,pattern,min_length,max_length_exceeded,enum_violation,formatfor a Date that isn't one andunknown_data_type, and in a property definition alsorequired,unknown_field,minimum,maximumandrule_not_applicable. Type andpatternfailures were TypeBox's numbers as strings ('41','53','62'), and aminLengthfailure was codedmax_length_exceededwith a "characters or less" message. Anenummiss is one problem whose message lists the allowed values; it was two, and every other problem on a property with anenumwas codedenum_violationtoo. The 255-characterTextmessage suggestsvalidation.maxLengthor anHtmlproperty instead of aFileproperty. See ItemPropertySaveValidationError. - Breaking:
pattern,minLengthandmaxLengthon a property that isn'tTextorHtml(or aListof those) fail withrule_not_applicable, and an unknown type name such asIntegerfails withunknown_data_type;blobis no longer read asBlob. A schema file with either is rejected when it loads, before anything is stored, soclient.initrejects. The rules were ignored and unknown names kept.validation.customis ignored with a warning: the schema loads, and the property'svalidate()lists it underwarnings. See Validation. - Breaking:
Item.getById(id)takes no cache hold unless called with{ hold: true }; without one the item stays cached for the grace period, as after afind.Item.createByIdstill holds. See Cache helpers. - Breaking:
Item.findcalled beforeclient.initor afterclient.stop()rejects oncereadyTimeoutpasses (15 seconds by default), with a plainErrorwhose message isClientManager is not initialized. Please call init() first.and which has nocode. It resolvedundefined, as for an item that doesn't exist. Afindcalled while init runs waits for the database. See Item.find. - Breaking:
Item.createchecks its initialRelationandListofRelationvalues when its write is applied, as an assignment's are. AseedLocalIdthat names no item rejects the create withOutboxOpFailedError(Relation target not found: <id>,errorName: 'RelationTargetError'), and a target whose own create is still queued is waited for. The create used to store the dangling ID.seedUids and the new item's own ID pass. See Validation. - Breaking:
getArweaveUrlForTransactionis@seedprotocol/arweave's, which returns{first read gateway}/raw/{id}. The SDK's deprecated copy is removed. See Media references. - Breaking:
DEFAULT_ARWEAVE_HOST, and so the defaultSeedConfig.arweaveDomain, isarweave.net(it wasar.seedprotocol.io), from@seedprotocol/arweave. Reads try it first, and publish writes it into Html links. An app that wants Seed's gateway setsarweaveDomain: 'ar.seedprotocol.io'. See SeedConfig. - Changed:
client.removeLocalCopiesForAddresses(addresses, options?)keeps a copy whose publisher is still an owned address and that has an unpublished edit or a pending outbox write, and lists it in the result's newkeptSeedLocalIds. It deleted those copies, and their edits, with the rest;{ includeUnpublished: true }still does. See client. - Changed: an Html property's value shows its Arweave links (
ar://<id>,/raw/<id>on any host,/<id>on a known gateway) on this reader's first read gateway. The stored file keeps the links as written, andexportArchiveexports it unchanged. See How readers re-host links. - Changed: publish uploads an unpublished Html value with every Arweave link on the public gateway (
arweaveDomain) in its path form, or asar://<id>with publish'shtmlArweaveRefs: 'ar', and saves the file that way. A link that a reader had re-hosted onto a sidecar or app proxy, and that an editor saved back, was uploaded as that local URL.prepareHtmlEmbeddedImagesForPublishandrewriteHtmlEmbeddedImagesOnDisktake{ htmlArweaveRefs }. See What publish writes. - Changed: a publish waits only for the pending outbox writes on the items in its graph, and reads the graph again if any were pending. It waited for every pending write in the database, so one stuck write held up every publish.
getPublishGraphand what's built on it (exportArchive,validateItemForPublish,getPublishPayload, the upload and cost summaries) wait the same way. See Other read helpers. - Changed: sync records no conflict for a saved Image, File or Html draft, and the draft stays the value. Only edits still in the outbox were exempt. See Conflicts.
- Changed: on Node,
client.stop()andBaseDb.close()complete openBaseDb.liveQuerysubscriptions, which got aFailed query: select …error instead. Node also deletesentity_changesrows older than 10 minutes when the database opens and every minute after, on a timer that doesn't keep the process running; the table grew without limit. See Browser and Node. - Changed: outside production, emitting
syncDbWithEasorfs.downloadAll.requestwith no listener logs a warning once per event, namingclient.syncFromEas(). See Events. - Changed: an automatic sync (at startup, on an address change) merged into a run that a
client.syncFromEas()call started is retried when that run fails. See Offline retries. - Changed:
ItemProperty.storagePropertyNameis the name new rows are stored under:authorIdfor a single Relationauthor(it wasauthor), and<singular name><Model>Idsfor a List of Relation. See Identity fields. - Changed:
ItemPublishOptions.dataItemSigneris typed as an object withaddressandsignMessage, such as an ethersWalletor aSeedSigner. Any value type-checked, a string included. - Added:
ItemProperty.save({ timeoutMs })rejects with the newOutboxSaveTimeoutError(code: 'OUTBOX_SAVE_TIMEOUT', the queued write'sopId) once that many milliseconds pass, and cancels nothing: the write stays queued. WithouttimeoutMsa save still waits with no limit, and outside production one still waiting after 10 seconds logs a warning naming the property. TheIItemPropertytype doesn't declare the option.OutboxSaveTimeoutErroris exported from both entries. See save. - Added: when
save(),Item.createor a delete rejects withOutboxOpFailedErrorbecause of a missing relation target or a file whose bytes were never stored,causeis aRelationTargetError(withtargets) or aStagedValueMissingError(withpath), rebuilt from the stored failure. No caller got an instance of either class before. - Added:
entityLoadError(instance)returns the error an entity's last load failed with while itsstatusis'error'. Exported from the main entry only. See entityLoadError. - Added:
@seedprotocol/sdk/nodeexportsTabLockTimeoutError. See Node entry point. - Added: the main entry re-exports
rehostArweaveLinksInHtml,nextArweaveGatewayUrl,mapUrlAttribute,parseArweaveTransactionLinkand the typesArweaveTransactionLinkandHtmlArweaveRefsfrom@seedprotocol/arweave. See Links in Html. - Fixed: a property assigned right after
Item.create(…, { waitForReady: false })is saved. In a process's first creates, the assignment was lost once the properties loaded. - Fixed:
Item.create({ modelName: 'Post', title: 'Hello' })type-checks as an object literal; the property values failed the excess-property check. ThePropertybuilders from@seedprotocol/sdk/nodekeep their own parameter types, soProperty.Html({ htmlEmbeddedDataUriPolicy: 'preserve' })type-checks, andProperty.List()without an element type and a third argument toProperty.Relationthat isn't a boolean don't. See Property builders.
@seedprotocol/react
- Breaking:
useModelProperty(schemaId, …)anduseCreateModelProperty().create(schemaId, …)no longer cut a trailing-<n>off a schema ID that no stored schema has as its name or file ID, soblog-2doesn't resolve toblog.useModelPropertyfinds nothing for such an ID, andcreaterejects withNo stored schema has the name or schema file id "<id>", also when called before the app database is open. See useCreateModelProperty. - Changed:
useGlobalServiceStatus()reportsstatusandinternalStatusas'stopped'afterclient.stop()until the next init starts. Both read'idle', so a stopped client looked ready.statusis typedstring(it wasClientManagerState). See useGlobalServiceStatus. - Changed:
useCreateItemhands a running create to a second call only when it's the same call (same model, values and options equal key by key), as from a double click; another call creates its own item. A value that's a new array or object counts as different.isLoadingstaystrueuntil every create finishes. See useCreateItem. - Changed:
SeedHtmlre-hosts the Html's Arweave links onto this reader's first read gateway before callingsanitize, soar://links reach the sanitizer ashttpsURLs. When animg,video,audioorsourcein it fails to load, it moves to the same transaction on the next read gateway.sanitizeruns whenhtmlorsanitizechanges, not on every render. See SeedHtml. - Added:
attachArweaveLoadFailover(container)adds that failover to markup an app renders itself withSeedHtml'srenderprop. See attachArweaveLoadFailover. - Added:
isClientReadySnapshot, the SDK's, is re-exported. See useIsClientReady. - Fixed:
useCreateModel,useCreateModelPropertyanduseCreateItemPropertyseterrorto the entity's own load error when there is one, asuseCreateSchemadoes. They always set a generic "Failed to create …" message. - Fixed:
useItemPropertyre-renders when only the property'sconflictchanges, from sync or another tab.useItemPropertiesre-renders after another tab's save. See useItemProperty.
@seedprotocol/publish
- Changed: every publish holds its item until the publish stops (it succeeds, fails or is stopped).
createPublishtakes the hold, soPublishManager.createPublish,ensureWalletThenPublishand direct callers get it, not onlyitem.publish(). See PublishManager.createPublish. - Changed:
createPublishpicks the data item signer for every entry point: the option'sdataItemSigner, else the config's, else the registered session's when that session publishes as the address, else the wallet's owndataItemSigner, else itssignerunless it'skind: 'smart'.PublishManager.createPublishcalled directly set none, so a bundler or Turbo publish failed at signing, anditem.publish()let the session's signer override a configureddataItemSigner. See CreatePublishOptions. - Changed:
PublishWalletKindadds'modular', the modular executor's in-app EOA, which publishes as the user's ManagedAccount.ConnectButtonwithuseModularExecutorandensureSmartWalletThenPublish's modular path register it as'modular'; they said'smart', which tells publish the signer can't sign data items. Direct EAS refuses'modular'as it does'smart'. See Wallets. - Changed:
ensureSmartWalletThenPublishsigns data items with the registered session'sdataItemSignerunderuseModularExecutor, not the in-app account's signer. WithoutuseModularExecutorand with no registered session, it signs with the connected account when that's the smart wallet's admin EOA; it had no signer, and bundler publishes failed at signing. - Changed:
useSeedWallet().connectregisters the wallet'skind, so a direct-EAS publish from an EIP-1193 or ethers wallet works withoutuseDirectEas. See useSeedWallet. - Changed: a publish whose revocation of retracted properties fails rejects with the new
PublishRetractionError(code: 'PUBLISH_RETRACTION_FAILED'), which says the new attestations are in place and thatPublishManager.retryAttestations(seedLocalId)sends the revocation again. It rejected with the attestation-failure message. See item.publish() and publishItem. - Changed:
checkingrefuses a publish with no publisher address, and a wallet that can't revoke a pending retraction, before it registers EAS schemas, so a refused publish no longer pays for them. Validation still runs after registration. See What a publish does. - Changed: cost estimates count charged bytes on the signed data items: each upload plus its ANS-104 header and tags. With user-paid storage,
estimatePublishCostreads Turbo's free data item size from its/v1/info(cached 10 minutes per upload URL) and falls back to 102,400 bytes; it always used 102,400. Estimates report it asarweave.freeBytes, and where uploads go asarweave.uploadTo. See estimatePublishCost. - Changed: the gas heuristic adds 35,000 per revocation (
PUBLISH_GAS_HEURISTICS.REVOCATION, not measured), andPublishWorkSummarydeclaresrevocationCount. - Fixed:
watchPublishCost's price refresh keeps the estimate's gas, charged bytes, payers,uploadTo,freeBytesandtestnetand applies the new rates, through the newrepricePublishCostEstimate. It repriced a Turbo or testnet estimate as an upload-API mainnet one. See watchPublishCost. - Fixed:
usePublishCostEstimateshows apreviousEstimatepassed in its options until its first estimate arrives. See usePublishCostEstimate. - Fixed: a tab that opens while another tab is uploading no longer marks that publish
interrupted. - Added:
PublishProvidertakesseedConfig,addressesandonInitErrorand passes them to theSeedProviderit renders, which starts the client whenseedConfigis set. See PublishProvider. - Added:
htmlArweaveRefs, in theinitPublishconfig and thecreatePublishanditem.publish()options.'gateway'(the default) writeshttps://<public gateway>/raw/<id>into published Html;'ar'writesar://<id>, which loads only in a Seed reader. See Options. - Added:
MissingSignedDataItemsErroris exported.uploadApiBaseUrlis optional in thePublishConfigtype;initPublishstill requires it unlesspayment.storageis'user', and the resolved config's is''when unset.
@seedprotocol/vite
- Breaking:
seedVitePluginthrows on an option it doesn't take.autoInitsays it was removed, and any other unknown option names the ones it takes (fsModules,debug,includeNodePolyfills,isolationHeaders). Both were ignored. See Options. - Changed:
includeNodePolyfills: truealso polyfillstimers(timers-browserify), so@seedprotocol/mapping'srssXmlToSourcesbuilds for the browser. See With includeNodePolyfills. - Changed:
debug: trueskips chunks made only of Seed packages' modules when it reports Node globals. A chunk that mixes app and Seed code is still scanned. See Warnings. - Fixed:
@zenfs/core(^2.7.7) and@zenfs/dom(^1.2.14) are declared as optional peers.
@seedprotocol/next
- Added:
withSeed'sdiroption, the directory it resolves the SDK andnextfrom and makes Turbopack alias paths relative to (defaultprocess.cwd()). Pass the config file's directory when Next.js runs from somewhere else, such as a monorepo root, where the aliases were skipped. See Options.
@seedprotocol/mapping
- Breaking: the
--fm-*and--map-*CSS custom properties are removed. Theme with--sfm-*; wire strokes default tovar(--sfm-map-N, currentColor). See Classes and CSS variables. - Changed:
applyMappingandapplyMappingAsynccopy a source'svalueonly, and lookups match itsvalueonly. An empty frontmatter field or Markdown body copies as''; the field's name, or "Full document", was copied. See applyMapping. - Fixed:
coerceValue(raw, 'Boolean')readstrue,1andyesin any case, trimmed;"TRUE"wasfalse. See coerceValue. - Fixed:
useFieldMapperkeeps each row's ID when another row is added, removed or edited, so React keys no longer shift.
@seedprotocol/arweave
- Breaking:
DEFAULT_ARWEAVE_HOSTisarweave.net: the default read host,resolveSeedGatewayEndpoints's defaultarweaveDomain, and the host publish writes into Html links.DEFAULT_ARWEAVE_GATEWAYSisarweave.net,permagate.io,zigza.xyz,ar.seedprotocol.io, andDEFAULT_ARWEAVE_GRAPHQL_URLishttps://arweave.net/graphql. They werear.seedprotocol.io, a list that started with it, andhttps://ar.seedprotocol.io/graphql. An app that wants Seed's gateway setsarweaveDomain: 'ar.seedprotocol.io'. See The default gateway and its fallbacks. - Breaking:
getArweaveUrlForTransaction(id)returns{first read gateway}/raw/{id}, the URL this reader loads from; it returned{read host}/{id}. The SDK and query re-export this one. For URLs that leave the reader, usegetPublicArweaveRawUrl. See Media references. - Breaking: in
http-gatewaymode, and hybrid's HTTP fallback, with nouploadApiBaseUrl,resolveSeedGatewayEndpointsresolves it to''. It fell back to the gateway's base URL. Set it when your gateway also serves the upload API. See resolveSeedGatewayEndpoints. - Changed: in a browser,
hybridtries the local sidecar only from a loopback page origin or with the newhyper.probeSidecarFromBrowser: true;transport: 'hyper'is unchanged. The newhyper.probeProxyturns the proxy's health check on or off by itself (it defaults toprobeSidecar, which turned both). See SeedGatewayConfig. - Changed: with a sidecar or app proxy active, the read gateways continue after it with the public gateway and the defaults, so
/rawreads fall back to them.fetchTransactionStatusAcrossGatewaysstill asks only the sidecar or proxy and the preferred gateways then. - Changed:
getArweaveReadGatewayHostsForPrimarykeeps every default after a custom primary; it droppedar.seedprotocol.io. - Changed: before endpoints resolve,
getPublicArweaveBaseUrlreturns the read host unless it's a loopback address, else the env host orhttps://arweave.net. An emptyarweaveDomainmeans the default host; it producedhttps:///raw/<id>. - Changed:
DefaultArweaveClientandNodeArweaveClientGraphQL requests abort after 30 seconds (Arweave GraphQL request timed out after 30000 ms) or when the caller's signal does. They had no limit. See Transaction methods. - Added: helpers for Arweave links in Html:
rehostArweaveLinksInHtml,publishedArweaveLinksInHtml,parseArweaveTransactionLink,buildArweaveTransactionLink,nextArweaveGatewayUrl,rewriteHtmlAttributesandmapUrlAttribute, and the typesArweaveTransactionLink,ArweaveLinkOptionsandHtmlArweaveRefs. The package depends onparse5. See Links in Html.
@seedprotocol/query
- Breaking:
getSeedreturnsnullfor a Seed with no Version from its attester, andgetSeedsleaves it out. Both returned a record withversionUid: ''and no properties. Withsource: 'auto', such a Seed in the local database is read from EAS.queryBySchemastill returns it. See getSeed. - Breaking:
QueryDataSource.listSeedsByUidPrefixis removed.listSeedsBySchemaName(schemaName, opts)takesuidPrefixandattestersinopts(theSeedListFiltertype) and applies them beforelimitandskip. A custom source that reads onlylimitandskipstill type-checks but ignores both filters. A customgetSeedByUidgets the UID in lowercase. See QueryDataSource. - Breaking: image
arweaveUrlfields and storage URLs in assembled Seeds are{first read gateway}/raw/{id}, from arweave'sgetArweaveUrlForTransaction; they werehttps://{read host}/{id}.isArweaveTransactionGatewayUrlaccepts/raw/{id}on any host and/{id}on a known gateway. See SeedRecord. - Changed:
getSeedlowercases the UID, so a mixed-case UID finds the Seed, and the record'sseedUidis lowercase. - Changed:
hydrateStoragefetches each body from the read gateways in order, skipping gateways whose circuit is open, instead of only the URL in the field, and re-hosts the Arweave links in each body onto the first read gateway. - Changed: reads with
source: 'local'don't throwDuplicateEasModuleErrororDuplicateArweaveModuleError, since they use neither client;initializeQueryPlatformtakessource. See DuplicateEasModuleError or DuplicateArweaveModuleError. - Added:
queryBySchema(schemaName, { attesters })keeps Seeds attested by one of the addresses, in any case. On the remote source it's part of the EAS request, beforelimitandskip. Entries that aren't addresses are dropped, and with none left there are no items. LikeuidPrefix, it skips the collection cache.normalizeAttestersis exported. See One publisher's items.
@seedprotocol/eas
- Breaking:
normalizeBytes32Hexreturns''unless the value is 64 hex digits, with or without0x. It padded short values and turned non-hex characters into0. The newbytes32FromNumberHexpads a decoded BigNumber'shex. See UID and address helpers. - Breaking:
toSnakeCaseis removed. UseeasName. - Changed: the 30-second indexer timeout holds when a caller passes a signal, and where
AbortSignal.anyorAbortSignal.timeoutis missing. A timeout rejects withTimeoutError: EAS indexer request timed out after <n> ms(it wasThe operation was aborted due to timeout), and the caller's abort withAbortError. See Client classes. - Changed:
enginesdeclares Node 20.18.1 or later. - Added:
getSeedsBySchemaName(schemaName, limit, skip, { attesters })keeps Seeds from those attesters; with no valid address in the list it returns[]without a request.checksumAddressesis exported, andgetSeedsFromSchemaUids,getPublishedByFromEas,getDomainOwnershipFromEasandgetPublishAuthorizationFromEasuse it, so a non-address in their address lists is left out of the request. See getSeedsBySchemaName.
@seedprotocol/feed
- Breaking: Atom entry IDs and JSON Feed item IDs are the item's RSS
guid, so the three formats identify an item the same way. They were the Seed UID, so subscribers to an existing Atom or JSON feed see each item once more as new. An item's ownupdatedandlinksno longer override Atom's. See Field mapping. - Changed: every Arweave link in a feed item, in link fields and inside Html, is on the public gateway (
arweaveDomain, defaultarweave.net). An item with its own storage transaction links tohttps://<host>/raw/<id>(it washttps://<host>/<id>), and the handlers use that link as itsguid, so anImagefeed's RSSguids change too and RSS readers show those items again. - Fixed: the stale fallback serves only a document built for the same site config; an entry cached before the upgrade isn't served stale until it's rebuilt. A weak
If-None-Match(W/"…") for the current ETag gets a304. An archive request whose year or month isn't a number gets a400, not a500. See Caching.
@seedprotocol/feed-hyper
- Fixed: the declarations include module declarations for corestore, hyperdrive, hyperswarm, hypercore-id-encoding and serve-drive, so they compile without
skipLibCheck.
@seedprotocol/gateway-hyper
- Breaking: the default
upstreamTimeoutMsis 130,000 (it was 30,000) andrequestTimeoutMs150,000 (it was 60,000), in the CLI too. Both are idle limits, set above the 120 seconds publish waits for an upload batch. See serveTunnel. - Breaking:
storePathis removed from theserveTunnel,connectTunnel,createGatewayProxyandcreateTunnelSessionoptions. Nothing read it. - Breaking:
serveTunnelthrows for anhttps://upstream, soseed-gateway serve --upstream https://…stops. The framed tunnel speaks plain HTTP to its upstream;serveRawTunnelcarries TLS end to end. See seed-gateway serve. - Breaking:
serveRawTunnelthrowsPass keyFile or keyPair, not bothwhen given both.keyPairwon. See serveRawTunnel. - Changed: an operator that refuses a client by address answers its request with a
403that says so, instead of resetting the stream. A framed reader that gets plain HTTP says the other side runs in raw mode, instead ofFrame too large: 1213486160 bytes. See Restricting who can connect. - Added:
serveTunneltakeskeyPair, asserveRawTunneldoes.seed-gateway serveandconnecttake--bootstrap host:port,…, warn about each flag the mode ignores, and print their own usage with--help.formatTunnelEventis exported. See seed-gateway CLI.
@seedprotocol/archive
- Changed:
enginesdeclares Node 20.12 or later.
0.7.11
Released 2026-10-24. Upgrade steps: Upgrading from 0.7.10 to 0.7.11.
@seedprotocol/sdk
- Breaking:
@seedprotocol/sdk/nodeexportsNodeDbandNodeFileManagerin place of the deprecatedDbandFileManager, which are removed. The classes are the same. - Breaking: migration 0030 drops the
property_uidstable, andpropertyUidsis no longer exported. Nothing wrote to it. - Changed: a local database that looks older than 0.7.7 is copied to
seed.before-recreate-<ms>.dbbesideseed.db(in OPFS in the browser) before it's recreated, and the warning names the copy. If the copy fails, or the database is a remote libsql URL, it's opened as it is. A 0.7.7 database whose background clean-up never finished looks older, so 0.7.8 to 0.7.10 recreated it without a copy. See Storage. - Changed: with
NODE_ENV=testorIS_SEED_DEVset,client.initrejects when storing its config or app state, adding the schema's models or running Node migrations fails, as it does without them. It used to carry on. - Added:
OutboxOpFailedError,RelationTargetErrorandStagedValueMissingErrorare exported from both entries;save()rejects with the first.@seedprotocol/sdk/nodealso exportsEntityUnloadedError,ItemCreateError,PublishValidationFailedError,RelatedItemUnpublishedError,HtmlEmbeddedDataUriLimitError,HtmlSeedPropertyRefError,exportArchiveandArchiveExportError. See Node entry point. - Fixed:
clear()on an unloaded ItemProperty rejects withEntityUnloadedErrorbefore it writes. It cleared the value and then rejected. - Fixed: a property saved after a publish registered its EAS schema stores that schema's UID, so
property.schemaUidhas it. It stayed empty until the indexer had the schema. Saving a draft value again fills in a missing UID.
@seedprotocol/publish
- Breaking:
configurePublish(an alias ofinitPublish) andSEED_PROTOCOL_CONTRACT_ADDRESS_OP_SEPOLIA(the old name ofMULTI_PUBLISH_ABI_REFERENCE_ADDRESS_OP_SEPOLIA) are removed. - Changed:
enrollPublishAutomationdefaultsexpiresAtto 365 days from now and gives the session key and its PublishAuthorization the same value. WithoutexpiresAt, the key used to get thirdweb's ten-year window. - Changed:
attestPublishAuthorizationthrows whenwalletisn't theidentityaccount. - Fixed:
assessPublishAuthorizationLivereports a revoked grant as'revoked'(it returned'valid') and sees the grant's EAS expiration, so a grant past it is'expired'.assertStorageBoundToIdentity(from/thirdweb) no longer accepts a revoked or expired grant. See Tool attestations. - Fixed:
attestPublishAuthorization,attestPublishedByandattestDomainOwnershipcheck their inputs before registering or naming a schema, so a call that throws sends no transaction.
@seedprotocol/eas
- Breaking: the deprecated
EasClientandQueryClientaliases are removed from@seedprotocol/eas/node. UseNodeEasClientandNodeQueryClient. - Changed:
assessPublishAuthorizationtakesrevokedand returns the new status'revoked'for a revoked grant. Aswitchover everyPublishAuthorizationAssessStatusneeds the case. - Changed:
getEasSchemaUidForExactDefinition,getEasSchemaUidForSchemaDefinitionandgetSchemaUidForSchemaDefinitionFromCacheshare one cache, keyed by EAS endpoint and exact definition (string title), whichsetSchemaUidForSchemaDefinitionwrites. The keys were snake-cased and shared across endpoints. See getEasSchemaUidForExactDefinition. - Added:
GET_PUBLISH_AUTHORIZATIONS, which selectsexpirationTimeandrevoked.getPublishAuthorizationFromEasuses it, so the grants it returns have both.
@seedprotocol/arweave
- Breaking: the deprecated
ArweaveClientalias is removed from@seedprotocol/arweave/node. UseNodeArweaveClient. - Changed:
DEFAULT_ARWEAVE_GATEWAYSno longer listsarweave.devandg8way.io, which don't resolve: it'sar.seedprotocol.io,arweave.net,permagate.io,zigza.xyz. See How the SDK reads. - Fixed:
fetchTransactionStatusAcrossGatewaysdoesn't start another gateway attempt when less thanMIN_TRANSACTION_STATUS_ATTEMPT_MS(1000, new) ortimeoutMsof the budget is left, whichever is smaller, so a near-zero attempt no longer times out and counts against the gateway. The first gateway is always asked.
0.7.10
Released 2026-10-24. Upgrade steps: Upgrading from 0.7.9 to 0.7.10.
@seedprotocol/sdk
- Breaking:
Item.find,Item.all,ItemProperty.findandItemProperty.alltake no cache hold unless called withhold: true.Item.createandItemProperty.createstill hold by default, andItem.getByIdstill takes a hold.unload()on an instance no one holds does nothing; it used to stop it. Code that calledunload()after a lookup withouthold: truemust drop the call, because it now releases a hold someone else took, such as a React hook's. See unload. - Breaking: an Item or ItemProperty no one holds stays cached for the grace period,
SeedConfig.entityCache.unloadGraceMs(default 60,000 ms), after its last release or its last lookup without a hold, and then stops. A hold taken meanwhile keeps the same instance. Its lastunload()used to stop it at once.destroy(),client.stop()and removals by other tabs still stop it at once, and the timers don't keep a Node process running. WithunloadGraceMs: 0the lastunload()stops an instance at once, but one that was never held stays cached until it's evicted or the client stops. See SeedConfig. - Breaking: no lookup or create on
Schema,ModelorModelPropertytakes a hold unless called withhold: true(create, also when it returns a cached instance,getById,createById,find,all,Model.getByName,Model.getByNameAsyncandModel.createBySchemaId). A definition stays cached until it's destroyed or evicted, or the client stops, whether or not anything holds it: its lastunload()no longer stops it. See unload and destroy. - Breaking: writes on a stopped instance throw
EntityUnloadedError, with a message naming the instance: assigning a property,setValue(),save(),publish()andunpublish()on an unloaded Item or ItemProperty, and assigning a field,saveNewVersion(),reload(),model.create(values)andmodelProperty.save()on a destroyed or evicted Schema, Model or ModelProperty. Assignments used to do nothing, andpublish()never settled. Reads still return the last values.EntityUnloadedErroris exported from the main entry, not from@seedprotocol/sdk/node. - Breaking:
client.stop()andclient.unload()return a promise, which resolves once the client has released everything: the outbox stops applying writes (pending ones stay in the database and are applied after the next init), every cached Item, ItemProperty, Schema, Model and ModelProperty is stopped and dropped, this tab hands tab leadership to another tab, and the database closes. An instance kept acrossstop()is stopped; look it up again afterinit. Aninitcalled while a stop is still releasing waits for it. See Stopping the client. - Changed:
@zenfs/core2.7.7 and@zenfs/dom1.2.14 (were 2.5.6 and 1.2.9). An app that installs them beside the SDK needs the same versions. - Changed:
item.publish()holds the item until the publish settles, so an item looked up without a hold doesn't unload mid-publish (through@seedprotocol/publish, below). - Changed: on Node, an open
BaseDb.liveQuerysubscription no longer outlivesclient.stop(): its next poll fails withFailed query: select …and the subscription errors. Unsubscribe before stopping to avoid the error. See Browser and Node. - Added:
hold?: booleanonItem.find,Item.all,ItemProperty.find,ItemProperty.all(defaultfalse), onItemCreateOptions(defaulttrue;Item.create(values, { hold: false })creates without one), and on every Schema, Model and ModelProperty lookup and create (defaultfalse). - Added:
Item.retain(item),Schema.retain,Model.retainandModelProperty.retaintake one more hold on a cached, running instance, released withunload(), and returnfalsewhen it has stopped. - Added:
SeedConfig.entityCache({ unloadGraceMs?: number }), the grace period above. - Added: supported browsers are Chrome and Edge 102, Firefox 114 and Safari 26 or later. In a browser without OPFS,
FileSystemFileHandle.createWritableor Web Locks,client.initrejects withUnsupportedBrowserError(code: 'BROWSER_UNSUPPORTED', the absent features inmissing) before it loads or stores anything.checkBrowserSupport()returns{ supported, missing }to check first; it can't see worker-only features, so Chrome 86 to 101 and Firefox 111 to 113 pass it and fail later in init. Also theBrowserFeatureandBrowserSupporttypes. See UnsupportedBrowserError. - Added:
BaseDb.close()closes the app database;client.stop()calls it. See Other BaseDb methods. - Added:
exportArchive(item, options?)writes a published item as a BagIt bag in memory, and throwsArchiveExportError(code'not_published'or'unpublished_changes'). Both are exported from the main entry, not from@seedprotocol/sdk/node. The SDK depends on the new@seedprotocol/archive. See Export a published item. - Fixed:
client.stop()works beforeinit, while it runs and after it fails; it threwClientManager is not initialized. Please call init() first.Aninitafter a failed one closes the database the failed one left open instead of opening it a second time. - Fixed: browser init no longer fails with
journalOperations.values(...).map is not a functionin browsers without Iterator helpers (Chrome before 122, Firefox before 131, Safari before 18.4), through the ZenFS update. - Fixed: in the browser, creating a file in an OPFS folder that the SQLite worker or another tab made after this tab started no longer fails with
ENOENT … (Out of sync!). Firefox and Safari could hit it. - Fixed: a schema file's single
Relationkeeps itsrefValueType, so{ "type": "Relation", "refValueType": "Image", "model": "Image" }saves adata:orblob:URL as an Image; it failed withRelation target not found. AFileorBlobis still rejected; declare{ "type": "Image" }for uploads. See Define the relations. - Fixed: an item property that's already loaded takes its property's new definition as soon as
updateModelPropertiesor a new schema file version rewrites the property's row, so the next value assigned is checked against the new rules. It kept the definition it loaded with: on Node always, in the browser for a moment. See Editing a property. - Fixed: a property never shows the synced value beside its sync conflict. A read between sync's two writes could show it for a moment.
@seedprotocol/react
- Breaking:
useItemholds the item it shows while mounted and releases it a tick after it unmounts or shows another item, so a StrictMode remount keeps it.useItemsholds each listed item while mounted and releases it when the item leaves the list or the hook unmounts; its list query is dropped once no hook uses it, so a remount fetches again. An item no hook or caller holds unloads after the SDK's grace period. Before, both hooks took holds they never released, so their items never unloaded. Code that keeps an item from them after unmount must hold it (Item.find({ hold: true })orItem.retain, thenunload()). See useItem and useItems. - Breaking:
useSchemas,useModels,useModel(by ID),useModelPropertiesanduseModelPropertyhold what they show while mounted and release it on unmount or change;useSchemaanduseAllSchemaVersionspasshold: true. A list that includes a stopped instance, or whose instance stops while shown, is fetched again.useModelPropertytook a hold on every lookup and never released it. - Breaking:
useCreateItem,useCreateItemProperty,useCreateModelanduseCreateModelPropertycreate without a hold. They held everything they created and never released it. Passhold: trueincreateItem's options to hold the new item, andunload()it when you're done; otherwise a created item that no hook shows unloads after the grace period. See useCreateItem. - Fixed:
useItemlooks the item up again when the instance it shows stops (another tab dropped it, a local purge,destroy()). It kept the stopped instance until the next sync.
@seedprotocol/publish
- Changed:
publishItem, behinditem.publish()andusePublishItem, holds the item until the publish settles.PublishManager.createPublishandensureWalletThenPublishdon't: hold the item yourself andunload()it once the publish settles. See PublishManager.createPublish. - Changed: in the browser,
watchPublishCostandusePublishCostEstimatere-estimate after any write to the item's seed, version or property rows, from this tab or another, through the SDK's change feed. They compared the loaded property values on every item update, which they still do on Node. The newwatchRowsoption replaces the row watch. See watchPublishCost. - Changed: a failed first estimate is retried once after
firstEstimateRetryMs(default 5,000;0turns it off), andisEstimatingstaystrueuntil the retry settles. It leftestimatenulluntil the item changed. - Changed:
usePublishCostEstimatekeeps showing its last estimate, withisStale: true, when it restarts for the sameseedLocalIdandpublishModewith a new Item instance, until the new estimate arrives. It showednullin between.watchPublishCosttakes the estimate to start from aspreviousEstimate. See usePublishCostEstimate. - Fixed: a publish restored from its saved record releases the item it loads to create attestations on the
multiPublishpath too, so the item can unload afterwards.
@seedprotocol/gateway-hyper
- Added: client allowlists.
serveTunnel({ allowlist: { keys, ips } })andseed-gateway serve --allow-keysand--allow-ipslet in only clients whose public key or address (an IP or a CIDR range) is listed; with neither list set, every client gets in, as before. An invalid entry stops the operator at startup. See Restricting who can connect. - Added: client keys.
connectTunnel,createGatewayProxyandcreateTunnelSessiontakekeyFileorkeyPairfor the key the client presents, andconnectTunnel's result andTunnelSessionhaveclientKey.seed-gateway connecttakes--key-file, and--print-key --key-file <path>prints the client's key. Without one, each run uses a new key, as before. - Added: raw TCP mode.
serveRawTunnel,connectRawTunnel,seed-gateway serve --rawandseed-gateway connect --rawpipe each TCP connection byte for byte over its own HyperDHT stream, so HTTP runs end to end with no 64 MiB limit. A raw operator needs its own key, and the SDK's sidecar andcreateGatewayProxyuse the framed tunnel only. See Raw TCP mode. - Added: tracing.
serveTunnel({ onEvent })gets aTunnelEventfor each stream and each step of a request, andseed-gateway serve --traceprints them.requestIdHeader(--request-id-header) sends each request's ID upstream, replacing any value the client sent.upstreamRequestHeadersis exported. See Tracing an operator. - Added:
bootstrapon every library entry point, to join a private or local HyperDHT network. The CLI has no flag for it and always uses the public network. See Private or local DHT networks. - Added:
seed-gateway connect --connect-timeout-ms(default 30,000;0disables). - Changed:
seed-gateway connect --port 0picks a free port, and a value that isn't a whole number from 0 to 65535 stops the command with--port must be 0–65535. Both fell back to 1984.connectalso printsClient key (z32): <key>. See seed-gateway connect.
@seedprotocol/archive (new)
- Added: first release, a BagIt library that the SDK's
exportArchiveuses. These docs cover it only throughexportArchive.
0.7.9
Released 2026-10-22. Upgrade steps: Upgrading from 0.7.7 to 0.7.9.
@seedprotocol/sdk
- Breaking: a local database last opened by an SDK older than 0.7.7 is recreated at init. Its tables are dropped before the migrations run, and a
console.warnsays so. Unpublished items in it are lost, published items come back through EAS sync, and files underfilesDirstay. To keep drafts, publish them first or start the app once on 0.7.8. The data repairs that 0.7.1 to 0.7.6 ran at start-up are gone with it. - Breaking:
statusonSchema,Model,ModelProperty,ItemandItemPropertyis the load status,'loading','ready'or'error', not the actor's state value.Itemgainsstatus, and every entity haswaitForReady({ timeout }). Entity actors start inidle, so waiting foridleno longer means loaded; a property with a pending save is'ready'. A schema no longer reports{ loading: <stage> }or'validating'. The client manager's'idle'is unchanged. - Breaking: a value that breaks its property's rules stays shown, with the reasons in
saveValidationErrors, until the next assignment, and isn't written.save()still rejects withItemPropertySaveValidationError. This reverses 0.7.7's rollback; a fresh load (a restart, another process) reads the stored value. - Breaking: a refused or failed write shows the property's current value again: its newest pending value from any tab, else its stored row. Ownership, a Relation's targets and the EAS schema UID are checked when the write is applied, so an assignment on an item the client doesn't own queues a write that fails with
OwnershipError.save()still checks ownership before it waits. - Breaking:
setValue(value, { before })loses its option:setValue(value).showValueno longer remembers a value to roll back to, so if the property is re-read before the nextsetValue, the stored value shows again. The typesPropertyValueSnapshotandSaveValueToDbEventare removed. See showValue. - Breaking: schema, model and property IDs that an import assigns come from the names: the first 10 hex characters of SHA3-256 of
blog,blog/Postandblog/Post/title, written into the stored version file. They used to be a hash of the whole schema, so adding a property changed every ID. A database that already has the schema keeps the IDs it stored, matched by name; a fresh database gets the derived ones; IDs set in the file are used as they are, with or without$schema. A model or property renamed without a setidis still a new one. - Breaking: schema files read the same way on every path (
config.schema,importJsonSchema,Schema.create).importJsonSchema(input, version)usesversiononly when the JSON has none. Minimal<name>-v<n>.jsonfiles in the working directory are no longer imported at init; pass them asconfig.schemaor import them.loadAllSchemasFromDb()only reads, andLoadedSchema.dbMatchesFileis removed. Renaming a published schema writes the next version file. A missing name fails withCould not load config.schema: Schema name is required (either at top level or in metadata.name), without theFailed to parse JSON import content:prefix. - Breaking: a property's stored rows are matched to it by its ID, not its name. A property renamed in code keeps its values after a restart. New rows of an Image, File, Html or Relation property are stored under
<name>Id(a List of Relation under<singular name><Model>Ids), andgetPublishPendingDiffreports each property under that storage name (authorId).pickLatestMetadataRowPerPropertygroups rows bypropertyId; code that filtersmetadatarows should match onpropertyId. - Breaking: publishing a property with no stored definition throws. It used to attest the value as
string <name>. - Breaking:
getPublishUploadData(item, options)andgetPublishUploads(item, options)take the options second, accept any{ seedLocalId }, and lose theuploadsandrelatedItemPropertyparameters.applyPropertyAttestationUidsFromPublishrecords a UID only on the row itsmetadataLocalIdnames.item.getPublishUploads(options?)is unchanged. - Breaking:
watchEntityRowsloses itssinceoption, andwindow.__SEED_INVALIDATE_ITEM_PROPERTIES__is no longer set or read. - Breaking: the SDK registers the EAS and Arweave clients from
@seedprotocol/easand@seedprotocol/arweaveinstead of its own copies. On Node that'sNodeEasClient, with its own connection pool (see eas below);EasClient.configure(new NodeEasClient({ dispatcher: false }))afterclient.initgoes back to fetch's defaults.EasClientfrom the main entry isBaseEasClient. Node'sgetTransactionDatatries the preferred read gateways first, as the browser did. - Breaking:
eventEmitter.emit('syncDbWithEas')does nothing: the SDK no longer listens for it. Callclient.syncFromEas().requestEasSyncFromEventBus, theEasSyncRequestSourcetype and the'event_bus'sync source are removed. - Breaking:
@seedprotocol/sdk/nodeloses the Markdown import (parseMarkdownFrontmatter,processSeedConfig,saveModelsFromMarkdown),withSeed(use@seedprotocol/next),getRefValueType, and the CLI helpersPathResolver,BasePathResolver,commandExists,ensureNodeDbSchema,runSeedInit,findSeedBinary,INIT_SCRIPT_SUCCESS_MESSAGEandSCHEMA_TS. - Breaking: removed from the main entry: the schema file API (
createSchema,readSchema,deleteModelFromSchema,deletePropertyFromModel;listSchemasandgetSchemaNameFromIdstay),waitForEntityIdle(usewaitForReady()),getSegmentedItemProperties,getRelatedItemsForPublish,getPublishDraftGraphand its types (usegetPublishGraph),shouldAttestProperty,getEasSchemaForItemProperty,getEasSchemaUidBySchemaName(usegetEasSchemaUidForExactDefinition),restoreDataItemIdsOverwrittenByL1,applyArweaveL1TransactionIdLocal, the staticsModel.updateNameIndex,Model.trackPendingWrite,Model.getPendingModelIdsandModelProperty.adoptForModel,ClientManagerState.ADD_MODELS_TO_STORE, and the typesGetItem,GetItemParams,GetPropertyDataOptions,SeedInitBrowserProps,SeedInitBrowser,DbQueryResult,ResultObject,SqliteWasmResult,SqliteWasmCallback,ReturnObj,PublishRequestData,PropertyToUpdateWithSeed,GlobalMachineContext,PublishMachineContext,GetSchemaForModelEvent,HydrateExistingItemEvent,WebpackConfigContext,EnvironmentandClientCallback. - Changed:
save()has no time limit; the 10-secondTimeout of 10000 ms exceededis gone. A failed Image, File or Html write rejects withOutboxOpFailedError(code: 'OUTBOX_OP_FAILED', the original class inerrorName, such as'TypeError'for a failed fetch or'Error'forNo file data found), because the fetch or file read now runs when the write is applied, and the property shows its current value again. See save. - Changed: a File, Image or Html value is written when its write is applied: a string (a URL, a file name, Html text) travels in the outbox, and a Blob's or
data:URL's bytes go to a staged file. A write whose staged bytes never landed fails withStagedValueMissingError.OutboxOpRetryableErroris removed. - Changed: what an Image string becomes: any
data:image URL is decoded (image/svg+xmland parameters included); anyhttp(s):orblob:URL is fetched,localhostand IP addresses included; the name of a file already in the images folder links it;host.tld/pathis fetched over https; raw base64 with an image signature is decoded; anything else fails withNo file data found. A bare name such asphoto.pngis no longer fetched relative to the page. A new file without a source name takes its MIME type's extension (.svg). - Changed: an empty value on an Image, File or Html property (
''ornull) clears it.''used to save a storage seed pointing at the folder, andnullfailed while the old file stayed. - Changed: Html values no longer store a session URL, and reads ignore one an older SDK stored; an Html property's value is still the file's text.
- Changed: a property's value is its newest row (or the newest pending write over it), revoked or not. A property retracted on the current Version reads as empty instead of showing an earlier Version's value, and a cleared Image no longer falls back to the image it replaced.
getPublishPendingDiffreports a pending value ascurrentValue. - Changed: a property set to
''ornullafter it was attested publishes the removal: the next publish attests the EAS type's empty value instead of skipping it, which left the old value on chain. A published Boolean or Date with an empty draft fails publish validation withpublish_clear_unsupported. See Remove a published value. - Changed: EAS sync stores an empty-value attestation, or a revoked property attestation on a live Seed, as a row with no value. A revoked Seed keeps its last values.
- Changed: in the browser, loaded items learn about sync's writes from the change feed only, and a loaded item also watches its Seed's
metadatarows, so a property sync adds shows on it. Sync no longer re-reads every cached item afterwards; Node still does. A loaded property's change-feed read no longer skips rows while one of its writes is pending; it shows the pending value. - Changed: a schema is applied at init only when its ID, version or content changed since it was last applied (the first start after upgrading applies each one again). Its rows are written in one transaction.
- Changed: a property's whole definition is stored on its row, so validation rules set in code (
Model.create,ModelProperty.create) apply after a restart.getPropertySchemareturns the stored definition. - Changed: init's steps run back to back with no fixed waits.
Model.allfor a schema with no models returns at once, and reading the stored addresses before the database opens waits up to 30 seconds, then returns none. The order isprocessSchemaFiles, thenaddModelsToDb. - Changed: the SDK no longer handles the events
fs.downloadAll.request,fs.downloadAllBinary.request,service.saveandservice.saveState.request, and nothing emitsfs.downloadAll.success. The download after each sync still runs. - Changed: six migrations run at init (0024 to 0029). The last adds unique indexes, so a second seed or version row under an attested UID fails with a UNIQUE constraint error.
- Changed: the SDK no longer depends on
js-yamlor@sqlite.org/sqlite-wasm(SQLite WASM still installs throughsqlocal). - Added:
property.clear()andproperty.clear({ retract: true }). Both show the property empty at once and resolve when the cleared row is stored. The next publish attests the type's empty value, or withretractrevokes the property's attestations. Boolean and Date have no empty value, so theirclear()rejects withClearNotSupportedError(exported from both entries) unless it retracts. See clear. - Added:
getPublishPendingDiffentries havechange('set','removed'or'retracted'), and an empty value with nothing on chain to remove isn't listed.summarizePublishWorkcounts removals inattestationCountand addsrevocationCount. New exportsgetPendingRetractions(typePendingRetraction) andmarkPropertyAttestationsRetracted. - Added:
isClientReady(),isClientReadySnapshot(snapshot)andwhenDbReady(timeoutMs?). See isClientReady and whenDbReady. - Added:
Schema.find({ schemaName })returns the schema with that name, orundefined, and never creates one. - Added:
getPublishGraphand its types (PublishGraph,PublishGraphNode,PublishGraphProperty,PublishGraphRef,PublishPropertyKind,GetPublishGraphOptions). The publish reads (getPublishPayload,getPublishUploadData,summarizePublishWork,getUnpublishedRelatedItems) are built from the stored rows, load no item, and wait for every pending write first. A property attests its newest row even when a loaded instance shows an older one, a Relation to a saved Image, File or Html uploads once, and a published storage seed's file isn't uploaded again. - Fixed: an edit at the same
versionno longer duplicates models. The SDK replaces the stored schema file and logs a warning asking you to raiseversion. - Fixed: a write to a new model or property made before its first write started is no longer dropped (
Model.createwaited out its timeout). A ModelProperty save withoutrequiredkeeps the stored value,Model.lastSaveErrorreports a failed rename, a second rename during the first is saved after it, andModel.find({ modelName, schemaName })reads the database when the model isn't cached. - Fixed: a renamed schema is cached under its new name only, so
Schema.create(oldName)no longer returns it; a schema created again after an unload is found by its file ID;Schema.clearCache()stops every cached schema;Model.destroy()stops its properties;ModelProperty.destroy()deletes its row by file ID. - Fixed: in the browser, a conflict that sync records over a loaded property's draft shows on
property.conflictonce the change feed reports the write. It used to appear only after a reload. No React hook re-renders for it. - Fixed: sync no longer makes a second seed for a republished item (migration 0024 merges existing ones) or a second row for a UID publish recorded meanwhile. An item synced without its model gets the model by EAS name at the model's
nameMatchlevel, andItem.createthrowsAmbiguousModelErrorwhen several schemas match. - Fixed: a loaded item takes its publish times from its rows; the local query source labels seeds with their EAS schema name and bounds months in UTC; revoking skips placeholder UIDs.
- Fixed: the download after each sync skips files already on disk and no longer runs a 480 px resize pass. The resize worker recognizes WebP without a MIME type, untyped HEIF as
image/heicand ICO asimage/x-icon. - Fixed: in the browser, opening the database while another tab holds it retries after 1, 2 and 4 seconds before failing with
DatabaseNotPersistentError; a second tab could fail init while the first was still migrating. - Fixed: browser init works in Chromium builds that refuse workers started by URL from inside a worker: SQLocal's worker and SQLite's OPFS proxy start from blobs. On a cross-origin isolated page,
DatabaseNotPersistentErrorsays SQLite's OPFS VFS failed to install and names SQLite's console lines, instead of asking for the COOP and COEP headers.
@seedprotocol/react
- Changed:
useDebouncedItemPropertykeeps a value that breaks a rule in the input, witherroranItemPropertySaveValidationError. A refused write (OwnershipError) or a failed one shows the stored value again, witherrorset. 0.7.8 rolled both back to what showed before the edit. - Changed: a property save refetches
useItemPropertiesonce, when the change feed reports the write.SeedProviderno longer refetches onitemProperty.saved, andinvalidateItemPropertiesForIteminvalidates once and resolves when the refetch is done. - Changed:
useSchema,useItem,useItemProperty,useCreateItemPropertyand the create hooks follow the entity's loadstatusinstead of its actor reachingidle.useCreateItemProperty'serrorfor a failed load isError('Failed to create item property'). - Changed:
useServices().percentCompletehas no 80% step: 10, 30, 50, 60, 70, 90, 100. - Changed:
isClientReadySnapshotis the SDK's; import it from@seedprotocol/sdk. The React package's entry doesn't export it. - Fixed:
useItemsreturns a new, re-sorted array when a listed item is published or republished. It kept its order and identity. - Fixed:
SeedImagestops showing an image once its property is cleared or points at another file.
@seedprotocol/publish
- Breaking: publish uploads only signed ANS-104 data items, through
POST /api/upload/arweave/batchon the upload API or to Turbo withpayment: { storage: 'user' }. The L1 path is removed: no/startrequest, reimbursement or chunk upload. Apps that leftuseArweaveBundlerunset took the L1 path and now need a data item signer (ConnectButtonregisters one, or setsignDataItems). - Breaking: the
signArweaveTransactionsandarweaveJwkoptions,ResolvedPublishConfig.useArweaveBundler, the typesSerializedPublishUploadandArweaveTransactionInfoResult, and the L1 context fields are removed. - Breaking: the L1 states (
creatingArweaveTransactions,sendingReimbursementRequest,pollingForConfirmation,uploadingDataand theirPhase2versions) are removed. A publish saved in one of them is markedinterruptedwhen it's restored; publish the item again. - Breaking: cost estimates are always bundler-priced.
storage.pathand an estimate'sarweave.pathare'bundler', andcombinePublishCostEstimateignoresarweavePath. Code comparingpathwith'l1'no longer type-checks. - Breaking: the L1-finalize worker runs for every publish, and no longer restores data item IDs that publish 0.7.4 and earlier overwrote with L1 IDs (the SDK recreates those databases).
- Deprecated:
useArweaveBundler.truechanges nothing;falsemakesinitPublishthrowPublishPaymentConfigError. - Changed:
item.unpublish()revokes the Seed attestation first, then the property attestations (one transaction per schema), then the Versions. The Seed used to go last. A sync between the transactions sees the Seed revoked and keeps the item's last values; a failure partway leaves the Seed revoked and some properties live. - Changed:
ensureEasSchemasForItemregisters the schemas of every Seed the publish attests (the item and the drafts published with it), each once, and looks a property schema up by its exact definition before registering it. Itsitemparameter takes{ seedLocalId, modelName }. - Added: a publish revokes retracted properties after attesting, one
multiRevokeper schema; an EOA publisher signs a second transaction. A failure waits inretractionFailureRecoverable, wherePublishManager.retryAttestationssends the revocations again, anditem.publish()rejects with the attestation-failure message.checkingrefuses the publish when the wallet can't revoke (an automation session key, or a Seed the executor attested). See What a publish does. - Fixed: published Html links its images to the public gateway (
https://<arweaveDomain>/raw/<tx>), not the read host. Under Gateway Hyper or a proxy, 0.7.8 and earlier wrote loopback or app-only URLs into the uploaded Html. - Fixed: each attestation UID is recorded on the row whose value was attested, so a value another tab changed after the payload was built stays a draft. A restored publish no longer loads the item first.
@seedprotocol/eas
- Breaking:
getEasSchemaUidBySchemaNameandfetchEasSchemaUidBySchemaNameare removed. Look a property schema up by its definition withgetEasSchemaUidForExactDefinition, and a model's with the model-schema lookup. - Changed:
NodeEasClientsends requests through its own undiciAgent: TLS key exchange limited to X25519 and P-256 (EAS_NODE_ECDH_CURVE), idle connections kept for 30 seconds (EAS_NODE_KEEP_ALIVE_TIMEOUT_MS). Per the package's changelog, Node 24.21 and later offer a post-quantum key exchange that easscan doesn't support, and each new TLS handshake took about 1.45 seconds. No Agent is made under Bun or Deno, or when the host set its own global dispatcher.new NodeEasClient({ dispatcher: false })uses fetch's defaults, andregisterNodeEasPlatform({ easClient })passes options. The 30-second request limit stays. eas now depends onundici, loaded only by@seedprotocol/eas/node. - Changed:
getEasSchemaUidForSchemaDefinitionlooks up the exact definition after its cache; it used to take the first schema whose definition ended with the text. Registering a schema forgets only that definition's cached miss. - Added:
createEasGraphQLClient(url, timeoutMs?, { dispatcher? })andcreateEasNodeDispatcher(from/node). - Added: the model-schema lookup:
fetchEasModelSchemas,matchEasModelSchemas,getEasModelSchemasForNamesandmodelSchemaDefinition. - Added: a per-request timing log under
DEBUG=seedEas:timing. - Added:
VERSION_SCHEMA_UIDandVERSION_SCHEMA_DEF(moved from the SDK, which re-exports them) andisEasEmptyValue(easType, value). - Fixed:
getItemVersionsFromEasreturns only Version attestations. A newer tool attestation from the Seed's own attester was taken as the latest Version, and@seedprotocol/queryreturned the record with no properties.
@seedprotocol/query
- Breaking:
expandRelationsis replaced byrelations.truewas'expand'andfalsewas'urls'. An untypedexpandRelations: falseis ignored, so relations come back expanded. - Breaking: the deprecated
Feedaliases (FEED_RICH_BODY_STORAGE_SCHEMAS,enrichImageSeedCloneForFeed,feedRichBodyModelPriority,getFeedFieldStorageModels,getFeedListElementStorageModels,hydrateArweaveRichTextInFeedItems,isFeedRichBodyStorageSchema,parseEasPropertyMetadataForFeed,setFeedFieldStorageModel,setFeedListElementStorageModelsand their types) are removed; the names withoutFeedstay.QueryCacheConfig.backgroundRefreshandrefreshIntervalare removed, andCACHE_BACKGROUND_REFRESHandCACHE_REFRESH_INTERVALaren't read. - Changed: a warm
queryBySchemais one round of requests: the change check goes out with the Seed list and covers every cached Seed. - Changed: a property whose attestation is the empty value is left out of the record, and a zero
bytes32isn't read as a related Seed. Changelog entries show a removal as a change toundefined. - Added:
relations: 'expand' | 'urls' | 'uids'(default'expand') onqueryBySchema,getSeed,getSeedsandqueryBySchemaForMonth.'uids'leaves each relation a Seed UID and reads no related Seeds. TypeRelationsMode. See queryBySchema options. - Added:
queryBySchema(name, { maxAge })andCACHE_MAX_AGE(default 0, off): a collection checked less thanmaxAgeseconds ago in this process is returned without a request, so changes show up tomaxAgeseconds late. - Fixed: on Node,
initializeQueryPlatformregistersNodeEasClientwhen no EAS client is registered. - Fixed:
queryBySchemaForMonthbounds the month in UTC. It used the process's time zone. - Fixed: records no longer come back empty when a tool attestation from the Seed's attester references the Seed (through eas, above).
@seedprotocol/arweave
- Added:
DefaultArweaveClient, a fetch-based client for Node and browsers, whichNodeArweaveClientextends.getTransactionDatafirst tries/raw/<id>on the preferred read gateways, unlessdecode: false, then the configured host's raw URL, then arweave-js's chunk API, which loads only when it's needed. - Added:
getPublicArweaveBaseUrl()andgetPublicArweaveRawUrl(txId), the public gateway for links written into published content, from the new requiredpublicArweaveBaseUrlfield of the resolved gateway endpoints. Code that builds that type itself has to set it.
@seedprotocol/feed
- Breaking:
FEED_SITE_URLis removed (it had no effect):loadFeedConfig()has nositeUrl, andTransformOptionsrequiresitemUrlBaseanditemUrlPath. The site config'ssiteUrlis unchanged. - Breaking:
FEED_EXPAND_RELATIONSis removed; feeds always expand relations.falseleft Image and author relations as UIDs, so items lost image enclosures and author names. - Breaking:
initializeFeedPlatformkeeps EAS and Arweave clients a host app or the SDK registered, and registers the Node ones otherwise; it used to replace them.initializeSeedClient,getClientandteardownSeedClientare removed;teardownFeedPlatformis added. - Breaking:
GraphQLItemis nowFeedItem, with the same shape.enrichImageSeedCloneForFeed,FEED_RICH_BODY_STORAGE_SCHEMASandisFeedRichBodyStorageSchemaare removed; use query'senrichImageSeedClone,RICH_BODY_STORAGE_SCHEMASandisRichBodyStorageSchema. - Changed: page 1 asks query on every request and is rebuilt only when query's collection changed, so new posts appear on the next request instead of up to
CACHE_TTLlater. ItsCache-Controlispublic, max-age=<CACHE_MAX_AGE>, s-maxage=<CACHE_MAX_AGE>, must-revalidate(default 0, was 3600); later pages stay at 3600 and archives at 86400, all withmust-revalidate. SetCACHE_MAX_AGEto let a CDN keep page 1. - Changed: ETags stay the same across rebuilds of an unchanged feed, and responses carry
ETagandLast-Modifiedwith the cache off too, though only a cached response answers304. - Changed: per-request logs print only under
DEBUG=seedFeed, andCache is DISABLEDprints once. The package depends ondebugand no longer onexpressorgraphql-request. - Added:
fetchFeedItemsBySchemaName(schemaName, { limit?, skip? })returns{ items, etag? }. - Fixed: a cached body is served only to requests with the site config overrides it was built with; another site used to get the first site's body.
- Fixed: a failed build falls back to an unexpired cached body with
X-Cache: STALEandWarning: 110 - "Response is stale", archives included. Without one, both handlers answer500withFailed to generate feed. - Fixed: a body read from the disk cache keeps its ETag and expiry.
- Fixed: image metadata probes the image the enclosure uses, not the item's own storage transaction; a probe no gateway answered isn't cached; archives get image metadata too.
- Fixed: archive months and the links between them are in UTC.
- Fixed: a page whose last item fills it exactly no longer links an empty next page, and a
FEED_PAGE_SIZEthat isn't a positive integer gives 25.
0.7.8
Released 2026-10-20. Upgrade steps: Upgrading from 0.7.7 to 0.7.9.
@seedprotocol/sdk
- Changed: item edits go through an outbox. An assignment queues a write, and the leader tab applies it in one transaction;
await property.save()resolves when it has. Only the newest of a property's pending values is written, and writes left pending by a reload or a closed tab are applied when a tab next leads.Item.create,item.destroy()andproperty.destroy()go through it too, and publish waits for every pending write first. Raw SQL or a live query onmetadatasees a value only once its write is applied, soawait save()before readingmetadatayourself. A pending value shows after a reload, before it's applied. The local query data source (source: 'local'and'auto'reads in@seedprotocol/query) reads published rows only. A save that a newer save in the same tab replaced queued nothing (changed in 0.7.9: each assignment queues a write before the setter returns). - Changed: a write is checked again when it's applied. A write on an item the client no longer owns fails with
OwnershipError, and a Relation value naming a local item that doesn't exist fails withRelation target not found: <ids>(RelationTargetError, not exported). The assignment also checked ownership (changed in 0.7.9: only the applied write is checked). - Changed: a write that fails when applied makes
save()reject withOutboxOpFailedError(code: 'OUTBOX_OP_FAILED', the original class inerrorName, not exported), except an ownership refusal, which rejects withOwnershipError. A write held up by a busy database is retried first. A rule-breaking value was still rolled back (changed in 0.7.9: it stays with its errors, and failed Image, File and Html writes reject this way too). - Changed: EAS sync keeps an unpublished local edit over a newer synced value. When the synced value differs from both the edit and the value the edit replaced, the SDK records a conflict. A synced value attested after a local edit used to replace it.
- Changed: an item holds its properties. Its last
unload()ordestroy()releases them, so a property nobody else holds stops with it. EachItemProperty.find()orItemProperty.create()is one hold, released by oneproperty.unload(). Code that calledproperty.unload()in a loop should callitem.unload()once andproperty.unload()once perfind. A property read throughitem.propertiesafter its item is unloaded is stopped. - Changed: the SDK revokes the
blob:URLs it makes for Image and File values when the value moves to another file and when the property stops. A URL fromproperty.valueworks while it's the current value and the property is loaded. URLs the app passes in are left alone. - Changed: in the browser, loaded items and properties watch the change feed instead of holding live queries, and re-read only when their own Seed's rows are written. Other tabs' writes still reach them. The SDK's measurement: 2 statements per create instead of 400, and 3 per save instead of 320, with 40 items of 8 properties loaded. Node has no change feed.
- Changed:
addresses.persistedfires only when the owned or watched addresses differ, ignoring case and order, from the ones this tab last announced. EverysetAddresses()and every init that saved addresses used to fire it, anduseItemsreloaded each time. - Changed: in the browser, identical SQL-tag queries passed to
BaseDb.liveQueryshare one SQLocal subscription, andBaseDb.liveQuerylogs SQLite errors throughdebug(seedSdk:browser:db:Db) instead ofconsole.error. Subscribers still get the error. - Changed: saves read EAS schema UIDs from the cache item creation uses (found UIDs kept, misses for 5 minutes) instead of asking the indexer each time, and
createNewItemlooks up a model's property schemas in parallel. Indexer requests abort after 30 seconds (see eas below). - Changed:
seeds,versionsandmetadatarows always getcreated_aton insert andupdated_aton every write. - Changed:
sqlocal0.18 (SQLite WASM 3.51). Four migrations run at init: the outbox, read indexes and the change feed's table and triggers. - Added:
property.conflict,property.resolveConflict('local' | 'remote'),item.conflicts(), theitemProperty.conflictevent and thePropertyConflicttype. See Conflicts. - Added:
property.showValue(value)shows a value without saving it, and the nextsetValueor assignment rolled back to what showed before it when refused (changed in 0.7.9: there's no rollback). - Added:
BaseDb.transaction(fn): the Drizzle queriesfnruns on itstxcommit together or not at all. A busy transaction is retried as a whole. On Node the SDK keeps one connection and uses WAL for a file database. - Added:
watchEntityChanges,watchEntityRows,isChangeFeedRunningand the typesChangeWatchandEntityChangeTable, browser only.watchEntityRowstook asinceoption (removed in 0.7.9). - Added:
Item.alltakes asignal; once it aborts,Item.allstops building items and rejects with the signal's reason. - Added:
client.subscribeService(listener)is called with the client manager actor whenclient.init()afterclient.stop()replaces it and whenstop()ends it. - Added:
DEBUG=seedSdk:timinglogs per-phase times for creates, finds, loads and saves, and when a ready wait timed out. - Added: the leader tab deletes applied outbox entries after a day and failed ones after a week.
- Fixed:
Item.allhonorswaitForReady: by default it returns items without waiting for them to load, and withtruewaits for all of them together. It waited for each item in turn either way. - Fixed: an item's live queries close when it stops, on
destroy()and onItem.dropCachedInstancesForSeedIds, and a schema's last unload releases the models it loaded. - Fixed: an Image or File property with no display URL no longer makes a new
blob:URL about 60 times a second, and a metadata write no longer shows the storage seed ID as the value for a moment. - Fixed: on Node, a write retried after
SQLITE_BUSYis no longer lost: the SDK replaces the connection before retrying. - Fixed:
client.claimUnpublishedDraftsruns in one transaction and also claims drafts whose create is still queued; its result addspendingCreates. - Fixed: assigning
item.propertiesthrowsCannot set item.properties directly…, a seed UID assigned to a new draft is stored, and an item keepsrevokedAt,versionsCountandcreatedAtsent before it finished loading.
@seedprotocol/react
- Breaking:
useCreateModelProperty'screatereturnsPromise<ModelProperty>, and a missingmodelName,nameordataTyperejects instead of throwing.schemaIdis looked up as a stored name or file ID first; a-<n>suffix is dropped only when nothing matches. - Changed:
useModels,useModel,useSchemas,useModelProperties,useFilesanduseImageFileswork withoutSeedProvider, on a shared fallback query client with Seed's defaults. In development builds, any hook that uses the fallback logs one warning that these hooks will throw withoutSeedProviderfrom 0.8.useItemsanduseItemPropertiesstill throw without it. - Changed:
useItemPropertiesre-reads when the change feed reports a write to its item's rows, instead of on every metadata write in every tab (changed in 0.7.9: once per save). On Node it polls. - Changed:
useItemPropertyanduseItemPropertieshold the properties they return while mounted and release them on unmount. - Changed:
useCreateItem'screateItem(modelName, itemData, options)takesItemCreateOptions. A call made while another is running returns that call's promise and item instead ofundefined. - Changed:
react-dom18 or 19 is a peer dependency. - Added:
useClientService()anduseClientSelector(selector). See useClientService and useClientSelector. - Fixed:
useIsClientReady,useDbsAreReady,useIsDbReady,useServicesandSeedClientGatefollow the client afterclient.stop()and a newclient.init(), so the gate shows its loading state again without akeyremount. - Fixed: a text input bound to
property.valuefromuseItemPropertykeeps every keystroke. - Fixed:
useDebouncedItemPropertyworks on a property without a loaded definition. A refused save or a broken rule rolled the input back to what showed before the edit, anderrorreported the failure (changed in 0.7.9: a rule-breaking value stays). - Fixed:
useLiveQueryanduseLiveQueryStateresubscribe after an error, after 0.5, 1, 2, 5 and 10 seconds and then every 30, keeping the last rows; an error while setting the query up lands inerror. - Fixed:
useItemslists an item created during its first load, and a superseded load stops building items. - Fixed:
SeedClientGatecomparesinitConfigandschemaby value, so an inline object doesn't start init on every render. - Fixed:
useService(...).percentCompleteis 100 once the service is idle and 0 to 100 while the client starts, anduseServicere-renders on state changes (changed in 0.7.9: no 80% step). - Fixed:
useCreateSchema,useCreateModelanduseCreateModelPropertyclearisLoadingwhen the SDK returns an already-loaded instance and when the create throws; a throw also setserror. TheisLoadingcheck followed the actor'sidlestate (changed in 0.7.9: the load status). - Fixed:
useSchemareleases its hold on unmount and whenschemaIdentifierchanges.useModelPropertyacceptsnullandundefinedarguments and waits instead of looking up the wrong form. - Fixed:
useEasSyncProcesses,useEasSyncProcessesStateanduseEasSyncProcessByIdreturnundefineduntil the client is ready instead of throwingDb not configured. - Fixed:
useResolvedMediaRefreportsstatus: 'loading'and nohrefaftervaluechanges, until the new value resolves.SeedImageforwardsrefto its<img>on React 18.
@seedprotocol/eas
- Changed: EAS indexer requests abort after 30 seconds (
EAS_REQUEST_TIMEOUT_MS) through the default clients; they used to wait indefinitely. So with a stalled indexer,@seedprotocol/queryreads reject (see TimeoutError), the feed handlers answer500(changed in 0.7.9: an unexpired cached body can answer instead), and publish's attestation check gives up after about 3.4 minutes. - Added:
createEasGraphQLClient(url, timeoutMs?), aGraphQLClientwith that limit, for apps that implementIEasClientthemselves. - Fixed: registering a schema forgets only the cached lookup misses it answers, not all of them (changed in 0.7.9: only that definition's miss).
0.7.7
Released 2026-10-18. Upgrade steps: Upgrading from 0.7.5 to 0.7.7.
@seedprotocol/sdk
- Breaking: after
client.stop()orclient.unload(), client methods such asgetAddresses,setAddressesandsyncFromEasthrowClientManager is not initialized. Please call init() first.untilclient.init()runs again. They used to keep working on the open database. A secondstop()orunload()logs aconsole.warnand returns instead of stopping again. - Changed:
ItemProperty.save()anditem.publish()reject with the error that was thrown, soinstanceof,codeandcausework (anOwnershipError, or@seedprotocol/publish'sManagedAccountPublishError). They used to reject with a new plainErrorcarrying the message.save()'s message loses itsFailed to save property <name>:prefix (No file data found, notFailed to save property cover: No file data found), so code that matches the old text has to change. - Changed: a value that breaks its property's rules rolls back.
property.value,Item.findandItem.allreturn the value from before the assignment, as they already did after an ownership refusal. The rejected value used to stay in memory for the rest of the session. - Changed: after a publish, Relation and List values stored on this device hold the attested Seed UIDs (a UID, or a JSON array of UIDs), as an EAS sync of the same attestation stores them. They used to keep the related drafts' local IDs. A List of Relation also records its attestation UID, so a patch publish no longer attests it again every time.
- Added:
property.setValue(value, { before })shows and saves a value like assigningproperty.value, but saves even when the value is already in context.beforeis what a refused or rejected save restores. - Added:
ItemCreateError, thrown byItem.createwhen a File, Image, Html or ItemStorage value fails to save for a reason other than its rules (an ownership refusal, for example) or its property doesn't load. It hascode('save_failed'or'property_not_loaded'),modelName,property, the removed draft'sseedLocalId, and the save's error ascause. It used to be a plainErrorwith the same message. Exported from the main entry, not from@seedprotocol/sdk/node. - Fixed: a File value works after a reload. Each load makes a fresh
blob:(browser) orfile://(Node) URL from the file instead of reading a stored one, and a one-time step after init clears the URLs that 0.7.5 and 0.7.6 stored. - Fixed: a non-list
Relationwhose schema sets arefValueTypeother thanImagesaves its value. The save resolved without writing. - Fixed:
item.destroy()marks only its own Seed row. It matched by local ID or UID, so other local rows with the same UID were marked deleted too. - Fixed:
Item.all(modelName, true)lists only deleted items. It also listed items that weren't deleted. - Fixed: an item claimed by
client.claimUnpublishedDraftsstops when its owner callsunload(). The claim no longer keeps a hold on it. - Fixed: a complete-format schema file (
$schemaset) with nometadata.namefails init with aSchema name is requirederror instead of a raw SQL error. - Fixed:
nameMatchlevels are loaded beforeclient.initresolves. For a few seconds after init, lookups by EAS type treated every model as'fuzzy'. - Fixed: on Node,
BaseDb.isAppDbReady()isfalseuntil the database opens. It returnedtruebefore init.
@seedprotocol/publish
- Fixed: a direct-to-EAS publish refuses a batch in which a property update targets a request attested earlier in the batch. It throws
ManagedAccountPublishErrorwith codePUBLISH_PREFLIGHT_FAILED(the message namesPublishTargetAlreadyAttested(i, t)) before anything is sent. The update used to be dropped without an error. The contract path already refused it, and batches the SDK builds don't contain such updates. - Fixed: both publish paths rewrite local Relation and List values to the Seed UIDs they attested (see the SDK above).
@seedprotocol/react
- Fixed:
useItems({ deleted: true })matches only deleted items. - Fixed:
usePublishItem'serroris the publish's own error class (see the SDK above).
@seedprotocol/next (new)
- Added:
withSeed(nextConfig, options?), the Next.js config helper. It points the SDK and@seedprotocol/queryat their browser builds in every graph, server rendering included, and adds cross-origin isolation headers throughheaders().fsModules(default[]) aliasesfsmodules to ZenFS in browser builds. It supports Next.js 16, and Next.js 15.5 with webpack; under Next.js 15 with Turbopack it logs a warning, because SQLite WASM can't load there. See @seedprotocol/next.
0.7.6
Released 2026-10-18. Upgrade steps: Upgrading from 0.7.5 to 0.7.7.
@seedprotocol/sdk
- Breaking: browser
client.initfails withDatabaseNotPersistentError(code'DB_NOT_PERSISTENT') when SQLite can't keep its database in OPFS: whenSharedArrayBufferorAtomicsis missing because the page isn't cross-origin isolated, or when SQLite reports another storage type. The message names the COOP and COEP headers. Before, the page ran on an in-memory database and lost everything on reload.@seedprotocol/vitesets the headers on the dev and preview servers; production hosting has to send them too.onInitErroronSeedClientGateandSeedProviderreceives the error. - Breaking:
@seedprotocol/viteis an optional peer of the SDK instead of a dependency, so installing the SDK no longer installs Vite, Rolldown and the polyfills. Importing@seedprotocol/sdk/viteneeds@seedprotocol/viteinstalled; without it the import fails withERR_MODULE_NOT_FOUND. - Changed: the SDK has no required peers.
xstate,drizzle-orm,@zenfs/core2.5.6 and@zenfs/dom1.2.9 are dependencies. The SDK no longer depends on@tanstack/react-query(its query cache uses@tanstack/query-core); React apps get React Query as a peer of@seedprotocol/react.fseventsis gone. - Changed: browser bundles no longer contain the Node platform. It loads through a
#seed-node-platformimport that maps to the browser platform under thebrowsercondition, and the browser entry no longer has a bareimport 'fs', which webpack and Turbopack failed to resolve. - Changed: models a schema drops are unlinked from it when the schema loads, with or without a new
version. From the next start,Model.all(schemaName)no longer lists them; their rows and items stay. Schemas with draft edits are skipped. - Deprecated:
withSeedfrom@seedprotocol/sdk/node. UsewithSeedfrom@seedprotocol/next. - Added:
@seedprotocol/sdk/drizzlere-exportsdrizzle-orm(eq,and,like,sqland the rest) from the SDK's own copy, so apps that query the SDK's tables don't installdrizzle-ormor keep it on 0.44. - Added:
client.init({ config: { schema } })is enough.filesDirdefaults toendpoints.files, then'.seed', and bothendpointsfields default tofilesDir, in the browser too. TheConfig must include endpoints with filePaths and fileserror is gone. On Node the default puts everything in<cwd>/.seed. - Added:
SeedConfig.schematakes aSchemaFileInput(new type export): onlymetadata.nameandmodelsare required, andversiondefaults to 1. A schema object without$schemaused to fail withSchema name is required. - Fixed: a changed schema whose complete-format file sets an
idloads at init even when itsversionis unchanged. The stored copy is replaced and a warning says to raiseversion; it used to load the stored copy and drop the changes without an error.importJsonSchemathrowsSchema … already exists with different contentin that case. Without anid, an edit at the same version still left duplicate models (fixed in 0.7.9). - Fixed: properties publish under the EAS schema for their exact definition (
<type> <name>), not any schema whose name matches. On OP Sepolia a legacybytes32 storage_transaction_idschema exists, and File, Image and Html IDs could be attested under it. Abytes32-shaped value is no longer read as an Arweave ID. - Fixed:
item.publish()works on the direct-EAS (EOA) path. It failed every time withCannot set property seedUid of #<Item> which has only a getter.publishItem(item)wasn't affected. - Fixed: a property edited after its Seed is published attests on the next patch publish. The edit went into a new draft row that publish skipped, and each further edit added another row. Publish also reconciles properties that still point at a published row, such as after another tab's edit.
- Fixed: loading a schema keeps a model's or property's
nameMatch,validationanddescriptionfrom the file, fresh browser inits no longer logSQLITE_CONSTRAINT_UNIQUEfor model and property rows, and a model no longer takes a same-named model's row from another schema. - Fixed: in a browser without Node polyfills, init errors report themselves instead of
process is not defined.
@seedprotocol/vite
- Breaking:
includeNodePolyfillsdefaults tofalse. It also controls theglobalandprocessdefines, theindex.htmlshims and thestreamstub, which used to apply with polyfills off too. - Breaking:
fsModulesdefaults to[], so nofsmodules are aliased to ZenFS unless you list them. - Breaking:
autoInitis removed from the options type. At run time it's ignored without a warning. - Breaking: the plugin no longer sets
optimizeDeps.includeorexclude, the interop aliases (debug,arweave,eventemitter3,nanoid-dictionary, the ZenFS entry files),build.commonjsOptionsor abetter-sqlite3external, and no longer rewrites the SDK'spath-browserifyimport. - Breaking: the Node-globals warning on production chunks runs only with
debug: true. - Added:
optimizeDeps.rolldownOptions.output.strictExecutionOrderdefaults totrueunless you set it. Without it,vite devfailed withCannot read properties of undefined (reading 'Symbol')when an app imported both@seedprotocol/sdkand@seedprotocol/sdk/drizzle. - Changed:
resolve.dedupelists only the packages the app root resolves, andserver.fs.allowfinds the SDK, sqlocal and SQLite WASM in pnpm and bun stores. - Changed: no longer depends on
debug. - Fixed:
vite devworks under pnpm's default isolated layout. The oldoptimizeDeps.excludelist broke it.
See @seedprotocol/vite for what the plugin sets now.
@seedprotocol/react
- Added:
SeedProvidertakesconfig,addressesandonInitErrorand starts the client itself, so<SeedProvider config={config}>works withoutSeedClientGate. Children render at once and the hooks report loading until the client is ready. Init runs again whenconfigchanges identity oraddressesholds different values.SeedClientGatestill works for a loading screen. - Added: the bundle is marked
'use client', so a React Server Components app (the Next.js App Router) can importSeedProviderand the components into a server component such asapp/layout.tsx. That used to fail withcreateContext is not a function. - Changed:
SeedClientGate'sschemaprop takesstring | SchemaFileInput.
@seedprotocol/publish
- Fixed: bundler and Turbo uploads no longer use the
Bufferglobal, so a browser publish doesn't need aBufferpolyfill. - Fixed: after a direct-to-EAS publish, attestation verification waits for the EAS indexer. A partial or empty result is retried up to 6 times, 5 seconds apart, before
AttestationVerificationError(METADATA_PROPERTIES_MISSING); it used to fail on the first incomplete result. - Fixed: File, Image and Html storage IDs are attested under
string storage_transaction_id, never the legacybytes32schema (through the SDK, above).
@seedprotocol/query
- Added:
getSeeds(seedUids, options?)reads many Seeds, of any schemas, in one set of indexer requests and returns aMapkeyed by each UID as given. It takesgetSeed's options except the changelog, pluspins, and shares the item cache withgetSeed. Also theGetSeedsOptionsandVersionPintypes. - Added: pinned reads.
getSeed(seedUid, { versionUid, asOf })reads a Seed at one of its Versions, with relations expanded and storage hydrated as for the latest, and returnsnullwhen the Seed has no such Version.asOf(Unix seconds) leaves out attestations made after it. A patch publish adds properties to the current Version, soversionUidalone also shows later patches. - Fixed:
queryBySchema,queryBySchemaForMonthandgetSeedreturn only the Seeds asked for. A related Seed with the same schema (a post linking to another post) used to come back as an extra record, pastlimit, and went into the item cache. Feeds from@seedprotocol/feedlose those extra items too.
@seedprotocol/arweave
- Changed:
getTransactionStatustries the read gateways in order (getArweaveReadBaseUrls()), 10 seconds each and 20 seconds in all, skipping gateways whose circuit breaker is open. A 2xx to 4xx answer is returned, and a 404 isn't retried elsewhere; a timeout, network error, 5xx or 429 moves on to the next gateway. If none answers, it returns 504 when the last attempt timed out, otherwise the last failure's status. It used to make one request to the configured host with no timeout. The SDK's browser client uses it too. - Added:
fetchTransactionStatusAcrossGateways,DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS,DEFAULT_TRANSACTION_STATUS_TOTAL_TIMEOUT_MSand theTransactionStatusFetchOptionstype. - Changed:
DuplicateArweaveModuleError's Vite advice: keep@seedprotocol/*packages out ofoptimizeDeps.exclude, and dedupe them when they're linked into the app.
@seedprotocol/eas
- Added:
getSchemaUidForSchemaDefinitionFromCache(text)returns the UID cached for a schema definition, without a request. - Changed:
DuplicateEasModuleError's Vite advice, as for arweave.
@seedprotocol/gateway-hyper
- Changed: the operator no longer forwards hop-by-hop request headers or
content-length, and moves the client'sHosttoX-Forwarded-Host, so the upstream sees its own host. Check any upstream rule that matched on the forwardedHost. - Changed: a tunnel session sends requests over up to
maxStreamsHyperDHT streams to the operator (default 4), one request per stream at a time, so a slow request holds up only its own stream. Operators see up to that many connections and concurrent upstream requests from each client. The wire protocol is unchanged. - Added: timeouts. The client answers 504 once a request's stream has sent and received nothing for
requestTimeoutMs(default 60,000), and 502 when it can't reach the operator withinconnectTimeoutMs(default 30,000). The operator answers 504 when its upstream has been idle forupstreamTimeoutMs(default 30,000), and 502 for other upstream failures, keeping the stream open.0turns a timeout off. A request the upstream never answered used to block the session until restart. - Added:
forwardHttpandrunOnTunneltake{ signal, timeoutMs }.handleFetchpassesrequest.signal, andhandleNodeand the sidecar abort when the caller disconnects. - Added: the CLI flags
seed-gateway serve --upstream-timeout-msandseed-gateway connect --request-timeout-msand--max-streams; the new options oncreateGatewayProxy,connectTunnelandserveTunnel; and the exportsDEFAULT_CONNECT_TIMEOUT_MS,DEFAULT_MAX_STREAMS,DEFAULT_REQUEST_TIMEOUT_MS,DEFAULT_UPSTREAM_TIMEOUT_MS,TunnelConnectTimeoutError,TunnelTimeoutError,UpstreamTimeoutErrorand theTunnelRequestOptions,TunnelSessionandTunnelSessionOptionstypes. - Fixed: bodies between 16 and 64 MiB pass through the tunnel, written in 1 MiB chunks. Bodies over 64 MiB are refused in both directions.
0.7.5
Released 2026-10-17. Upgrade steps: Upgrading from 0.7.4 to 0.7.5.
@seedprotocol/sdk
- Breaking: Number properties publish as
string <name>instead ofuint8 <name>, so values above 255, negatives and decimals publish; they used to fail withFailed to encode property. The value is attested as its decimal string, and a stored value that isn't a finite number fails publish validation withpublish_invalid_number. The first publish after upgrading registers the new schema, so published Number values move to a new EAS schema UID. Items published before keep theiruint8attestations until the property changes. - Breaking: EAS sync skips, with a warning, a property attestation whose EAS type doesn't fit the local property. A Number accepts integer types and numeric strings; other types accept only the type publish writes for them. Any attestation with a matching name used to be stored.
- Breaking: a
Fileproperty's value is its content URL (blob:in browsers,file://in Node) after a save and after a reload. It used to turn into the file's text a moment after a save or load. Read the contents through the URL (fetchin browsers,fsin Node).HtmlandItemStoragevalues still read as their text. - Breaking: a draft that has never had a publisher is owned whatever the owned addresses are: it's editable,
addressFilter: 'owned'lists it, and the next wallet to connect becomes its publisher. A draft with a publisher is owned only while that address is. A draft with no publisher used to be read-only, and left out of owned lists, whenever any owned address was set. - Changed: a new draft gets the owned address as its publisher when exactly one is set and no publisher getter supplies one. With several, it has none until the next wallet connects; register
setGetPublisherForNewSeedsto give it one at creation. - Changed: EAS sync stores numeric values decoded: a Number as a decimal string, a Date attested as an integer as an ISO string. It used to store
{"type":"BigNumber","hex":…}text. Rows synced before stay as they are. - Changed:
item.unload()andmodelProperty.unload()release one holder. A shared instance stops when its last holder unloads it, and a model's own properties run until the model is evicted. The firstunload()used to stop it for everyone. - Added:
nameMatch('exact','normalized'or'fuzzy') on a property, a model or a schema's top level sets how loosely names from EAS match it. The setting nearest the property wins, and the default is'fuzzy'. Also theSchemaNameMatchtype. - Added:
summarizePublishWorkreturnsuploadSizes, the size of each upload.restoreDataItemIdsOverwrittenByL1()is exported. - Deprecated:
applyArweaveL1TransactionIdLocal. - Fixed:
Item.createleaves nothing behind when it rejects: the draft, the storage items it points at and their files are removed. A File, Image, Html or ItemStorage value that breaks its property's rules rejects withItemCreateValidationErrorlike the other values; Html rule failures used to be a plainError. - Fixed: Node processes exit on their own after
client.stop(), so scripts don't needprocess.exit(). - Fixed:
client.initafterclient.stop()initializes again with the new config, andclient.isInitialized()isfalseafterstop(). It used to resolve without doing anything. - Fixed:
client.initno longer creates<cwd>/.seed, so an absolutefilesDirworks from a read-only working directory such as a packaged Electron app's/. A relativefilesDirstill resolves under<cwd>/.seed. - Fixed: a
dbConfig.dbUrlin a folder that doesn't exist yet works; the folder is created. - Fixed: publishing Html with an embedded
data:image/…image works again. Since 0.7.2 it failed withItemPropertySaveValidationErrorbefore anything was uploaded. - Fixed: version and property rows take their Seed's publisher, and
claimUnpublishedDraftsclaims them only together with their Seed, so connecting another wallet no longer claims rows of someone else's Seed. A one-time step after init fixes existing rows. - Fixed:
0andfalseare attested; only a missing or empty value is skipped. - Fixed: assigning a property the value it already has no longer adds a draft row next to a published value.
- Fixed:
getEasSchemaUidForModellooks up the model's EAS name (blog_post), not its lowercased name (blogpost).
@seedprotocol/publish
- Breaking: Number properties register
string <name>EAS schemas (see the SDK above). - Breaking: without
useDirectEas, a publisher with no contract code attests straight to EAS only when itsPublishWallethaskind: 'eoa'and signs as the publisher.fromEip1193ProviderandfromEthersWalletsetkind: 'eoa',fromThirdwebAccount(account, { kind })takes it, andConnectButton,ensureSmartWalletThenPublishandenrollPublishAutomationset it. A wallet with nokindtakes the registered session's kind when the signer matches, and is refused otherwise. Addkindto a hand-built{ signer, txSender }wallet, or setuseDirectEas. New typePublishWalletKind. - Breaking:
initPublishthrows whenuploadApiBaseUrlis missing, unlesspayment.storageis'user', and when it isn't an absolute http(s) URL or an origin path such as/seed-api. A missing value used to fail only at upload time. - Breaking:
PublishManager.query(seedLocalId)only reads: it returns the publish's current snapshot, orundefined. It used to stop the publish. - Breaking:
ensureWalletThenPublishandensureSmartWalletThenPublishresolve once the publish has started, with{ outcome: 'started', publish }(the publish actor) or the new{ outcome: 'already_publishing' }.startedis deprecated and already resolved. - Changed:
@seedprotocol/sdkand@seedprotocol/reactare peer dependencies at the exact release version, no longer dependencies. npm installs one top-level SDK even when the app lists only@seedprotocol/publish; publish used to bring its own. - Changed: data items are signed by an EOA.
PublishWalletSession.dataItemSignerholds that signer, andConnectButtonsets it to a smart wallet's admin EOA (withuseModularExecutor, the connected EOA).item.publish()andensureWalletThenPublishdefault to it, or to the wallet's own signer when the wallet isn'tkind: 'smart'.PublishManager.createPublishstill has no default. Signing checks that the signature recovers to the signer's address. - Changed:
ConnectButtonstops running publishes only when another wallet takes over, not on every wallet sync. - Changed:
useCanPublishItemisfalseuntil its check finishes, and checks again when owned addresses change. - Changed:
pollForConfirmationgives up after 30 minutes, or after 12 failed status requests in a row, soitem.publish()on the L1 path settles. - Changed: cost estimates charge an L1 upload for at least 256 KiB, a bundle once, and nothing for Turbo uploads up to 100 KiB (
arweave.chargedBytes;arweave.bytesstays the raw size). Testnet gas is priced at $0, withevm.testnet: true. - Deprecated:
useLocalWalletAccount(alwaysnull), and thegatewayTransportandmodularAccountModuleDataconfig fields, which nothing reads. - Fixed: a new user's smart wallet is deployed on its first publish.
PublishWalletSession.deploydeploys it, andConnectButtonsets it for a smart wallet withoutuseModularExecutor. In 0.7.4 that first publish was refused withMANAGED_ACCOUNT_NOT_DEPLOYED, which a wallet withoutdeploystill gets. - Fixed: a bundler or Turbo publish restored after a reload no longer skips its upload. Its signed data items aren't saved, so restore marks it
interrupted, to publish again, and an upload with nothing to send fails with codeSIGNED_DATA_ITEMS_MISSING. - Fixed: local storage IDs stay the attested data item IDs. The L1-finalize worker records the L1 ID in its own table instead of rewriting them, and once per session restores IDs it rewrote before.
- Fixed: Html with embedded images gets its second upload pass on the direct-EAS route, and on the L1 path that pass is reimbursed, so the user's Arweave key no longer needs AR.
- Fixed: EIP-7702 wallets keep attesting directly after their first publish. A delegated EOA's code no longer counts as a deployed contract.
- Fixed:
PublishProvidercallsinitPublishduring render, once per config object, soConnectButtonno longer throws on first render under a config-only provider.
@seedprotocol/react
- Changed: the
@seedprotocol/sdkpeer is the exact release version instead of^0.6.0. npm refuses a mismatched React and SDK pair withERESOLVEinstead of letting it fail at import, and nooverridesblock is needed. The source is unchanged.
@seedprotocol/eas
- Added:
decodeEasNumber(value)turns an indexer value (a number, a decimal or hex string, a bigint, or{ type: 'BigNumber', hex }) into a decimal string, andisNumericEasType(easType)says whether a type isuint*orint*. - Added:
NAME_MATCH_LEVELS,isNameMatchLevelandeasNamesToFetch(name, level).localNamesInBestMatchTier'slevelcan be a function of the local name. - Changed: the
exactname-match tier means the EAS name is exactlyeasName(local), soTitleandhtml-pageare nownormalizedmatches. At the defaultfuzzylevel only the tier ranks change. - Changed:
getCanonicalItemPropertiesFromEaschecks attesters by default: a Version counts only if its Seed's attester made it, and a property only if its Version's attester did. It makes two more requests; passcheckAttesters: falseto skip them.
@seedprotocol/query
- Fixed: values from
uint*andint*property attestations come back as decimal strings, not the indexer's{"type":"BigNumber","hex":…}JSON. A Date published as an integer reads as Unix seconds. - Fixed:
mainpoints at the Node build, with a top-leveltypesand abrowserfield, for resolvers that ignoreexports(TypeScript'snode10resolution, older bundlers).
All packages
- Internal
@seedprotocol/*dependencies and peers are the exact release version, and every package has a top-leveltypesfield.vite,arweave,feed,feed-hyper,gateway-hyperandmappingchanged only their manifest and README.
0.7.4
Released 2026-10-16. Upgrade steps: Upgrading from 0.7.3 to 0.7.4.
React and the SDK had to be upgraded together. React 0.7.4 imports easName, which first shipped in SDK 0.7.4, so React 0.7.4 with SDK 0.7.3 failed at import (does not provide an export named 'easName'). Its peer range still said ^0.6.0, which no 0.7 SDK matches, so npm needed an overrides block. Since 0.7.5 React's peer is the exact SDK version and no overrides block is needed.
@seedprotocol/sdk
- Breaking:
Item.createchecks its values before writing anything. An unknownmodelName(inschemaName, if given) throwsUnknownModelError. Unknown keys, and values that fail their property's type or validation rules, throwItemCreateValidationError, whoseproblemslists each one as{ property, message, code }. Pass{ validate: false }as the second argument to skip the value checks when importing data that's already stored or attested.Model#create(values)and React'suseCreateItemalways validate.requiredisn't checked, and File, Image, Html and ItemStorage values are still checked by their own save. - Breaking: validation rules that were skipped are enforced.
patternapplies toText; onHtmlit's kept alongside a length rule;enumcombines with the other rules instead of replacing them (and no longer gets Text's default 255-character limit); rules on properties defined in code (Model.create(…, { properties })) are enforced. Values that used to save may now be rejected, on create and on assignment. - Breaking: EAS names come from one function,
easName(ADR 0008). Model and property names with acronyms, a digit before a capital, spaces or hyphens publish to new EAS schemas (HTMLPagewasbytes32 htmlpageand is nowbytes32 html_page;ID3Tagbecomesid3_tag). A singleRelation,Image,FileorHtmlproperty always publishes under its schema key (bytes32 author), neverauthor_id. Names likePost,BlogPostandpublishedAtare unchanged. EAS sync still finds items published under the old names. - Breaking: a
Fileproperty given a string stores it as follows: adata:URL is decoded to bytes, anhttp(s):orblob:URL is fetched, the name of a file already in the files folder links that file, and anything else is stored as the file's text. A raw base64 string used to be decoded; it's now stored as text, so pass adata:URL instead. - Changed: after init, the SDK rewrites the model types it stored (
seeds.type, and the matching columns of versions and metadata) to theeasNameform (image_v2, notimage_v_2;html_page, nothtmlpage) once per type, in the leader tab. Raw SQL that filters on the old forms stops matching. - Changed: importing a schema treats a property as renamed only when its
idmatches a stored property's. The metadata rows then move to the new name. Without anidmatch, a changed name is an added property plus a removed one, and the old values stay under the old name. - Changed:
unload()releases one reference, and the instance stops only when no reference is left. This covers Schema, Model and the other entities, but not Item:Item.unload()still stops the shared item at once. - Changed:
Schema.destroy()deletes the schema's version files in the files folder, so the nextSchema.all()doesn't import it again. It still doesn't delete items or anything published. - Changed:
validation.customis reported as unsupported by schema validation (custom_validation_unsupported). Loading a schema that uses it still doesn't fail, so it still has no effect. - Deprecated:
DbConfig.schemaDir, which nothing reads. - Added: exports
ItemCreateValidationError,UnknownModelErrorand theItemCreateProblemandItemCreateOptionstypes (also from@seedprotocol/sdk/node);easName,matchEasNameand theNameMatchLeveltype, re-exported from@seedprotocol/eas;easPropertyNameForPublish; andsetRetriesWhenOnlineEnabled. - Added: offline retries. The start-up EAS schema lookup and failed automatic syncs are retried when the browser comes back online, or otherwise after 15 seconds, 30 seconds, 1, 2 and 5 minutes (also in Node).
client.stop()cancels them. They're off whenNODE_ENVor Vite's mode istest, unless you callsetRetriesWhenOnlineEnabled(true). - Added:
schema.lastSaveErrorandmodel.lastSaveError. A rename or other tracked edit starts a background draft save; when it fails (aConflictError, for example) the error is kept there and logged withconsole.warn, instead of only reaching the debug log. - Added: importing a schema warns when two models, or two properties of one model, have the same
easNameand so would publish to the same EAS schema. The schema still loads. - Fixed (security): EAS sync ignores Version attestations not made by the Seed's attester, and property attestations not made by the Version's attester. Rows like that from earlier syncs are deleted once after init, without the network. Drafts are kept.
- Fixed:
config.dbConfigworks in Node:dbUrlsets the database file (default<filesDir>/db/seed.db) andoutDirwhere migrations are copied and run from (default<filesDir>/db). The browser ignores it. - Fixed:
Datevalues validate, so assigning aDateproperty on an existing item saves. - Fixed:
Item.create, and Image, File and Html saves, work offline. The model's EAS schema UID is read from the local database first, and a failed EAS request no longer fails the create. - Fixed: a
ListofRelationreads back as an array, after create and after a reload. - Fixed: new Image and File values are stored as
<seedLocalId>-<name>, so two items whose files share a name no longer overwrite each other. - Fixed: writes to a property go through a queue, one at a time, and a stored value no longer replaces the in-memory one while a save is pending. A text input bound to
item.title(viauseItem) no longer drops keystrokes (one bound toproperty.valuefromuseItemPropertystill can, see the React notes), and another tab's write no longer replaces a value mid-edit. - Fixed: editing a draft moves its timestamp to the time of the edit, so the edit wins over synced values attested before it.
- Fixed: a sync cut short by a closed tab no longer stays
in_progress. The next sync marks itfailedwithInterrupted: the sync stopped before it finished (tab closed?). - Fixed:
client.init({ addresses })emitsaddresses.persistedafter saving them, assetAddressesdoes. - Fixed:
saveNewVersion()writes a version file that loads again: every model, each property in file format, and the newversion. A schema created only in code gets aversionandmetadata, so it can be saved. If the app keeps passing the older file asconfig.schema, the next start reports the older version andsaveNewVersion()throwsConflictError. - Fixed: a version bump through
config.schemaupdates the schema row to the new file's id and version, and an older file no longer rolls it back.renameModelPropertyand the other schema edits work after a bump. In the session that applies the bump,Schema.create(name).versionandmodel.propertiesstill show the old version until the next start. - Fixed: schema ids keep hyphenated names (
my-appstaysmy-app); only a numeric-<n>suffix is dropped. - Fixed: a destroyed model is no longer cached again by a lookup that runs while its rows are deleted.
- Fixed: the 10-second start-up EAS timeout is cleared when the lookup settles. Node processes still don't exit on their own.
- Fixed: an unrelated error whose stack happens to contain
2067is no longer taken for a SQLite UNIQUE violation. - Fixed:
Schema.nameandModelProperty.idare declared in the types.
@seedprotocol/publish
- Breaking: a publish from a smart account that isn't deployed yet is refused with
ManagedAccountPublishErrorcodeMANAGED_ACCOUNT_NOT_DEPLOYED, instead of attesting straight to EAS one transaction per Seed. A wallet that sends as a different address than the publisher is refused too. Plain wallets (EOAs) anduseDirectEas: truestill attest directly.ConnectButtonrecords the admin address so publish can tell the two apart. - Breaking:
clearPublishWallet()returns a Promise and also clears the SDK's owned addresses, keeping watched ones.ConnectButton's disconnect anduseSeedWallet().disconnectcall it. Drafts created after a disconnect are signed-out drafts that the next connect claims. - Changed: a publish starts in a new
authorizingstate, beforechecking. It only does something whenauthorizePublishis set. - Changed: EAS schemas are registered under
easNamenames (see the SDK above). - Changed: the per-publish
arweaveJwkanddataItemSignerare no longer saved inpublish_processes. A publish restored after a reload signs withPublishConfig.arweaveJwkordataItemSigner, and fails atcreatingArweaveTransactionswithout them. Rows written by 0.7.3 and earlier keep their copy. - Changed:
createPermissionlessTxSendertakessponsored(defaulttrue);falsesends without a paymaster. - Added:
PublishConfig.payment({ gas?: 'sponsored' | 'user', storage?: 'operator' | 'user', userStorage? }) says who pays. It fills in the defaults of the lower-level settings, andinitPublishthrowsPublishPaymentConfigErrorwhen one contradicts it. AlsoresolvePublishPayment,resolvePublishSenderand their types. - Added: user-paid storage through Turbo, with
payment: { storage: 'user' }. Data items signed by the user's key go straight to Turbo and are charged to that address's Turbo credits (oruserStorage.paidByfirst). An unfunded signer getsInsufficientStorageCreditsError.useArweaveBundlerdefaults totruewith this setting. AlsogetTurboBalanceWincandgetTurboPriceWinc. - Added:
authorizePublish, in the config and per publish. It's called before anything is spent, with the item, the payment and a cost estimate. Returnfalseor throw to refuse (PublishNotAuthorizedError), or{ token }to send a token asX-Seed-Publish-Authorization(PUBLISH_AUTHORIZATION_HEADER) on the upload API requests. - Added:
DOMAIN_OWNERSHIP_SCOPE_HOSTNAME. A challenge made withnormalizeRegistrable: falsedefaults to it, and the attestation records the hostname. - Fixed:
estimatePublishCostreports who pays from the sender the publish will use:evm.userPaysisfalsewhen Thirdweb or a paymaster sponsors, andarweave.userPaysistrueonly withpayment.storage: 'user'. Prices are unchanged. - Fixed: connecting a wallet (
setPublishWallet) keeps the SDK's watched addresses. - Fixed: a direct-to-EAS publish records each Seed as soon as it's attested, so a retry after a failure part-way no longer attests a second Seed.
- Fixed:
unpublish()revokes from the attesting account when the registered wallet isn't it (for example withuseModularExecutor), leaves out UIDs EAS already shows as revoked, and recognizesAlreadyRevokedby its selector too. - Fixed:
assessDomainOwnershipLivehonors the attestation'sexpirationTime, and live checks of easscan data no longer report a falselikely_transferred. - Fixed: the package's
testscript runs every test file.
@seedprotocol/react
- Breaking: needs
@seedprotocol/sdk0.7.4 (see the note above).useItemsmatches items by theeasNameof the model. - Breaking:
UseCreateModelOptionsno longer hasregisterWithSchema, which did nothing. - Deprecated:
usePersistedSnapshotsanduseHasSavedSnapshotsare no-ops: the first does nothing and the second always returnsfalse. - Added: exports
useIsClientReady,useLiveQueryState({ data, error }),useStorageEstimate,useThumbnail, and theStorageEstimate,ThumbnailStatus,FileEntry,UseDeleteItemReturn,UseCreateModelOptions,UseCreateModelReturnandUseDestroyModelReturntypes. - Fixed:
useLiveQuerykeys a Drizzle builder by its SQL and parameters, so an inline builder no longer resubscribes on every render; rows reset toundefinedwhen the query changes. A SQL tag function is still keyed by reference. - Fixed:
useItemsreportsisLoading: trueuntil the client is ready, anditemskeeps its identity while the data is unchanged. Deleting the last item still leaves it on screen. - Fixed:
useCreateItemno longer creates twice when called twice before a re-render. - Fixed:
useDebouncedItemPropertysaves the pending edit on unmount and returnsflush(). - Fixed:
useItemPropertywithitemIdtries it as a local ID, then as aseedUid. - Fixed:
useModelalways calls its hooks in the same order;useModelsanduseSchemasrefresh when models or schemas are deleted;useSchema(name)returnsschema: nullfor an unknown name instead of creating a draft, and reports machine errors withisLoading: false. - Fixed:
useDbsAreReady,useIsDbReadyanduseGlobalServiceStatus().internalStatusturn ready once the database is open, and back to not ready if init fails.useServices().percentCompletestarts from the current state. - Fixed:
useCreateModelPropertyand three-argumentuseModelPropertykeep hyphenated schema names. - Fixed:
useEasSyncProcessById(undefined)reportsisLoading: false. - Fixed:
useResolvedMediaRefrevokes the blob URLs it creates;formatSeedJsonprints[Circular]only for real cycles;SeedImagere-renders when any prop changes, setswidthon the<img>, and takes anIItemProperty.
@seedprotocol/vite
- Changed:
@seedprotocol/eas,@seedprotocol/arweaveand@seedprotocol/queryare pre-bundled together and deduped, so a later optimizer pass can't load a second copy. - Changed: the plugin adds its own aliases,
resolve.dedupeentries and build options without copying yours back, so nothing is duplicated and your aliases match first. - Changed: new dependency
@rollup/pluginutils, for the bare-globals scan. - Fixed: COOP, COEP and CORP headers you set in
server.headersorpreview.headersare kept; the plugin adds only the ones you didn't set. - Fixed: builds no longer warn "Both
rollupOptionsandrolldownOptionswere specified", and your build options pass through. - Fixed: the bare Node globals warning reports only free references to
global,process,Buffer,__dirnameand__filename, so a default app builds without it.
@seedprotocol/eas
- Added:
easNameand the name-matching helpers ADR 0008 uses (matchEasName,easNameVariants,normalizedNameKey,fuzzyNameKeyand others). - Added:
partitionByRefAttesterandkeepAttestationsFromRefAttester, to keep only Versions made by their Seed's attester and properties made by their Version's attester. The raw reads don't filter. - Added:
assertSingleEasModuleandDuplicateEasModuleError, thrown when another copy of the package registered the EAS clients. - Added:
GET_DOMAIN_OWNERSHIPS(withexpirationTime) andDOMAIN_OWNERSHIP_SCOPE_HOSTNAME. Nested bytes32 values from easscan now decode.
@seedprotocol/query
- Breaking: every read throws
DuplicateEasModuleErrororDuplicateArweaveModuleErrorwhen the clients were registered on another copy of@seedprotocol/easor@seedprotocol/arweave(a second install, or a separate Vite pre-bundle). It used to read quietly through other clients and possibly another chain. - Fixed (security): a Seed's Versions count only when its attester made them, and a Version's properties only when the Version's attester made them, in
queryBySchema,getSeedand changelogs.
@seedprotocol/arweave
- Added:
assertSingleArweaveModuleandDuplicateArweaveModuleError.
All packages
- Internal
@seedprotocol/*dependencies are pinned to 0.7.4.@seedprotocol/feedchanged only its README;feed-hyper,gateway-hyperandmappingchanged only their version.
0.7.3
Released 2026-10-14. Upgrade steps: Upgrading from 0.7.2 to 0.7.3.
React and the SDK had to be upgraded together. React 0.7.3 imports SDK exports that first shipped in SDK 0.7.3, so React 0.7.3 with SDK 0.7.2 failed at import (does not provide an export named 'placeholderUidSql'). Its peer range still said ^0.6.0, so npm needed an overrides block, and pnpm and bun installed a mismatched pair with only a warning. Since 0.7.5 React's peer is the exact SDK version and no overrides block is needed.
@seedprotocol/sdk
- Breaking: the property decorators are removed from
@seedprotocol/sdk/node:Text,Number,Boolean,Date,Json,Image,ListandRelation. Nothing read the metadata they recorded. Use thePropertybuilders, which./nodekeeps along withModel. - Breaking:
engines.nodeis^20.19.0 || >=22.12.0 <=24(it was>= 20 <= 24). Node 20.0–20.18, 21 and 22.0–22.11 are outside the range. - Breaking: a second
client.initwith a different config throwsINIT_CONFIG_CONFLICT_MESSAGEinstead of resolving and keeping the first config. Callclient.stop()before initializing with a new config. A repeatinitwith the same config resolves, and applies newaddressesthroughsetAddresses. - Breaking:
client.initrejects whenconfig.schemaorconfig.schemaFilecan't be read or applied, withCould not load config.schema from "<path>": <reason>. It used to succeed and log the error. With React, the error reachesSeedClientGate'sonInitError. - Breaking: assigning a property on an item the client's addresses don't own (
post.title = 'x') is refused. Nothing is written, the value rolls back, and the property's_saveErroris anOwnershipError. A side effect: once owned addresses are set, a draft created without a publisher isn't owned, so assignments to it are refused until it's claimed.@seedprotocol/publishsupplies the publisher while a wallet is connected; Node scripts have to supply it themselves. See the upgrade steps. - Breaking:
destroy()on Item, ItemProperty, Model, ModelProperty and Schema rejects when the delete fails. It used to resolve with the rows still there. - Changed:
item.publish(options?)hands the item to the executor that@seedprotocol/publishregisters ininitPublish()(or<PublishProvider config>) and resolves when the publish has finished. Without the publish package it throws "Publishing is not configured…" at once. The 5-minute timeout and the "payload only" behavior are gone.Item.publish(item, options)takes the options too. - Changed: the default
readyTimeoutforcreate,findandallon Schema, Model, ModelProperty, Item and ItemProperty is 15 seconds. Most were 5 seconds. - Changed:
client.initwithoutaddresseskeeps the stored addresses and warns once.addresses: []still clears them in Node and in the first tab. - Changed:
destroy()on a ModelProperty, Model or Schema works when items have values for it. The items' stored values are kept and unlinked from the property. - Changed: the
Content-SHA-256upload tag is SHA-256 on both upload paths (the L1 path wrote SHA3-256). Downloaded files are checked against it: a gateway that serves different bytes is skipped, and if none matches, nothing is saved. Uploads from 0.7.2 and earlier are still accepted. - Changed: every file write is announced to other tabs as
fs.files.changed, coalesced over 25 ms, not only Image, File and Html saves. - Changed: publish creates L1 transactions four at a time.
- Changed:
reflect-metadatais no longer a dependency. - Deprecated:
setUploadExecutor,getUploadExecutorandUploadExecutor(nothing calls them), andownedPublisherSql(useownedSeedsSql). - Added: exports from the main entry:
OwnershipError,assertItemOwned,ConflictErrorand theConflictResult,ValidationErrorandValidationResulttypes;setPublishExecutor,getPublishExecutor,PUBLISH_NOT_CONFIGURED_MESSAGE,INIT_CONFIG_CONFLICT_MESSAGEand thePublishExecutorandItemPublishOptionstypes;getPublishUploadData,contentSha256TagMatchesand the otherContent-SHA-256helpers;ownedSeedsSql,localUnsealedDraftSqlandplaceholderUidSql; and the experimental Arweave content read transform from@seedprotocol/arweave. - Added:
@seedprotocol/sdk/nodeexportsAmbiguousModelError,ConflictError,OwnershipError,assertItemOwned,isItemOwned, and theIItem,IItemProperty,MultiTabMode,SyncFromEasOptionsandHtmlEmbeddedDataUriPolicytypes. - Added: after each init, the leader tab moves ItemStorage content that older versions stored in the database row into its file, once.
- Fixed:
require('@seedprotocol/sdk')andrequire('@seedprotocol/sdk/node')work on Node 20.19+ and 22.12+. They load the same ESM build asimport, so mixed code shares one client. - Fixed: Node no longer needs
--import @seedprotocol/sdk/node-eas-lodashfor the SDK. Processes that load@seedprotocol/publishstill do. - Fixed: in Node the SDK finds its database migrations from any working directory. It still creates
.seedin the working directory at every init. - Fixed:
@seedprotocol/sdk/platformexportsconfigurePlatform. - Fixed:
PropertyDataTypeis a union of string literals, so{ dataType: 'Text' }type-checks.Schema.create,Model.create,ModelProperty.createandItemProperty.createare typed as returning a Promise unless you passwaitForReady: false.client.initis typed withSeedConstructorOptions. - Fixed:
client.onReady(callback)registered after the client is ready fires, and returns a function that cancels it. - Fixed:
client.claimUnpublishedDraftsupdatespublisheron items already loaded, in every tab, and returns the claimedseedLocalIds. - Fixed:
addressFilter: 'owned'lists exactly whatisItemOwnedcalls owned: local drafts when no owned addresses are set, otherwise items whose publisher (or, when it's blank, attester) is owned. - Fixed:
Model.createwithpropertieswaits until the properties exist and rejects if they can't be created.ModelProperty.createrejects when its row isn't written.Model.all({ waitForReady })no longer drops ready models. - Fixed: schema reloads (
renameModelProperty,updateModelProperties, a changedconfig.schema) update the property row in place and refresh cachedModelPropertyinstances. - Fixed:
ItemProperty#destroy()checks ownership and deletes only its own item's rows (every version of them). - Fixed: an ItemStorage value passed to
Item.createis written to its file beforecreateresolves, and is included in publishes. Each ItemStorage file is locked across saves. - Fixed: a listener that throws no longer breaks the SDK code that emitted the event. The error is logged and the other listeners still run.
- Fixed: upload
Content-Typecomes from the file's own type, then its extension (documents, audio, video, AVIF and HEIC are now recognized), then its bytes. Downloads are filed byContent-Type, so non-images no longer land inimages/and plain text is kept. Binary files inside multi-file ItemStorage uploads stay intact. - Fixed: file downloads settle when the worker fails or a gateway stalls (60-second timeout), instead of hanging.
- Fixed: browser OPFS writes no longer leave the old tail when a file gets shorter, or cut off bytes another tab wrote.
- Fixed:
filesDir: '.seed-alt/x'resolves under.seed; only a first segment of exactly.seedcounts as already inside it. - Fixed: in Node before 24.5, which has no
navigator.locks, concurrent inits and syncs in one process take turns instead of racing.
@seedprotocol/publish
- Added:
initPublish()registerspublishItemas the executor behind the SDK'sitem.publish(), soitem.publish(options)runs a full publish (uploads and attestations) with the wallet fromConnectButtonorsetPublishWallet.optionsareCreatePublishOptions. On the bundler path, pass your owndataItemSigner: withConnectButton, the default signer is the smart account, which can't sign data items. - Added:
publishItem(item, options),waitForPublishActor(actor, seedLocalId)and thePublishStartedtype. - Changed:
ensureWalletThenPublishandensureSmartWalletThenPublishreturn{ outcome: 'started', started }.startedresolves to the publish actor, orundefinedwhen the item was already publishing. Neither waits for the publish to finish. - Fixed: the bundler path builds its uploads with the SDK's
getPublishUploadData, so both paths upload the same bytes and tags. Hashing no longer needs WebCrypto, so it works on plain-HTTP pages other than localhost.
@seedprotocol/react
- Breaking: needs
@seedprotocol/sdk0.7.3 (see the note above). - Fixed:
usePublishItempublishes once@seedprotocol/publishis initialized, and itserrorreports the rejection ofitem.publish(): not configured, not owned, already publishing, or the publish's own failure.publishItem(item, options?)takes the publish options, andisLoadingcovers the whole publish. - Fixed:
useItems({ addressFilter: 'owned' })uses the same rule asisItemOwned. - Added:
useEnsureLocalImagereturnsgatewayHrefsandonGatewayError(), andSeedImagemoves to the next read gateway when an image fails to load, so a just-published image no longer 404s while it propagates.
@seedprotocol/arweave
- Added:
getTransactionTagsByIds(ids, options?)reads many transactions' tags in batched GraphQL requests (up to 100 per request), withGET_TRANSACTIONS_TAGS_BY_IDSandTRANSACTION_TAGS_BATCH_SIZE. - Added:
BaseArweaveClient.isConfigured(). - Added (experimental): a read-side content transform:
setArweaveContentReadTransform,hasArweaveContentReadTransform,transformArweaveContent,readArweaveContentResponseandgetResponseMediaType. Nothing registers one yet. - Changed:
BaseArweaveClient.getTransactionDataisasyncand runs decoded data through the transform when one is registered.
@seedprotocol/eas
- Added:
DefaultEasClient,DefaultQueryClientandensureEasPlatform()from the main entry: fetch-based clients for browsers and Node.NodeEasClientandNodeQueryClientnow extend them. - Added:
isConfigured()onBaseEasClientandBaseQueryClient.
@seedprotocol/query
- Fixed:
initializeQueryPlatformkeeps EAS clients and an Arweave gateway that are already registered, so query next to a runningSeedClient, or after@seedprotocol/feed's init, no longer replaces them. The browser build no longer loads the Node clients. AnarweaveDomainyou pass still sets the host.
All packages
- Internal
@seedprotocol/*dependencies are pinned to 0.7.3.@seedprotocol/feed,feed-hyper,gateway-hyper,mappingandvitechanged only their version.
0.7.2
Released 2026-10-11.
@seedprotocol/eas
- Breaking: version and property attestations returned by
GET_VERSIONS,GET_PROPERTIES,GET_ALL_PROPERTIES_FOR_ALL_VERSIONS,GET_FILES_METADATAandGET_IMAGE_VERSIONSno longer includeschema { schemaNames }, which easscan resolved slowly. ReadschemaIdinstead. - Added:
GET_SEEDS_LEAN,getSeedsByUidsFromEas,getAttestationChangesSincewithGET_ATTESTATION_CHANGES, andgetSchemaNamesBySchemaUids. - Changed:
getSeedsBySchemaNameskips the schema join and takes an optional 4th argument,{ uidPrefix }. - Changed: in Node,
NodeQueryClientis now a real TanStack Query client (@tanstack/query-coreis a new dependency). Identical requests in flight at the same time share one fetch. Nothing is kept once a request settles unless you passstaleTime, there are no retries, and cache keys are scoped to the EAS endpoint.
@seedprotocol/query
- Added:
queryBySchema(schema, { uidPrefix, limit })finds a schema's seeds by UID prefix without listing the schema, andnormalizeUidPrefixis exported. See Query overview. - Fixed: a cached collection now asks EAS what changed since the last check and re-assembles only those seeds. Before, edits to existing seeds were missed, and under steady traffic the cache never expired.
- Changed: Arweave bodies are fetched in parallel and cached by transaction ID.
- Changed: cached records are deep-frozen outside production and typed read-only (
SeedRecordfields arereadonly). Mutating a returned record now throws in development.QueryCacheConfig.freezeRecordsoverrides the default. - Changed: EAS requests that don't depend on each other run in parallel, so reads are faster.
@seedprotocol/feed
- Fixed: feed defaults no longer modify records in the query cache.
@seedprotocol/vite
- Changed:
viem,isows,kerium,utilium,memiumandreadable-streamare prebundled only when your app can resolve them, which removes "Failed to resolve dependency" warnings.
@seedprotocol/sdk
- Fixed: a local ID that happened to start with
0xwas taken for an EAS UID, and publishing a list that contained it failed with "invalid BytesLike value". UIDs are now recognized by their full shape, and new local IDs never start with0x. - Fixed:
Item.unpublishcould stall for several seconds loading a module lazily.
All packages
- Releases are published from CI with npm trusted publishing. Builds of the main branch are published as prereleases under the
nextdist-tag (versions like0.7.3-next.613.g79a8dfd).latestonly moves on a tagged release.
0.7.1
Released 2026-10-09.
@seedprotocol/sdk
- Added:
seed:property/<name>placeholders in Html. Write<img src="seed:property/featureImage">and the publish rewrites it to the gateway URL of that property's upload, instead of embedding the image as adata:URI. A placeholder naming a missing or empty property fails the publish before anything is uploaded. - Added:
Item.peekReadyandItemProperty.peekReadyreturn a cached instance only if it can be read right now, without taking a cache hold. - Changed: a publish now carries every draft reachable through relation and list properties, each with all its properties. Before, related drafts were published one level deep with only some of their properties, so a published relation could point at a draft that was never published.
- Fixed: images embedded in Html no longer overwrite the Html seed's reference, and their
srcis rewritten in place without re-serializing the rest of the Html. - Changed: the optional
@tanstack/react-querypeer range is now^5.102.0.
@seedprotocol/react
- Added:
OPFSFilesManagercan delete folders. - Changed:
useItemanduseItemPropertyload through React Query and return a ready cached instance on the first render, so a row thatuseItemsjust returned renders filled in instead of empty for a tick. - Changed:
useItemre-renders when one of the item's property values changes. - Fixed: changing the ID passed to
useItemoruseItemPropertyno longer shows the previous item while the new one loads.
@seedprotocol/publish
- Fixed: a direct-to-EAS publish attests every list member's seed UID.
@seedprotocol/vite
- Fixed: the dev server on Vite 8.2 and later with
includeNodePolyfills. Every prebundled dependency declared the polyfill banner twice and failed to parse. Your ownoptimizeDepsincludeandexcludeentries are no longer duplicated either. - Changed: with node polyfills on,
globalresolves toglobalThis.
@seedprotocol/feed-hyper and @seedprotocol/gateway-hyper
- Added: the
seed-feedcommand (publish,seed,serve) ships with@seedprotocol/feed-hyper, andseed-gateway(serve,connect) with@seedprotocol/gateway-hyper. See Hyper distribution and Gateway Hyper.
0.7.0
Released 2026-10-08.
@seedprotocol/sdk
- Added: tabs of the same app now coordinate (
multiTab: 'coordinate', the default, set onclient.init). One leader tab runs automatic EAS sync and the Arweave finalize worker. Sync results, address changes and file changes are relayed to the other tabs. Migrations and init writes take turns, and a tab can open the database while another tab is using it. PassmultiTab: 'off'for the old behavior. See Multi-tab behavior. - Added: each item records its model, and models are resolved by schema and name.
Item.allandgetItemsDataacceptschemaNameormodelFileId. - Breaking: model names are only unique within a schema. Name-only lookups (
Model.getByName,Item.createwithout a schema, and others) throwAmbiguousModelErrorwhen two loaded schemas define the same model name. Pass the schema name. - Breaking: on first start after upgrading, if items saved before this release have no recorded model and their model name exists in more than one schema, the local item data is cleared once. Schemas and models are kept, and published items come back on the next EAS sync. Unpublished drafts in that situation are lost.
- Added: version revocations are recorded locally. Revoked versions are skipped when picking a seed's latest version, and property revocations and replacements reach stored data.
- Changed: republishing an unpublished (revoked) item creates a new seed instead of adding a version to the revoked one.
- Changed: publishing an item that refers to an unpublished item fails before anything is sent, with
RelatedItemUnpublishedError(coderelated_item_unpublished). - Changed: unchanged schema files aren't re-applied on each
client.init. - Fixed:
ModelProperty.save()looped until it ran out of memory. - Fixed: many schema and model fixes: importing a schema no longer takes over another schema's model with the same name,
Schema.destroywaits for in-flight writes, propertyrequiredand storage settings persist, and Image properties acceptBlobvalues. - Fixed: EAS sync now syncs versions of seeds that were already stored, and batches its reads and inserts under SQLite's variable limit.
- Fixed: OPFS file locks are released when a worker write fails.
@seedprotocol/react
- Breaking:
OPFSFilesManagerwas redesigned as a folder browser with thumbnails, search, sort, grid and list views and in-component dialogs. Itsthemenow defaults to'system'(it was'dark'), andonBeforeDeleteruns after the user confirms instead of before. See Files and OPFS. - Added:
onNotifyanddeleteActiononOPFSFilesManager. - Added:
SeedClientGatereports init failures throughonInitError(for exampleFileSystemLockedError, codeOPFS_LOCKED). Before, a failed init left the loading UI up. - Changed:
useItemsacceptsschemaName. - Fixed: the destroy hooks report
isLoadinganderrorcorrectly,useModelPropertiesfollows the model, anduseModelPropertyretries a lookup that found nothing. - Fixed: the
@seedprotocol/sdkpeer range is^0.6.0. It was^0.4.0, which reported a conflict with SDK 0.6.
@seedprotocol/publish
- Fixed: each
multiPublishrequest is matched to its own receipt events, so a publish that sends several requests in one transaction records all of them. Direct-to-EAS publishes record each request's own seed. - Fixed: images embedded in a related draft's Html are published with it.
- Fixed: an in-progress publish resumes in one tab only.
@seedprotocol/eas
- Fixed: revoked attestations are skipped when picking a property's canonical value, and version and property attestations include
revocationTime. - Fixed: query cache keys include the query's inputs.
@seedprotocol/query
- Fixed: seeds whose versions are all revoked are left out, from both the local and the remote source.
0.6.10
Released 2026-10-05.
@seedprotocol/publish
- Breaking:
waitForPublishReceiptthrowsPublishTransactionRevertedError(withtransactionHashandreceipt) on a reverted transaction. It used to return the receipt, so callers treated a revert as success. Functions that return its receipt, such asattestPublishAuthorization, now throw too. - Breaking:
ensureExecutorModuleInstalledno longer callsinstallModuleon thirdweb ModularCore accounts (that path could never work). It returns{ status: 'skipped' | 'already-installed' }or{ status: 'installed', eas, transactionHash }.PublishConfig.modularAccountModuleDatais no longer used. - Added:
readFactorySeedExecutor(factory?)reads the executor the factory's router extension trusts, so you can compare it withmodularAccountModuleContractat startup. - Added:
seedPaymaster(client, chain, bundlerUrl?)in@seedprotocol/publish/thirdweb, for smart wallets the SDK doesn't build. - Fixed: sponsored ManagedAccount UserOps ran out of gas on large publishes. The call gas limit now gets 20% headroom before the paymaster signs.
- Fixed: turning on automation could fail with "The Seed executor is not installed" right after a successful install, because of a lagging RPC read. The install is now confirmed from the receipt's events, and a failed install reports the revert reason.
0.6.9
Released 2026-10-03. This is the minimum version for the contracts switched on 2026-10-03 (Deployments, upgrade steps).
@seedprotocol/publish
- Breaking:
encodeMultiPublishencodes the newSeedProtocolExtensionstruct (selector0x2a29fadc), in which a property names its target bypublishIndex, its position in the batch.encodeExecutorMultiPublishuses the same field order and works only with the new executor. - Breaking: removed
encodeMultiPublishInteger,transformPayloadToIntegerIds, theRequestWithStringIdsandRequestWithIntegerIdstypes, and theuseIntegerLocalIdsoption. No deployed contract accepts them. - Changed: both encoders throw on a duplicate local ID or a missing or unknown
publishLocalId, instead of dropping the reference or pointing it at the first request.
0.6.8
Released 2026-10-03.
@seedprotocol/publish
- Added: the publish chain is configurable.
PublishConfigacceptseasContractAddress,schemaRegistryAddressandmanagedAccountFactoryAddress, and known chains fill them in fromconfig.chain. See Deploying to a new chain. - Added:
verifyPublishChainchecks before the first publish or revoke that the RPC reports the configured chain and that EAS, the SchemaRegistry and any configured factory or executor have code there. It throwsPublishChainConfigError. - Added:
PublishConfig.thirdweb(bundlerUrl,sponsorGas,modularWalletMode) configures the in-app wallets, andseedTwinConfigreads a local twin'stwin.json. - Added: every publish route simulates
multiPublishbefore sending. A call that would revert throwsPUBLISH_PREFLIGHT_FAILEDwith the decoded reason, and a failed UserOp reportsUSEROP_FAILED_NO_REASONorUSEROP_REVERTED. - Changed: client changes for the new contracts. An interactive publish is a UserOp from the ManagedAccount calling
multiPublishon itself.setEasis sent only for older accounts that report no EAS. Session keys never get EAS as an approved target. - Changed: revocation is owner-only, through EAS. Automation session keys can't revoke, and can't register missing schemas: register them once with the owner wallet through
ensureEasSchemasForItem. - Fixed: list relations are always attested under one EAS name ending in
_ids(for examplebytes32[] staff_identity_ids). Already-published data is unchanged. - Fixed: a property whose EAS schema can't be found fails validation with
publish_schema_not_foundinstead of being attested under the Version schema. - Fixed: turning on automation works on older ManagedAccounts that use the Router design, and failed UserOps on the permissionless (EIP-7702) sender throw instead of reporting success.
- Fixed: a Node server can publish without a browser thirdweb client.
@seedprotocol/sdk
- Added:
SeedConfig.eas(chainId,indexerUrl) and theEAS_CHAIN_IDenvironment variable choose which chain's EAS the SDK reads. - Added: the local database records its EAS chain and refuses to sync, publish or revoke against a different one. Databases from earlier versions are treated as OP Sepolia.
- Changed: EAS sync waits up to 30 seconds for
initPublishto set the chain, unless the chain is already known from config, the environment or the database. - Changed:
VERSION_SCHEMA_UID_OPTIMISM_SEPOLIAis renamedVERSION_SCHEMA_UID. The old name still works and is deprecated. - Fixed: property data types are case-insensitive, and failed saves in Node are reported instead of hanging.
@seedprotocol/eas
- Added:
EAS_CHAIN_DEPLOYMENTSlists EAS and SchemaRegistry addresses and easscan URLs for known chains.
@seedprotocol/query
- Fixed:
parseEasRelationPropertyNameturnsstaff_identity_idsintostaff(wasstaffs).
@seedprotocol/feed
- Changed: item links point to the configured chain's EAS explorer.
0.6.7
Released 2026-09-29.
@seedprotocol/sdk
- Added:
ensureImageLocaldownloads a missing image and creates only the size variants that are needed. - Added: an Arweave gateway circuit breaker. Gateways that fail a probe or a media request are skipped for a cooldown.
- Fixed: Node built-ins no longer end up in browser bundles, and Rollup builds of the SDK work.
@seedprotocol/react
- Added:
useEnsureLocalImage.SeedImageshows the gateway URL until the image is stored locally.
@seedprotocol/publish
- Added: PublishedBy sidecar attestations over a publish batch:
attestPublishedBy,revokePublishedBy,ensurePublishedBySchemaand helpers to read them.
@seedprotocol/vite
- Fixed: plugin bugs.
0.6.4 to 0.6.6
Released 2026-09-25 (three releases the same day).
@seedprotocol/publish (0.6.4)
- Fixed: the first publish failed when the ManagedAccount wasn't deployed yet. It's now deployed as part of that publish.
@seedprotocol/sdk (0.6.5)
- Fixed: a failed
client.initwas treated as ready, so the app ran against a missing database. Init now stays failed, overlappinginitcalls share one run, and save failures reject immediately. - Changed:
@zenfs/core2.5.6 and@zenfs/dom1.2.9 are optional peer dependencies.
@seedprotocol/sdk (0.6.6)
- Fixed: database init hung silently in Chrome profiles that kill the SQLite module worker. The worker now starts in a way those profiles accept, and a worker that dies fails init with an error instead of hanging.
0.6.3
Released 2026-09-25.
@seedprotocol/mapping
- Added:
deriveandassembleresolve jobs,useFieldMapper.setDeriveandsetAssembleBlocks, and thetransformOptionsandrenderRowExpansionprops onFieldMapper.applyMappingskips derive and assemble edges; your app applies them. - Fixed: a later edit could bring back a mapping your app had removed.
@seedprotocol/sdk
- Fixed: Item proxies made React 19 abort some updates.
0.6.2
Released 2026-09-24.
@seedprotocol/publish
- Added: publish cost estimates:
estimatePublishCostandwatchPublishCost. See Costs and who pays.
@seedprotocol/sdk
- Added:
summarizePublishWork, and ownership helpers such asisLocalUnsealedDraftandclaimUnpublishedDrafts. - Fixed: item ownership checks.
0.6.1
Released 2026-09-23.
@seedprotocol/mapping
- Changed: relation lookups are stored on the mapping edge as
FieldMapping.lookup.entries({ value, ref }[]).MappingDocument.lookupsstill works and is deprecated. - Added:
renderLookuponFieldMapperfor your own picker, anduseFieldMapper.setLookupEntries.
@seedprotocol/publish
- Fixed: seed UIDs after a publish are read from the transaction receipt and don't depend on a separate created-attestation lookup.
0.6.0
Released 2026-09-22. The other packages only changed version.
@seedprotocol/mapping
- Breaking:
FieldMapperand the other UI exports moved to@seedprotocol/mapping/react. The main entry no longer imports React, so it loads in Node without it. - Breaking:
FieldMapper'slayoutdefaults to'rows'(was'wires').layout="wires",theme="unstyled",showPreviewandconnectionColorsstill work and are deprecated. - Added: the headless
useFieldMapperhook, the rows layout, the theming contract (--sfm-*tokens,theme,classNames,slots),renderRowAccessoryandSourceNode.subtitle. - Added:
resolve: 'extract' | 'file' | 'lookup'jobs,applyMappingAsync,classifyUrl, and one source mapped to several properties.
See FieldMapper UI.
0.5.3
Released 2026-09-17.
@seedprotocol/mapping (first release on npm)
- Added: the headless mapping core, Markdown and RSS adapters, and the
FieldMapperUI. See Mapping overview.
@seedprotocol/publish
- Added: publish automation: session keys hosted on the ManagedAccount,
enrollPublishAutomationandrevokePublishAutomation, and the PublishAuthorization sidecar. See Publish unattended with a session key. - Added: domain ownership tooling: DNS TXT challenges, RDAP snapshots, and
attestDomainOwnership/revokeDomainOwnership. See Domain ownership.
@seedprotocol/eas
- Added: helpers to read and assess PublishAuthorization and DomainOwnership attestations.
@seedprotocol/sdk
- Added:
client.removeLocalCopiesForAddressesdeletes the local copies of a publisher's on-chain items (for example on wallet disconnect). Drafts stay, and EAS sync brings the rest back on reconnect.
0.5.2
Released 2026-09-06.
@seedprotocol/query (first release on npm)
- Added: reads of published Seed data as canonical JSON, with local, remote and auto sources, collection and item caching, and a Version changelog on
getSeed. See Query overview.
@seedprotocol/feed
- Changed: reads through
@seedprotocol/query, and emits media URLs for Image relations instead of EAS links.
@seedprotocol/arweave
- Added: a configured
proxyBaseUrlis preferred as the gateway path. EAS explorer URLs are recognized, and RSS image references resolve.
@seedprotocol/gateway-hyper
- Added:
createGatewayProxy, to proxy the gateway from your own app server.
@seedprotocol/feed-hyper (first release on npm)
- Added: publish Seed feeds into a Hyperdrive, seed them over Hyperswarm and serve them over local HTTP.
0.5.1
Released 2026-08-29. The package CHANGELOGs list most of these changes under 0.5.0, which was never published to npm.
New packages: @seedprotocol/eas (EAS GraphQL client), @seedprotocol/arweave (Arweave gateway client and gateway resolution), @seedprotocol/vite (the Vite plugin, extracted from @seedprotocol/sdk/vite) and @seedprotocol/gateway-hyper.
@seedprotocol/sdk
- Breaking:
drizzle-ormandxstateare peer dependencies. Install them in your app. - Changed: EAS and Arweave code moved to
@seedprotocol/easand@seedprotocol/arweave. The SDK re-exports them, so its public API is unchanged.@seedprotocol/sdk/vitere-exports the plugin from@seedprotocol/vite. - Changed:
ethers,globalsandbetter-sqlite3are no longer runtime dependencies. - Added: Seed gateway endpoint resolution for HTTPS and sidecar paths,
VITE_ARWEAVE_HOST, andDEFAULT_SEED_FEED_HYPER_KEY.
@seedprotocol/publish
- Breaking: thirdweb is optional. The main entry no longer exports thirdweb helpers or
ConnectButton; import them from@seedprotocol/publish/thirdweb. The main entry'sPublishProviderno longer wrapsThirdwebProvider. - Breaking:
thirdwebClientIdis optional, andrpcUrlis required when it's unset. - Breaking:
SeedSignerno longer hassendTransaction. UseSeedTxSenderor aPublishWallet({ signer, txSender }), and wrap thirdweb accounts withfromThirdwebAccount. - Added:
PublishWallet,fromEthersWallet,fromEip1193Provider,useSeedWallet,ensureWalletThenPublish, andcreatePermissionlessTxSenderfor gasless EIP-7702 publishing. - Fixed: in-app auth tokens sync when a managed wallet connects.
@seedprotocol/feed
- Changed: depends on
@seedprotocol/easand@seedprotocol/arweaveinstead of the full SDK. Documents usefeedUrlfor their self-links, and the Arweave gateway comes from the environment.
Related
Account abstraction: bundler and paymaster
How Seed Protocol sends attestations through ERC-4337: what the bundler and paymaster do, the thirdweb defaults, the managed, EIP-7702 and EOA account modes, the bundlerUrl, paymasterUrl and sponsorGas settings, and how to run without sponsorship.
Upgrading
Upgrade steps for each breaking change from @seedprotocol 0.4 to 0.8.0, with before and after code.