@seedprotocol/arweave
Reference for @seedprotocol/arweave, the low-level Arweave gateway client: gateway selection and fallback, environment overrides, proxy and Hyper transports, raw reads, upload status checks, media reference helpers, Html link helpers and constants.
@seedprotocol/arweave is how Seed packages reach Arweave, where published files and long-form content live. It picks a gateway (an HTTP server that serves Arweave data), falls back to others when one is down, reads transaction data and tags, and checks whether an upload has landed. The SDK, @seedprotocol/query and @seedprotocol/feed use it, and the SDK re-exports most of it. Use it directly in a script or server that reads Arweave content without the SDK, or to inspect what the SDK resolved.
import { BaseArweaveClient, fetchArweaveRawTextAcrossGateways } from '@seedprotocol/arweave'
import { registerNodeArweavePlatform } from '@seedprotocol/arweave/node'It doesn't upload: building, signing and sending uploads is @seedprotocol/publish's job. Permanence explains what's stored on Arweave and why.
Install
npm install @seedprotocol/arweaveIt has no Seed dependencies and no peer dependencies. The SDK and @seedprotocol/query install it for you.
Entry points
| Import | What it has |
|---|---|
@seedprotocol/arweave | BaseArweaveClient, the fetch-based client DefaultArweaveClient, gateway resolution and fallback, raw-read and upload-status helpers, public gateway URLs, media reference helpers, Html link helpers, constants and types |
@seedprotocol/arweave/node | registerNodeArweavePlatform and the Node client NodeArweaveClient, which extends DefaultArweaveClient |
There is no /react entry. DefaultArweaveClient works in browsers and Node; register it with BaseArweaveClient.configure(new DefaultArweaveClient()). The SDK registers it during client.init. It loads the arweave npm client only on first use, for the data fallback and createTransaction. The URL and gateway helpers that don't go through the registered client (getRawUrl, fetchArweaveRawTextAcrossGateways, resolveSeedGatewayEndpoints, …) work anywhere fetch does.
The default gateway and its fallbacks
Reads go to a primary gateway, arweave.net by default, and fall back through a list of public gateways:
const DEFAULT_ARWEAVE_HOST = 'arweave.net'
const DEFAULT_ARWEAVE_GATEWAYS = ['arweave.net', 'zigza.xyz', 'ar.seedprotocol.io']A primary you set goes first, followed by each default not already in the list: with example.org as the primary, the order is example.org, arweave.net, zigza.xyz, ar.seedprotocol.io. Seed's own gateway is last; an app that wants it first sets arweaveDomain: 'ar.seedprotocol.io' (here through registerNodeArweavePlatform, or in the SDK's config, see Client and configuration). Under the SDK, arweaveDomain is also the host publish writes into Html links (Links in published content).
Environment variables
| Variable | Effect |
|---|---|
ARWEAVE_READ_GATEWAYS, NEXT_PUBLIC_ARWEAVE_READ_GATEWAYS | Comma-separated hosts that replace DEFAULT_ARWEAVE_GATEWAYS as the fallback list |
NEXT_PUBLIC_ARWEAVE_HOST, ARWEAVE_HOST, VITE_ARWEAVE_HOST | Primary gateway host (first one set wins; https:// is stripped, http:// keeps plain HTTP). It overrides registerNodeArweavePlatform's arweaveDomain, but not BaseArweaveClient.setHost, and stops applying once a probe has picked a host |
They're read from process.env only. In a browser build, VITE_ARWEAVE_HOST has no effect unless your bundler defines process.env. Under the SDK, client.init sets the host from arweaveDomain with setHost, so the host variables don't apply after init.
registerNodeArweavePlatform
Registers the Node client and sets the primary gateway.
function registerNodeArweavePlatform(options?: { arweaveDomain?: string }): void| Name | Type | Default | Description |
|---|---|---|---|
options.arweaveDomain | string | 'arweave.net' | Primary gateway host. Plain hosts use HTTPS; prefix http:// for a plain HTTP gateway such as http://localhost:1984 |
Importing @seedprotocol/arweave/node registers the client as a side effect; calling the function also sets the primary. It doesn't lock the host, so the environment variables and the read probe can still change it. Call it before setHost, because it resets a host set earlier. Without a registered client, the transaction methods throw ArweaveClient not configured.
import { registerNodeArweavePlatform } from '@seedprotocol/arweave/node'
import { BaseArweaveClient, fetchArweaveRawTextAcrossGateways } from '@seedprotocol/arweave'
registerNodeArweavePlatform({ arweaveDomain: 'arweave.net' })
const txId = process.argv[2]!
const tags = await BaseArweaveClient.getTransactionTags(txId)
console.log(tags.find((t) => t.name === 'Content-Type')?.value)
const text = await fetchArweaveRawTextAcrossGateways(txId)
console.log(text?.slice(0, 200))BaseArweaveClient
The process-wide gateway setting and the registered client, as static methods. ArweaveClient is the same class.
Gateway settings
| Method | Returns | Description |
|---|---|---|
setHost(host) | void | Sets the gateway and locks it: the read probe won't change it and the host environment variables no longer apply. 'arweave.net', 'https://arweave.net' or 'http://localhost:1984'; a path prefix such as 'app.example.com/api/seed-gateway' is allowed |
setPreferredReadGateway(host) | void | Sets the gateway without locking it |
isReadGatewayLocked() | boolean | True after setHost |
getHost() | string | Host without scheme, possibly with port and path, e.g. arweave.net |
getProtocol() | 'http' | 'https' | |
getBaseUrl() | string | e.g. https://arweave.net |
getRawUrl(txId) | string | {base}/raw/{txId}: the data as uploaded |
getStatusUrl(txId) | string | {base}/{txId} |
getEndpoint() | string | {base}/graphql |
getGatewayPath() | string | The path prefix, or '' |
getArweaveSdkHost(), getArweaveJsApiConfig() | The host and { host, port, protocol } to pass to the arweave npm client's init(). Path prefixes are dropped | |
resolveGateway() | { protocol, host } | The effective gateway, including the environment override |
isConfigured() | boolean | True once something (the SDK, registerNodeArweavePlatform or your app) has registered a client. The transaction methods below throw until then |
Transaction methods
These go through the registered client.
| Method | Signature | Description |
|---|---|---|
getTransactionTags | (txId: string) => Promise<TransactionTag[]> | Tags through the gateway's GraphQL API. [] on error, after 30 seconds without an answer, or when not found |
getTransactionStatus | (txId: string) => Promise<TransactionStatus> | Whether a gateway has the transaction: fetchTransactionStatusAcrossGateways with its defaults. confirmed is always null |
getTransactionData | (txId: string, options?: { decode?: boolean; string?: boolean }) => Promise<Uint8Array | string> | The data. It first tries /raw/{txId} on each preferred read gateway in turn (10 seconds each), unless decode is false. When none serves it, it fetches getRawUrl with a path-prefixed gateway, throwing on a non-2xx status, and otherwise downloads through the arweave npm client. Decoded data goes through the content read transform when one is registered |
downloadFiles | (params: { transactionIds: string[]; excludedTransactions?: Set<string> }) => Promise<DownloadResult[]> | Fetches each /raw/{id} in turn. Each result has transactionId, success, and contentType and data or error |
createTransaction | (data: string | Uint8Array, options?: { tags?: TransactionTag[] }) => Promise<ArweaveTransaction> | An unsigned arweave transaction with the tags added. It doesn't sign or post it |
getArweaveClient | () => GraphQLClient | A graphql-request client for getEndpoint(). With DefaultArweaveClient and NodeArweaveClient, each request aborts after 30 seconds (ARWEAVE_GRAPHQL_TIMEOUT_MS; Arweave GraphQL request timed out after 30000 ms), or when the caller's signal aborts |
getTransactionTagsByIds
The tags of many transactions, in batched GraphQL requests, keyed by transaction ID. The SDK uses it to check downloads against their Content-SHA-256 tag.
function getTransactionTagsByIds(
transactionIds: readonly string[],
options?: { client?: GraphQLClient; batchSize?: number; onError?: (error: unknown) => void },
): Promise<Map<string, TransactionTag[]>>| Name | Type | Default | Description |
|---|---|---|---|
transactionIds | string[] | IDs to look up. Duplicates and blanks are dropped | |
options.client | GraphQLClient | BaseArweaveClient.getArweaveClient() | The gateway's GraphQL client |
options.batchSize | number | 100 (TRANSACTION_TAGS_BATCH_SIZE, also the maximum) | IDs per request |
options.onError | (error: unknown) => void | Called for a batch whose request failed |
An ID the gateway doesn't know yet, such as a fresh upload that isn't indexed, is missing from the map. A failed batch is skipped, not thrown. The query is exported as GET_TRANSACTIONS_TAGS_BY_IDS.
When no preferred read gateway serves the data and the gateway has no path prefix, getTransactionData downloads through the arweave client's chunk API, which fails for bundled data items (how most uploads are stored today). It then logs [chunk] Failed to fetch chunk … and can return an empty result. Set preferred read gateways, or fetch getRawUrl(txId) or use fetchArweaveRawTextAcrossGateways instead.
One copy of the package
The registered client and the gateway settings live in module state, and assertSingleArweaveModule() throws DuplicateArweaveModuleError when the client was registered through another copy of this package; @seedprotocol/query calls it before every read. When two copies happen and how to fix it is in query's troubleshooting.
Gateway probing and fallback
A probe is GET {base}/info; a gateway is healthy when that returns a 2xx JSON object within 5 seconds, whether or not the caller passed a signal. A host that fails a probe is skipped for 60 seconds (a circuit breaker), then tried again. A probe the caller aborts resolves false and doesn't count as a failure.
ensureReadGatewaySelected
Probes the primary and then each fallback in order, and switches the gateway to the first healthy one.
function ensureReadGatewaySelected(signal?: AbortSignal): Promise<string>It returns the selected host. The result is cached for 3 minutes (1 minute when nothing was healthy, in which case the last healthy host is kept). When signal aborts before a gateway passes, nothing is cached and it returns the last healthy host, else the current one. It does nothing while the host is locked with setHost.
fetchArweaveRawTextAcrossGateways
Reads a transaction's data as text, trying gateways until one returns a non-empty body.
function fetchArweaveRawTextAcrossGateways(
transactionId: string,
options?: { fallbackHosts?: readonly string[]; timeoutMs?: number },
): Promise<string | undefined>| Name | Type | Default | Description |
|---|---|---|---|
transactionId | string | The Arweave transaction or data item ID | |
options.fallbackHosts | string[] | the primary and fallback list | Hosts to try when no endpoints were resolved |
options.timeoutMs | number | 10000 (DEFAULT_ARWEAVE_RAW_READ_TIMEOUT_MS) | Per-gateway timeout |
It tries the preferred read gateways first, then runs ensureReadGatewaySelected (unless a sidecar or proxy is active) and tries the resolved and fallback gateways. Each body goes through the content read transform when one is registered; a gateway whose body can't be read, or whose transform throws, is skipped. The bytes are decoded as UTF-8, so a binary body comes back with U+FFFD in place of invalid sequences. It returns undefined when none had it. It never throws.
fetchArweaveRawBytesAcrossGateways
The same read as fetchArweaveRawTextAcrossGateways, with the same options, gateway order and errors, returning the body as bytes without decoding it.
function fetchArweaveRawBytesAcrossGateways(
transactionId: string,
options?: { fallbackHosts?: readonly string[]; timeoutMs?: number },
): Promise<Uint8Array | undefined>The bytes are the transaction's data only while no content read transform is registered. With one registered it returns the transform's output, and there's no option to skip the transform, as getTransactionData's decode: false does. @seedprotocol/archive's exportArchive downloads Arweave content through it.
fetchTransactionStatusAcrossGateways
Checks whether a gateway has a transaction, trying gateways in order. BaseArweaveClient.getTransactionStatus calls it with the defaults.
function fetchTransactionStatusAcrossGateways(
transactionId: string,
options?: { timeoutMs?: number; totalTimeoutMs?: number; baseUrls?: readonly string[] },
): Promise<TransactionStatus>| Name | Type | Default | Description |
|---|---|---|---|
transactionId | string | The Arweave transaction or data item ID | |
options.timeoutMs | number | 10000 (DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS) | Per-gateway timeout |
options.totalTimeoutMs | number | 20000 (DEFAULT_TRANSACTION_STATUS_TOTAL_TIMEOUT_MS) | Budget across all gateways. The first gateway is always asked; a later one only while the budget left is at least timeoutMs or MIN_TRANSACTION_STATUS_ATTEMPT_MS (1000), whichever is smaller. The last one gets only what's left |
options.baseUrls | string[] | getArweaveReadBaseUrls(); while a sidecar or proxy is active, only the preferred read gateways and the sidecar or proxy | Base URLs to try, in order. A data item the sidecar or proxy just accepted isn't on the public gateways yet, and their 404 would read as missing |
Each attempt is GET {base}/{txId}, without downloading the body. Gateways whose circuit is open are skipped, unless every one is open, in which case all are tried. The first gateway to answer with a 2xx to 4xx status decides the result: any 2xx comes back as 200 (present), and a 404 is returned without asking the other gateways. A timeout, network error, 5xx or 429 counts as a failure against that gateway's circuit and moves on to the next. When no gateway answers, status is 504 if the last attempt timed out and otherwise that attempt's status (500 for a network error, or when there was nothing to try). confirmed is always null. It never throws.
Other read helpers
| Function | Signature | Description |
|---|---|---|
fetchArweaveRawFromBaseUrl | (baseUrl: string, txId: string, options?: { timeoutMs?: number }) => Promise<Response | undefined> | GET {baseUrl}/raw/{txId} with a timeout; undefined on error or non-2xx |
getArweaveReadBaseUrls | (options?: { fallbackHosts? }) => string[] | The ordered base URLs reads try: preferred gateways, then the resolved ones. Doesn't probe |
getResolvedArweaveReadBaseUrls | (options?: { fallbackHosts? }) => string[] | The same without the preferred gateways |
ensureArweaveReadGatewayForFallback | () => Promise<void> | Runs the probe unless a sidecar or proxy is active. Never throws |
probeGateway | (baseUrl: string, options?: ProbeGatewayOptions) => Promise<boolean> | One /info probe. After timeoutMs (default 5000) it fails. With recordCircuit (default true) it returns false without a request while the host's circuit is open, and records the result; with false it probes an open host and records nothing. A probe that signal aborts resolves false and records nothing. An AbortSignal passed as the second argument, which TypeScript refuses, rejects with a TypeError before any request; pass { signal } |
selectFirstHealthyReadGateway | (hosts: string[], protocol?, signal?) => Promise<string | null> | The first host whose probe passes |
isReadGatewayKnownHealthy, getLastHealthyReadGatewayHost, invalidateReadGatewayCache | Inspect or reset the probe result | |
getArweaveReadGatewayHostsForPrimary | (primary: string) => string[] | Primary first, then each host of the env list, or of DEFAULT_ARWEAVE_GATEWAYS, not already in it |
getDefaultArweaveReadGatewayHostsOrdered | () => string[] | The env list, or DEFAULT_ARWEAVE_GATEWAYS |
mergePrimaryHostWithDefaults | (primary: string, defaults: readonly string[]) => string[] | Deduplicated, primary first |
isKnownArweaveGatewayHostname | (hostname: string) => boolean | True for a host in KNOWN_ARWEAVE_GATEWAYS or a subdomain of one, and for *.arweave.net and *.ar-io.net |
resolveArweaveHostFromEnv | () => string | undefined | The host from the environment variables |
The circuit breaker is exported too (isGatewayHostCircuitOpen, getGatewayHostCircuitState, listOpenGatewayCircuitHosts, recordGatewayHostSuccess, recordGatewayHostFailure, beginGatewayHostHalfOpenProbe, cancelGatewayHostHalfOpenProbe (ends a half-open probe without a result, so the next caller can probe the host), normalizeGatewayHostKey, configureGatewayCircuitBreaker({ failureThreshold?, openDurationMs? })). The reset…ForTests functions are for tests only.
Experimental: content read transform
Groundwork for private publishing. A transform registered with setArweaveContentReadTransform sees content after it's fetched and before it's used, for example to decrypt it. Nothing in the Seed packages registers one, and the signature may change.
type ArweaveContent = { bytes: Uint8Array; contentType?: string }
type ArweaveContentReadContext = { transactionId: string }
type ArweaveContentReadTransform = (
content: ArweaveContent,
context: ArweaveContentReadContext,
) => ArweaveContent | Promise<ArweaveContent>
function setArweaveContentReadTransform(transform: ArweaveContentReadTransform | undefined): void
function hasArweaveContentReadTransform(): boolean
function transformArweaveContent(content: ArweaveContent, context: ArweaveContentReadContext): Promise<ArweaveContent>
function readArweaveContentResponse(response: Response, context: ArweaveContentReadContext): Promise<ArweaveContent>
function getResponseMediaType(response: Response): string | undefinedReads that go through it: fetchArweaveRawTextAcrossGateways, fetchArweaveRawBytesAcrossGateways, BaseArweaveClient.getTransactionData (not with decode: false) and @seedprotocol/query's rich-text hydration (which then re-hosts the links in each body). Gateway URLs handed straight to the browser, such as SeedImage's progressive gatewayHref, media links and feed image URLs, don't, and neither does the SDK's browser download worker.
setPreferredArweaveReadBaseUrls
Gateways tried before all others for /raw reads, typically the one your app uploads through, which has a new upload before the public gateways do.
function setPreferredArweaveReadBaseUrls(urls: readonly string[]): void
function getPreferredArweaveReadBaseUrls(): string[]Each entry is a base URL with scheme and any path, e.g. https://app.example.com/api/seed-gateway. A relative path such as /api/seed-gateway resolves against the page origin and is dropped where there is none (Node). Pass [] to clear. It doesn't change uploads, GraphQL or BaseArweaveClient's host. BaseArweaveClient.getTransactionData tries these gateways too. The SDK sets it from config.gateway.preferredReadBaseUrls.
Proxy and Hyper transports
Besides public HTTP gateways, an upload API and gateway can be reached over the Hyper network, either through a local sidecar (seed-gateway connect <operator key>, listening on http://127.0.0.1:1984) or through your app server (a proxy route built with createGatewayProxy from @seedprotocol/gateway-hyper). Gateway Hyper explains the setup.
resolveSeedGatewayEndpoints
Works out which gateway and upload API to use for a transport setting, probing the proxy or sidecar first.
function resolveSeedGatewayEndpoints(
config: SeedGatewayConfig,
options?: { signal?: AbortSignal; timeoutMs?: number; forceSidecarProbe?: boolean; origin?: string },
): Promise<ResolvedSeedGatewayEndpoints>transport | What it picks |
|---|---|
'http-gateway' (default) | The proxyBaseUrl if set (throws Gateway HTTP proxy not reachable if its probe fails), otherwise arweaveDomain (default arweave.net; an empty string means the default) |
'hyper' | A healthy proxyBaseUrl, otherwise the local sidecar. Throws Gateway Hyper sidecar not reachable at http://127.0.0.1:1984 if the sidecar doesn't answer |
'hybrid' | A healthy proxy, then a healthy sidecar, then arweaveDomain over HTTP (activePath: 'hybrid-fallback-http') |
'hyper' probes the sidecar from wherever the code runs, so a web app on a public origin sends every visitor's browser a request to 127.0.0.1:1984, where most visitors run no sidecar. In a browser, 'hybrid' tries the sidecar only when the page's origin is loopback (localhost, 127.0.0.1, [::1] or *.localhost), or with hyper.probeSidecarFromBrowser: true, for a page that is meant to reach it, such as an Electron or desktop shell. hyper.probeSidecar: false skips the sidecar probe, and hyper.probeProxy turns the proxy's /info check on or off on its own (default: probeSidecar's value, else true); without that check, a set proxyBaseUrl is used even when it's down. A healthy probe result is cached for 3 minutes and a failed one for 60 seconds; forceSidecarProbe: true or invalidateSidecarProbeCache() clears them. timeoutMs is one budget for all of the resolve's probes (each probe still times out after 5 seconds on its own). A probe cut short by it or by signal counts as unreachable for that resolve only: it isn't cached or recorded in the circuit breaker. client.init passes no timeoutMs. A relative proxyBaseUrl needs options.origin outside a browser.
With a sidecar or proxy active, reads try it first and then the public gateway and the default list, so /raw reads and re-hosted links still work when it's down. Status checks ask only it and the preferred read gateways.
ResolvedSeedGatewayEndpoints:
| Field | Description |
|---|---|
mode | The transport asked for |
activePath | 'http', 'http-proxy', 'hyper-sidecar' or 'hybrid-fallback-http' |
arweaveHost, arweaveProtocol, arweaveBaseUrl, arweaveGraphqlUrl | The gateway to use |
uploadApiBaseUrl | The upload API. On the 'http' and 'hybrid-fallback-http' paths, the config's uploadApiBaseUrl, or '' when it's unset: a public gateway doesn't serve the upload API. On the sidecar and proxy paths, the sidecar's or proxy's base URL |
publicArweaveBaseUrl | The public HTTP gateway for links written into published content, scheme://host from arweaveDomain (default https://arweave.net). The same as arweaveBaseUrl on the 'http' path; on the sidecar and proxy paths, whose hosts only this machine or app can reach, it differs |
gatewayHyperKey, proxyBaseUrl | Echoed when set |
setResolvedSeedGatewayEndpoints and getResolvedSeedGatewayEndpoints store the result for the read helpers; the SDK calls the setter during init. getReadGatewayHostsForConfig(resolved) gives the read host list for a result (with a sidecar or proxy active, its base URL followed by the public gateway and the defaults as https:// URLs), resolveProxyBaseUrl(url, { origin? }) turns a relative proxy URL into an absolute one, and resolveSidecarOrigin(config) gives the sidecar's address as { host, port, baseUrl } from hyper.localSidecarHost and localSidecarPort, else 127.0.0.1:1984 (host leaves out port 80 and 443; baseUrl always has the port).
SeedGatewayConfig
interface SeedGatewayConfig {
transport?: 'http-gateway' | 'hyper' | 'hybrid'
arweaveDomain?: string
uploadApiBaseUrl?: string
proxyBaseUrl?: string
preferredReadBaseUrls?: string[]
hyper?: {
gatewayHyperKey?: string
localSidecarHost?: string // default '127.0.0.1'
localSidecarPort?: number // default 1984
probeSidecar?: boolean // default true
probeProxy?: boolean // default probeSidecar, else true
probeSidecarFromBrowser?: boolean // default false
}
// Shorthands for the hyper fields: gatewayHyperKey, localSidecarHost, localSidecarPort, probeSidecar
}seedGatewayConfigFromSeedConfig(seedConfig) builds one from an SDK-style config (top-level arweaveDomain and uploadApiBaseUrl win over the gateway ones; probeProxy and probeSidecarFromBrowser are read from gateway.hyper only), and seedGatewayConfigFromClientContext(context) from the SDK client's context. Note that resolveSeedGatewayEndpoints reads only the nested hyper object; the shorthands are merged into it by seedGatewayConfigFromSeedConfig. The SDK's gateway options are described in Client and configuration.
Upload status helpers
Read-only checks on an upload made through an upload API (uploadApiBaseUrl) or a gateway. There's no Arweave testnet, so uploads go to mainnet and cost real AR even when its attestations are on OP Sepolia.
| Function | Signature | Description |
|---|---|---|
getUploadPipelineTransactionStatus | (uploadApiBaseUrl: string, txOrDataItemId: string, options?: { timeoutMs?: number }) => Promise<TransactionStatus> | GET {api}/api/upload/arweave/data/{id}: 200 when the upload API has it, 404 when not, 504 after the timeout (default 45 s), 500 on other errors |
getArweaveUploadStatus | (uploadApiBaseUrl: string, dataItemId: string) => Promise<ArweaveUploadStatusResponse | null> | GET {api}/api/upload/arweave/status/{id}: the upload's phase, bundle and L1 confirmation state, or null |
isArweaveL1AnchoringComplete | (status: ArweaveUploadStatusResponse) => boolean | True when l1.confirmed or l1.meetsMinConfirmations |
queryArweaveGatewayTransaction | (graphqlUrl: string, dataItemId: string, init?: { signal? }) => Promise<ArweaveGatewayTransactionQueryResult | null> | The transaction's transactionId, bundledInId, blockHeight and blockTimestamp from a gateway's GraphQL API, or null on error |
queryArweaveGatewayTransactionWithFallback | (graphqlUrl: string | undefined, dataItemId: string, init?) => Promise<… | null> | The same, trying the given gateway's host and then the fallbacks, skipping unhealthy ones |
getUploadApiArweaveDataUrl, getUploadApiArweaveStatusUrl, normalizeUploadApiBaseUrl | Build the URLs above |
The GraphQL query behind queryArweaveGatewayTransaction declares its ID as String!, and arweave.net rejects that with HTTP 400 (it expects ID!), so the function returns null there. Query the gateway's GraphQL API yourself if you need this from arweave.net.
Media references
Helpers for turning a stored value (an Arweave transaction ID, a Seed UID, a URL) into something you can display. The SDK re-exports them, with its own resolveMediaRef that also loads local files.
| Export | Signature | Description |
|---|---|---|
classifyMediaRef | (raw: string, options?: { treatAs?: 'arweaveTx' | 'seedUid' | 'url' }) => MediaRefClassification | { kind: 'url' } for http(s), blob: and data: URLs, 'nonMediaUrl' for EAS explorer pages, 'seedUid' for a 32-byte hex UID, 'seedLocalId' for a 10–21 character ID, 'arweaveTxId' for a 43-character ID, else 'unknown' or 'empty' |
getArweaveUrlForTransaction | (txId: string) => string | {first read gateway}/raw/{txId}, the URL this reader loads from: the first of getArweaveReadBaseUrls(), so a preferred read gateway, sidecar or proxy comes first. The SDK and @seedprotocol/query re-export this function. Don't write its result into published content |
getPublicArweaveBaseUrl, getPublicArweaveRawUrl | () => string, (txId: string) => string | The public gateway's base URL and {public base}/raw/{txId}, for URLs that other people will open, such as links in published content. The read gateway can be a local sidecar or your app's proxy. Before endpoints are resolved, they use the read gateway unless it's a loopback address or has a path prefix, such as an app proxy, else the host environment variable or https://arweave.net |
normalizeFeedItemFields | (item: Record<string, unknown>, manifest: FeedFieldManifest) => Record<string, NormalizedFeedFieldValue | undefined> | Classifies a feed item's fields by role (image, audio, video, file, html, text) |
getFeedItemStringField | (item, key) => string | Reads a field by its name, its snake_case or its camelCase form |
resolveSeedRssImageRelationRef, resolveSeedRssImageRelationFromItem | Find an image's storage transaction ID, URL or Seed UID in a Seed RSS item. A URL built from a transaction ID is getPublicArweaveRawUrl's. SEED_RSS_IMAGE_RELATION_FIELD_KEYS lists the fields tried | |
normalizeRelationPropertyValue, resolveSeedIdsFromRefString | Read a relation value as a seedLocalId or seedUid | |
isEasAttestationExplorerUrl | (url: string) => boolean | True for easscan attestation pages, which aren't media |
In a React app, useNormalizedFeedItemFields classifies a feed item's fields and the SeedMedia* components resolve and render raw media strings.
Links in Html
Links into Arweave inside an Html value name a gateway. Seed readers re-host them onto their own read gateways before showing the Html, and publish puts them back on the public gateway before upload; Links in published content says which reader does what. These are the helpers that do it. The SDK's main entry re-exports rehostArweaveLinksInHtml, nextArweaveGatewayUrl, mapUrlAttribute, parseArweaveTransactionLink and the types ArweaveTransactionLink and HtmlArweaveRefs.
An Arweave link is the URL in a src, href or poster attribute, or a candidate in a srcset, when it is one of:
ar://<id>/raw/<id>on any host, after any path prefix, so an app proxy's URL counts/<id>on a known gateway: a host of a read base URL at a host's root, a host inKNOWN_ARWEAVE_GATEWAYS, a subdomain of either,*.arweave.netor*.ar-io.net{base}/<id>under a read base URL with a path prefix, such ashttps://app.example.com/api/seed-gateway/<id>. Such a base doesn't make/<id>on its host's root a link
<id> is a 43-character transaction ID, and whatever follows it (path, query, hash) is kept. A bare /<id> on any other host isn't treated as a link, because the path shape is common elsewhere. URLs in other attributes or in text are left alone.
| Function | Signature | Description |
|---|---|---|
rehostArweaveLinksInHtml | (html: string, options?: ArweaveLinkOptions) => string | Moves each Arweave link onto the first of options.baseUrls (default getArweaveReadBaseUrls()), keeping its form: /raw/<id> stays /raw/<id>, /<id> stays /<id>, and ar://<id> becomes /raw/<id>. The first base URL can be a sidecar or proxy that only this reader reaches, so don't store or upload the result |
publishedArweaveLinksInHtml | (html: string, options: { to?: HtmlArweaveRefs; publicBaseUrl: string; baseUrls?: readonly string[] }) => string | The reverse, for upload: each Arweave link on publicBaseUrl with its form kept, or ar://<id> with to: 'ar' (default 'gateway'). publicBaseUrl and baseUrls (default the read list) count as known gateways. Publish runs it on each Html file it uploads |
nextArweaveGatewayUrl | (url: string, options?: ArweaveLinkOptions) => string | undefined | The same link on the base URL after the one it's on, for retrying a failed load. A URL is on the base with the longest matching path, so a link under a proxy on https://app.example.com/api/seed-gateway moves past a root base on the same host. A link on none of the base URLs, such as ar://<id>, moves to the first; after the last, or for a URL that isn't an Arweave link, it's undefined. SeedHtml's load failover uses it |
parseArweaveTransactionLink | (url: string, options?: ArweaveLinkOptions) => ArweaveTransactionLink | undefined | Splits an Arweave link into its ID, form and rest; undefined for any other URL. options.baseUrls (default the read list) adds known gateways: a root base adds its host, a base with a path prefix adds that prefix |
buildArweaveTransactionLink | (link: ArweaveTransactionLink, baseUrl: string) => string | link on baseUrl (scheme, host and any path prefix) |
rewriteHtmlAttributes | (html: string, rewrite: (element, attrName: string, value: string) => string | undefined) => string | Calls rewrite with each attribute's decoded value, including inside <template>, and writes each value it returns in place as name="value". Every other byte of html stays as written. element is a parse5 DefaultTreeAdapterTypes.Element |
mapUrlAttribute | (attrName: string, value: string, map: (url: string) => string | undefined) => string | undefined | Runs map on the URL in a src, href or poster value, or on each candidate of a srcset (descriptors kept). undefined for other attributes or when nothing changes |
type ArweaveTransactionLink = {
transactionId: string
form: 'raw' | 'bare' // `/raw/<id>` (and `ar://<id>`), or `/<id>`
rest: string // path, query and hash after the ID, e.g. '/index.html' or '?v=2'
}
type ArweaveLinkOptions = { baseUrls?: readonly string[] }
type HtmlArweaveRefs = 'gateway' | 'ar'import { publishedArweaveLinksInHtml, rehostArweaveLinksInHtml } from '@seedprotocol/arweave'
const id = 'Xb1kQ3t9mZp4Rr8sVw2yLc6nHf0jDg5aEu7iOq1xYzM'
const html = `<img src="ar://${id}"><a href="https://ar.seedprotocol.io/${id}">file</a>`
console.log(rehostArweaveLinksInHtml(html, { baseUrls: ['https://zigza.xyz'] }))
// <img src="https://zigza.xyz/raw/Xb1k…"><a href="https://zigza.xyz/Xb1k…">file</a>
console.log(publishedArweaveLinksInHtml(html, { to: 'ar', publicBaseUrl: 'https://arweave.net' }))
// <img src="ar://Xb1k…"><a href="ar://Xb1k…">file</a>Constants
| Constant | Value |
|---|---|
DEFAULT_ARWEAVE_HOST | 'arweave.net' |
DEFAULT_ARWEAVE_GATEWAYS | ['arweave.net', 'zigza.xyz', 'ar.seedprotocol.io'] |
KNOWN_ARWEAVE_GATEWAYS | ['arweave.net', 'zigza.xyz', 'ar.seedprotocol.io', 'permagate.io']: the defaults plus hosts whose /<id> links still parse and get re-hosted. Nothing reads from a host that is only in this list |
DEFAULT_ARWEAVE_GRAPHQL_URL | 'https://arweave.net/graphql' |
ARWEAVE_HOST_ENV_KEYS | ['NEXT_PUBLIC_ARWEAVE_HOST', 'ARWEAVE_HOST', 'VITE_ARWEAVE_HOST'] |
DEFAULT_GATEWAY_SIDECAR_HOST, DEFAULT_GATEWAY_SIDECAR_PORT | '127.0.0.1', 1984 |
DEFAULT_ARWEAVE_RAW_READ_TIMEOUT_MS | 10000 |
ARWEAVE_GRAPHQL_TIMEOUT_MS | 30000 |
DEFAULT_TRANSACTION_STATUS_TIMEOUT_MS, DEFAULT_TRANSACTION_STATUS_TOTAL_TIMEOUT_MS, MIN_TRANSACTION_STATUS_ATTEMPT_MS | 10000, 20000, 1000 |
DEFAULT_SEED_GATEWAY_HYPER_KEY, DEFAULT_SEED_FEED_HYPER_KEY | '' |
GET_TRANSACTION_TAGS, GET_ARWEAVE_TRANSACTIONS, GET_TRANSACTIONS_TAGS_BY_IDS | GraphQL documents for a gateway's API |
TRANSACTION_TAGS_BATCH_SIZE | 100 |
DEFAULT_SEED_GATEWAY_HYPER_KEY and DEFAULT_SEED_FEED_HYPER_KEY are empty strings, so there's no built-in key to fall back on: to reach a gateway over Hyper, pass an operator key to a sidecar or proxy. See Gateway Hyper.
Types
| Type | Description |
|---|---|
TransactionStatus | { status: number; confirmed: { block_height; block_indep_hash; number_of_confirmations } | null } |
TransactionTag | { name: string; value: string } |
GetDataOptions | { decode?: boolean; string?: boolean } |
DownloadFilesParams, DownloadResult | Arguments and results of downloadFiles |
CreateTransactionOptions | { tags?: TransactionTag[] }, for createTransaction |
ArweaveTransaction | The arweave package's Transaction, which createTransaction returns. Declared members are checked, such as id and addTag. Its base class has an index signature, so reading an undeclared property compiles and is any |
SeedGatewayConfig, SeedGatewayHyperConfig, SeedGatewayTransportMode, SeedGatewayActivePath, ResolvedSeedGatewayEndpoints, ResolveSeedGatewayEndpointsOptions | Gateway resolution |
ProbeGatewayOptions | { signal?: AbortSignal; timeoutMs?: number; recordCircuit?: boolean }, for probeGateway |
SeedConfigGatewayInput, ClientGatewayContextInput | Inputs to the two seedGatewayConfigFrom… functions |
ArweaveReadBaseUrlOptions | { fallbackHosts?: readonly string[] } |
TransactionStatusFetchOptions | The options of fetchTransactionStatusAcrossGateways |
ArweaveUploadStatusResponse, ArweaveGatewayTransactionQueryResult, GetUploadPipelineTransactionStatusOptions | Upload status |
GatewayCircuitState | 'closed' | 'open' | 'half-open' |
MediaRefClassification, ClassifyMediaRefOptions, ResolveMediaRefResult, ResolveMediaRefOptions, FeedFieldManifest, FeedFieldDescriptor, FeedFieldRole, NormalizedFeedFieldValue, NormalizedMediaField, NormalizedHtmlField, NormalizedTextField, SeedRssImageRelationRef | Media references |
ArweaveTransactionLink, ArweaveLinkOptions, HtmlArweaveRefs | Html links |
IArweaveClient | The interface a platform client implements, for BaseArweaveClient.configure |
ArweaveContent, ArweaveContentReadContext, ArweaveContentReadTransform | The content read transform (experimental) |
Related
- Permanence: what's on Arweave and how gateways fit in
- Arweave and gateways: transports and fallbacks in depth, and how readers re-host Html links
- Gateway Hyper: the sidecar and proxy
- Client and configuration: the SDK's gateway settings
- @seedprotocol/eas: the attestation side
- Read published Seed data: reading content without the SDK