Rising Tide Developers
StorageArweave

Arweave

The Arweave destination - the owner's key, the free allowance and what it is measured in, buying more room, gateways, and how detach pins an edition to a permanent URL.

Arweave is the destination an edition owner can write a permanent copy to, and currently the only one. Everything on this page is specific to it; the provider-neutral model - saved versus Saved As, the three Save As legs, detach authority - is in Storage.

The protocol does not write here. It holds no Arweave key, and there is nothing in rt-api that could sign a data item if something asked it to. Every permanent copy on this destination was signed by the key of the person who owns that edition.

The owner's key

A user who turns on storage gets a Privy embedded EVM wallet, created in their session. It is theirs, it is exportable, and it does two jobs here: it signs ANS-104 data items, and it is the address a free allowance and any bought credits are counted against.

Signing is EIP-191 over the data item's deep hash, which is why an EVM key works on Arweave at all. Three signers are supported: the Privy embedded wallet, an injected EIP-1193 wallet, and Wander. A bring-your-own-wallet account names the signer it will use in the plan request, because the server has to know which address to check the receipts against.

A Stacks wallet cannot sign here today. Stacks keys are secp256k1, the same curve, so sharing one key across both is plausible and is filed as an open question rather than a plan. Two keys is the shipped model.

The free allowance, and what it is not

Turbo gives each address a 10 MiB lifetime allowance. It never resets. It is per wallet and per subnet, it is not a monthly quota, and it is not a rate limit that refills if you wait.

It is readable without authentication, for any address, which makes it easy to check independently:

GET https://payment.ardrive.io/v1/account/free?address=<address>

Two separate limits apply, and they fail differently:

LimitValueWhat exceeding it does
Lifetime allowance10,485,760 bytesthe upload is charged to credits, or refused if there are none
Per itemfreeUploadLimitBytes from /v1/info, 107,520 bytes todaythat item cannot ride the free tier at all

Read the per-item cap at write time rather than pinning it. It is the destination's number, not ours.

What that allowance is worth, measured: one Save As of a single-artifact edition, manifest counted, averages 45,926 bytes across 28 real editions. So a lifetime allowance is roughly 228 saves for one key. This figure is measured rather than assumed, and it converts a price - it is what tells an owner how many more pages their money buys - so it is re-derived from the database rather than estimated.

Buying more room

Turbo sells credits, denominated in winc (winston credits). An owner can buy them by card through Turbo's own hosted checkout, landing on their own storage address.

This rail is behind STORAGE_PAID_RAIL_ENABLED and the runtime setting storage.paidRail.enabled (which can only close it, never open it), and off means the price does not exist rather than that a client declines to draw it: the posture carries paidRail.offer: null, and the checkout route refuses with a reason naming free storage and no amount in the body. On testnet the flag alone opens nothing, because every testnet surface promises no real money and a card checkout is real money on any network: a walk of the checkout path sets STORAGE_PAID_RAIL_ALLOW_TESTNET beside it, deliberately, and removes both afterwards.

Storage here is cheap enough that the paid rail is close to a formality - $5 buys many times the entire free lifetime allowance. Almost nobody reaches it.

Gateways

Two gateway hostnames appear on a stored blob, and publicUrl is the one to use.

  • publicUrl -> arweave.net/<id>, the official gateway
  • gatewayUrl -> turbo-gateway.com/<id>, the uploader's own cache

The caches serve a fresh upload immediately while arweave.net's index can lag by minutes, which is why receipt verification fetches from several named hosts before giving up. For reading a stored artifact afterwards, prefer publicUrl.

Preferring a secondary gateway is not a neutral choice. Measured 2026-08-11: every ar-io.net URL in the database was unreachable (0 of 12) while arweave.net served 11 of 12, and the failure was at the DNS delegation rather than a gateway hiccup - public resolvers returned SERVFAIL with "No Reachable Authority at delegation". That preference had been writing a dead host into the image field of manifests.

This is the worst place for a dead hostname. A manifest is what an edition's on-chain token-uri points at after detach, and lock-detached freezes it permanently - so an edition locked against a manifest naming an unresolvable gateway renders blank in every wallet forever, per edition, unfixable. The bytes on Arweave are fine either way; the hostname is the fragile part, which is why the manifest also carries properties.arweaveTxId so recovery never depends on a hostname at all.

Read publicUrl off the blob rather than assembling a gateway URL yourself - a preference repeated is a preference that drifts.

Manifests

A manifest is the JSON document an edition's token-uri resolves to after detach. It carries the edition's metadata plus an image URL and properties.arweaveTxId.

Two rules about what goes in it:

  • It names no host of ours. A permanent document pointing back at our infrastructure is the thing detach exists to prevent, and the severance check refuses one that does.
  • The social-card image is not in it. The og derivative is a convenience for link previews, not part of the edition, and it stays on the landing store.

The manifest is verified complete and readable before anything is written on chain, and its image must be an item this run actually wrote.

Detach

Detach is the point where a specific Arweave URL becomes an edition's permanent identity. The detachable capability and its owner-authority model are provider-neutral and documented in Storage; what this destination contributes is the URL that gets pinned.

A completed Save As is what triggers it. For a custodial account the protocol signs the detach on the owner's behalf, seconds after the manifest verifies. On confirmation, ProductInstance.detachedTokenUri becomes non-null - that is the field to read to know an edition is detached.

For a wallet account it does not run at all. The detach job records SKIPPED with the reason that the signer is not custodial, and no shipped surface calls set-detached-uri from a client wallet. detachedTokenUri stays null on an edition whose permanent copy exists and verifies, so null means the chain does not point there rather than nothing was written - read the manifest reference, not this field, to answer the second question.

Nothing here can be swept

Data on Arweave is permanent and paid for. A superseded copy is not garbage to collect; it is bytes that were bought and cannot be returned. Any cleanup an operator runs is restricted to the landing store by construction, and never reaches this destination.

What a second destination would change

The key type, the allowance, its unit, the per-item cap, the credit denomination, the gateway preference and the manifest shape are all this destination's. The saved / Save As model and the detach authority rules are not - they are on Storage and do not change when a second destination arrives.

If you surface storage in your own UI, that boundary is worth respecting: a section heading reading "Arweave" and a bare transaction id both bake in an assumption that will not survive a second destination. A byte allowance in particular does not port - a fee-market destination has no allowance to have remaining.