From 42e74090548420073d23c733dfa393b68fa6560d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:43:37 +0000 Subject: [PATCH] chore(docs): regenerate all from upstream Ran by vilenarios via the Regenerate Docs (Manual) workflow. Run: https://github.com/ar-io/docs/actions/runs/34850933099 --- content/sdks/meta.json | 3 +- .../(apis)/turboauthenticatedclient.mdx | 45 ++++ .../the-twelve-sponsored-actions.mdx | 53 +++++ content/sdks/turbo-sdk/llm.txt | 98 +++++++++ public/llms-full.txt | 202 +++++++++++++++++- public/sdks/turbo-sdk/llm.txt | 98 +++++++++ 6 files changed, 495 insertions(+), 4 deletions(-) diff --git a/content/sdks/meta.json b/content/sdks/meta.json index ffd70a77a..751ab0db3 100644 --- a/content/sdks/meta.json +++ b/content/sdks/meta.json @@ -6,11 +6,10 @@ "ardrive-core-js", "ar-io-sdk", "turbo-sdk", - "turbo-upload", "wayfinder", "---CLIs---", "...(clis)" ], "root": true, "defaultOpen": false -} +} \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx b/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx index feea3aa8c..e1bca5b92 100644 --- a/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx +++ b/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx @@ -224,6 +224,49 @@ await turbo.uploadFile({ }); ``` +Large items are uploaded in chunks and paid for when the upload is created, so +the payload is never sent just to discover its price. Smaller items go in a +single request, which is buffered in memory so its length can be declared — the +service prices an x402 upload from `Content-Length`, and a streamed body has +none. The service URL must be HTTPS: an x402 authorization is a bearer +credential, so the SDK refuses to send one over cleartext. Loopback is the +exception — `localhost`, `127.0.0.1` and `::1` are allowed over plain HTTP, so +local development against a bundler on your own machine still works. + +#### Pricing an x402 Upload Before Sending It + +`getX402PriceForDataItem` prices a signed data item from its byte count, so you +can learn the cost without transmitting the payload. Without it the only way to +get a price is to POST the data and read the 402 challenge. + +```typescript +const turbo = TurboFactory.unauthenticated({ token: 'base-usdc' }); + +const quote = await turbo.getX402PriceForDataItem({ + byteCount: signedDataItemByteCount, // the SIGNED item, not the payload inside it +}); +console.log(quote.usdcAmount); // amount to pay, in USDC's smallest unit +``` + +`getX402PriceForRawData` prices raw data that Turbo will wrap into a data item +itself, and reports the wrapping overhead — a data item is larger than its +payload by its header, signature and tags, which a caller cannot compute. + +```typescript +const quote = await turbo.getX402PriceForRawData({ + byteCount: myRawData.byteLength, + tagCount: 3, // tags you intend to attach; they change the overhead + contentType: 'image/png', +}); +console.log(quote.overhead, quote.estimatedDataItemSize); +``` + +Both take an optional `network`, defaulting to `base`. **This is the x402 +network, not the SDK token type**: the route builds its token as +`usdc-{network}`, so `base-usdc` is accepted on mainnet only because the +network there is literally `base`. Against a testnet service, pass +`network: 'base-sepolia'`. + #### Raw x402 Data Uploads Using the x402 protocol, you can also upload raw data to Turbo without signing a data item. This method is ideal for quick agent workflows where the ownership of the data is not required to be tied to a specific wallet. The eventual data item on chain will be signed by Turbo's x402 EVM signer. @@ -374,6 +417,8 @@ const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ ##### Incremental Folder Uploads +A runnable version of everything below is in [`examples/folder-index`](examples/folder-index/index.mjs): it deploys the same folder three times and prints what each run uploaded and reused. + An Arweave upload is permanent, so paying twice for byte identical files buys nothing. Pass a `folderIndex` and `uploadFolder` hashes every file, asks the index which of those files already have a data item on Arweave, and signs, diff --git a/content/sdks/turbo-sdk/(arns-names)/the-twelve-sponsored-actions.mdx b/content/sdks/turbo-sdk/(arns-names)/the-twelve-sponsored-actions.mdx index a2dd670c1..69e50de8f 100644 --- a/content/sdks/turbo-sdk/(arns-names)/the-twelve-sponsored-actions.mdx +++ b/content/sdks/turbo-sdk/(arns-names)/the-twelve-sponsored-actions.mdx @@ -67,6 +67,59 @@ await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); | `setArNSRecord` / `removeArNSRecord` / `setArNSRecordMetadata` / `removeArNSRecordMetadata` / `transferArNSRecord` | yes — small flat/derived margin | only after you revoke Turbo | | `addArNSController` / `removeArNSController` / `transferArNSAnt` | yes — small flat/derived margin | yes | +#### Point the name at your content while you buy it + +Pass `antState` and the ANT's opening record is written by the +`ario_ant::initialize` that runs inside the transaction you already sign — free +and atomic. No second action, no second signature, no second debit. Without it a +fresh name resolves to the AR.IO logo, which is the on-chain default. + +```typescript +await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'permabuy', + antState: { + transactionId: '\<43-char Arweave tx id\>', // the root `@` target + targetProtocol: 0, // 0 = Arweave (default), 1 = IPFS CID + ticker: 'MYSITE', + }, +}); +``` + +`antState` is accepted on `buyArNSName` only — the service rejects it on every +other action. Note it is nested: a **top-level** `transactionId` on a buy is a +400 by design, because that spelling means the set-record target and silently +accepting it would point the name at the logo while the caller believed +otherwise. + +**Mind the size budget.** The sponsored mint is ONE Solana transaction against +the 1232-byte packet limit, shared with Turbo's fee-payer transfer and an +`add_controller` grant. At a worst-case 51-character name only ~71 bytes are +spare: + +| Fields | Cost | Fits? | +| ------------------------------------------- | --------- | ------ | +| `transactionId` + `targetProtocol` | ~1 byte | always | +| `ticker` (16) + `logo` (43) | ~65 bytes | yes | +| `description` (512), or a full keyword list | — | **no** | + +The budget is dynamic — a shorter name buys headroom — so this SDK imposes no +client-side cap. The server measures the real transaction and returns a 400 +naming Solana's 1232-byte limit _before_ you are handed anything to sign, with +the credit debit refunded inline. That error is deterministic: do not retry it, +and surface the server's message rather than replacing it, because it names +which fields to drop. + +Field limits, all rejected at the service edge before any debit: `description` +≤ 512 characters, `keywords` ≤ 16 entries, and `logo` (plus `transactionId` +when `targetProtocol` is 0 or unset) must be 43-character Arweave ids. When +`targetProtocol` is 1 the target is an IPFS CID and is not shape-checked as an +Arweave id. + +See [`ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state](https://github.com/ar-io/ar-io-bundler/blob/main/docs/architecture/ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state) +in `ar-io/ar-io-bundler` for the measured byte table. + Every action costs credits — gas sponsorship was never meant to be _free_ sponsorship. The four purchase actions charge the ARIO cost (plus, for `buyArNSName`, a rent-derived surcharge for the ANT it mints); the other eight diff --git a/content/sdks/turbo-sdk/llm.txt b/content/sdks/turbo-sdk/llm.txt index 224094d48..823e7dee7 100644 --- a/content/sdks/turbo-sdk/llm.txt +++ b/content/sdks/turbo-sdk/llm.txt @@ -221,6 +221,49 @@ await turbo.uploadFile({ }); ``` +Large items are uploaded in chunks and paid for when the upload is created, so +the payload is never sent just to discover its price. Smaller items go in a +single request, which is buffered in memory so its length can be declared — the +service prices an x402 upload from `Content-Length`, and a streamed body has +none. The service URL must be HTTPS: an x402 authorization is a bearer +credential, so the SDK refuses to send one over cleartext. Loopback is the +exception — `localhost`, `127.0.0.1` and `::1` are allowed over plain HTTP, so +local development against a bundler on your own machine still works. + +#### Pricing an x402 Upload Before Sending It + +`getX402PriceForDataItem` prices a signed data item from its byte count, so you +can learn the cost without transmitting the payload. Without it the only way to +get a price is to POST the data and read the 402 challenge. + +```typescript +const turbo = TurboFactory.unauthenticated({ token: 'base-usdc' }); + +const quote = await turbo.getX402PriceForDataItem({ + byteCount: signedDataItemByteCount, // the SIGNED item, not the payload inside it +}); +console.log(quote.usdcAmount); // amount to pay, in USDC's smallest unit +``` + +`getX402PriceForRawData` prices raw data that Turbo will wrap into a data item +itself, and reports the wrapping overhead — a data item is larger than its +payload by its header, signature and tags, which a caller cannot compute. + +```typescript +const quote = await turbo.getX402PriceForRawData({ + byteCount: myRawData.byteLength, + tagCount: 3, // tags you intend to attach; they change the overhead + contentType: 'image/png', +}); +console.log(quote.overhead, quote.estimatedDataItemSize); +``` + +Both take an optional `network`, defaulting to `base`. **This is the x402 +network, not the SDK token type**: the route builds its token as +`usdc-{network}`, so `base-usdc` is accepted on mainnet only because the +network there is literally `base`. Against a testnet service, pass +`network: 'base-sepolia'`. + #### Raw x402 Data Uploads Using the x402 protocol, you can also upload raw data to Turbo without signing a data item. This method is ideal for quick agent workflows where the ownership of the data is not required to be tied to a specific wallet. The eventual data item on chain will be signed by Turbo's x402 EVM signer. @@ -370,6 +413,8 @@ const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ ##### Incremental Folder Uploads +A runnable version of everything below is in [`examples/folder-index`](examples/folder-index/index.mjs): it deploys the same folder three times and prints what each run uploaded and reused. + An Arweave upload is permanent, so paying twice for byte identical files buys nothing. Pass a `folderIndex` and `uploadFolder` hashes every file, asks the index which of those files already have a data item on Arweave, and signs, @@ -1126,6 +1171,59 @@ await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); | `setArNSRecord` / `removeArNSRecord` / `setArNSRecordMetadata` / `removeArNSRecordMetadata` / `transferArNSRecord` | yes — small flat/derived margin | only after you revoke Turbo | | `addArNSController` / `removeArNSController` / `transferArNSAnt` | yes — small flat/derived margin | yes | +#### Point the name at your content while you buy it + +Pass `antState` and the ANT's opening record is written by the +`ario_ant::initialize` that runs inside the transaction you already sign — free +and atomic. No second action, no second signature, no second debit. Without it a +fresh name resolves to the AR.IO logo, which is the on-chain default. + +```typescript +await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'permabuy', + antState: { + transactionId: '\', // the root `@` target + targetProtocol: 0, // 0 = Arweave (default), 1 = IPFS CID + ticker: 'MYSITE', + }, +}); +``` + +`antState` is accepted on `buyArNSName` only — the service rejects it on every +other action. Note it is nested: a **top-level** `transactionId` on a buy is a +400 by design, because that spelling means the set-record target and silently +accepting it would point the name at the logo while the caller believed +otherwise. + +**Mind the size budget.** The sponsored mint is ONE Solana transaction against +the 1232-byte packet limit, shared with Turbo's fee-payer transfer and an +`add_controller` grant. At a worst-case 51-character name only ~71 bytes are +spare: + +| Fields | Cost | Fits? | +| ------------------------------------------- | --------- | ------ | +| `transactionId` + `targetProtocol` | ~1 byte | always | +| `ticker` (16) + `logo` (43) | ~65 bytes | yes | +| `description` (512), or a full keyword list | — | **no** | + +The budget is dynamic — a shorter name buys headroom — so this SDK imposes no +client-side cap. The server measures the real transaction and returns a 400 +naming Solana's 1232-byte limit _before_ you are handed anything to sign, with +the credit debit refunded inline. That error is deterministic: do not retry it, +and surface the server's message rather than replacing it, because it names +which fields to drop. + +Field limits, all rejected at the service edge before any debit: `description` +≤ 512 characters, `keywords` ≤ 16 entries, and `logo` (plus `transactionId` +when `targetProtocol` is 0 or unset) must be 43-character Arweave ids. When +`targetProtocol` is 1 the target is an IPFS CID and is not shape-checked as an +Arweave id. + +See [`ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state](https://github.com/ar-io/ar-io-bundler/blob/main/docs/architecture/ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state) +in `ar-io/ar-io-bundler` for the measured byte table. + Every action costs credits — gas sponsorship was never meant to be _free_ sponsorship. The four purchase actions charge the ARIO cost (plus, for `buyArNSName`, a rent-derived surcharge for the ANT it mints); the other eight diff --git a/public/llms-full.txt b/public/llms-full.txt index 20d85d004..b699f5ac5 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -19332,6 +19332,11 @@ const uploadResult = await turbo.uploadFile({ // Your file is now permanently stored! ``` +Adding uploads to a server that already exists, where a new dependency has to +get past a review? [`turbo-upload`](/sdks/turbo-upload) does the signing and +uploading with no dependencies and nothing else. The Turbo SDK is the right +choice everywhere else. + ## Organize Your Data Before uploading, learn best practices for structuring and tagging your data for optimal retrieval and organization. @@ -30260,10 +30265,11 @@ Build powerful applications with our comprehensive suite of SDKs designed for th Each SDK serves a specific purpose in the ar.io ecosystem: - **Turbo SDK** - For applications that need fast, reliable data uploads to Arweave -- **ar.io SDK** - For interacting with ar.io Solana programs and services +- **turbo-upload** - The same uploads with no dependencies, for adding Turbo to a server that already exists +- **ar.io SDK** - For ArNS names, ANT contracts and gateway data on the ar.io network - **Wayfinder SDK** - For decentralized data access with built-in verification and gateway routing -All SDKs are available for both Node.js and browser environments, with TypeScript support included. +All of them ship TypeScript types. Every one runs in both Node.js and the browser except `turbo-upload`, which is server-side only: it signs with a raw Arweave key, and that does not belong in a browser. ## Next Steps @@ -30490,6 +30496,49 @@ await turbo.uploadFile({ }); ``` +Large items are uploaded in chunks and paid for when the upload is created, so +the payload is never sent just to discover its price. Smaller items go in a +single request, which is buffered in memory so its length can be declared — the +service prices an x402 upload from `Content-Length`, and a streamed body has +none. The service URL must be HTTPS: an x402 authorization is a bearer +credential, so the SDK refuses to send one over cleartext. Loopback is the +exception — `localhost`, `127.0.0.1` and `::1` are allowed over plain HTTP, so +local development against a bundler on your own machine still works. + +#### Pricing an x402 Upload Before Sending It + +`getX402PriceForDataItem` prices a signed data item from its byte count, so you +can learn the cost without transmitting the payload. Without it the only way to +get a price is to POST the data and read the 402 challenge. + +```typescript +const turbo = TurboFactory.unauthenticated({ token: 'base-usdc' }); + +const quote = await turbo.getX402PriceForDataItem({ + byteCount: signedDataItemByteCount, // the SIGNED item, not the payload inside it +}); +console.log(quote.usdcAmount); // amount to pay, in USDC's smallest unit +``` + +`getX402PriceForRawData` prices raw data that Turbo will wrap into a data item +itself, and reports the wrapping overhead — a data item is larger than its +payload by its header, signature and tags, which a caller cannot compute. + +```typescript +const quote = await turbo.getX402PriceForRawData({ + byteCount: myRawData.byteLength, + tagCount: 3, // tags you intend to attach; they change the overhead + contentType: 'image/png', +}); +console.log(quote.overhead, quote.estimatedDataItemSize); +``` + +Both take an optional `network`, defaulting to `base`. **This is the x402 +network, not the SDK token type**: the route builds its token as +`usdc-{network}`, so `base-usdc` is accepted on mainnet only because the +network there is literally `base`. Against a testnet service, pass +`network: 'base-sepolia'`. + #### Raw x402 Data Uploads Using the x402 protocol, you can also upload raw data to Turbo without signing a data item. This method is ideal for quick agent workflows where the ownership of the data is not required to be tied to a specific wallet. The eventual data item on chain will be signed by Turbo's x402 EVM signer. @@ -30639,6 +30688,8 @@ const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ ##### Incremental Folder Uploads +A runnable version of everything below is in [`examples/folder-index`](examples/folder-index/index.mjs): it deploys the same folder three times and prints what each run uploaded and reused. + An Arweave upload is permanent, so paying twice for byte identical files buys nothing. Pass a `folderIndex` and `uploadFolder` hashes every file, asks the index which of those files already have a data item on Arweave, and signs, @@ -31395,6 +31446,59 @@ await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); | `setArNSRecord` / `removeArNSRecord` / `setArNSRecordMetadata` / `removeArNSRecordMetadata` / `transferArNSRecord` | yes — small flat/derived margin | only after you revoke Turbo | | `addArNSController` / `removeArNSController` / `transferArNSAnt` | yes — small flat/derived margin | yes | +#### Point the name at your content while you buy it + +Pass `antState` and the ANT's opening record is written by the +`ario_ant::initialize` that runs inside the transaction you already sign — free +and atomic. No second action, no second signature, no second debit. Without it a +fresh name resolves to the AR.IO logo, which is the on-chain default. + +```typescript +await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'permabuy', + antState: { + transactionId: '\', // the root `@` target + targetProtocol: 0, // 0 = Arweave (default), 1 = IPFS CID + ticker: 'MYSITE', + }, +}); +``` + +`antState` is accepted on `buyArNSName` only — the service rejects it on every +other action. Note it is nested: a **top-level** `transactionId` on a buy is a +400 by design, because that spelling means the set-record target and silently +accepting it would point the name at the logo while the caller believed +otherwise. + +**Mind the size budget.** The sponsored mint is ONE Solana transaction against +the 1232-byte packet limit, shared with Turbo's fee-payer transfer and an +`add_controller` grant. At a worst-case 51-character name only ~71 bytes are +spare: + +| Fields | Cost | Fits? | +| ------------------------------------------- | --------- | ------ | +| `transactionId` + `targetProtocol` | ~1 byte | always | +| `ticker` (16) + `logo` (43) | ~65 bytes | yes | +| `description` (512), or a full keyword list | — | **no** | + +The budget is dynamic — a shorter name buys headroom — so this SDK imposes no +client-side cap. The server measures the real transaction and returns a 400 +naming Solana's 1232-byte limit _before_ you are handed anything to sign, with +the credit debit refunded inline. That error is deterministic: do not retry it, +and surface the server's message rather than replacing it, because it names +which fields to drop. + +Field limits, all rejected at the service edge before any debit: `description` +≤ 512 characters, `keywords` ≤ 16 entries, and `logo` (plus `transactionId` +when `targetProtocol` is 0 or unset) must be 43-character Arweave ids. When +`targetProtocol` is 1 the target is an IPFS CID and is not shape-checked as an +Arweave id. + +See [`ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state](https://github.com/ar-io/ar-io-bundler/blob/main/docs/architecture/ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state) +in `ar-io/ar-io-bundler` for the measured byte table. + Every action costs credits — gas sponsorship was never meant to be _free_ sponsorship. The four purchase actions charge the ARIO cost (plus, for `buyArNSName`, a rent-derived surcharge for the ANT it mints); the other eight @@ -31939,6 +32043,100 @@ The Turbo CLI provides the following commands to manage Credit Share Approvals: - `--ignore-approvals`: Ignore all Credit Share Approvals and only use the signer's balance. - `--use-signer-balance-first`: Use the signer's balance first before using Credit Share Approvals. +# turbo-upload (/sdks/turbo-upload) + +**For AI and LLM users**: Access the complete turbo-upload documentation in plain text format at{" "} + llm.txt for easy + consumption by AI agents and language models. See [AI Agents & LLMs](/build/agents) for the full + agent toolkit. + +`turbo-upload` signs ANS-104 data items with an Arweave JWK or a Solana key and uploads them to Turbo. That is all it does, and it has no dependencies. + +## Which One To Use + +**Start with the [Turbo SDK](/sdks/turbo-sdk).** It is the full client and the right choice for most things. Reach for `turbo-upload` when the size of a new dependency decides whether your change gets merged. + +| | [Turbo SDK](/sdks/turbo-sdk) | turbo-upload | +|---|---|---| +| Keys | Arweave, Solana, Ethereum, KYVE, Polygon | Arweave JWK or Solana key | +| Runs in | Node and the browser | Node only | +| Does | upload, payments, credits, folders, CLI, ArNS | sign and upload | +| Installed on its own | 784 lockfile entries, 895 MB | 1 entry, 460 KB | + +Measured on 2026-09-11 against `@ardrive/turbo-sdk@1.43.0` and `@ardrive/turbo-upload@0.3.0`, installing each into an empty project. Re-run it rather than trusting the table. + +The two share no state, so having both installed is fine. + +## Quick Start + + ### Install + + ```npm + npm install @ardrive/turbo-upload + ``` + + ### Upload + + ```javascript + + // .testnet() is free and nothing it writes is permanent. + // .production() is mainnet: permanent, public, paid from this wallet. + const client = TurboUpload.testnet({ jwk: JSON.parse(process.env.ARWEAVE_JWK) }); + + const { id, winc } = await client.upload({ + data: Buffer.from("hello permanence"), + tags: [{ name: "Content-Type", value: "text/plain" }], + }); + ``` + +Read it back from any gateway at `/`, so retrieval never depends on the service you uploaded through. [Wayfinder](/sdks/wayfinder) handles gateway choice and verification for you. + +## Solana Keys + +```javascript + +const client = TurboUpload.production({ + jwk: process.env.SOLANA_SECRET_KEY, + token: "solana", +}); +``` + +Takes any form a Solana user holds: a base58 secret key as Phantom exports it, the JSON array `solana-keygen` writes, raw 64 bytes, or a bare 32-byte seed. `client.address` is the base58 Solana address. + +This is **ANS-104 signature type 4**, the same type the Turbo SDK uses for `token: "solana"`, so both produce identical ids for identical content. + +A 64-byte key carries its own public key, and that half is checked rather than trusted. A key whose halves disagree is refused when the client is constructed, because signing with one produces items nothing can verify and you find out after paying. + +## What Gets Written Wrong + +The rest of the API behaves as you would expect. These do not. + +- **Signing twice charges twice.** RSA-PSS uses a fresh random salt each time, so the same payload signed twice has a different id. `upload()` signs internally, so `sign()` then `upload()` charges for two items. Use `sign()` then `uploadSigned(item)`, and record `item.idB64Url`, which is the string form. +- **`target` and `anchor` look alike and are not.** Adjacent 32-byte fields: `target` is base64url that decodes to 32 bytes, `anchor` is 32 raw bytes. A 43-character base64url anchor throws. +- **Production is the default.** A bare `new TurboUpload({ jwk })` writes to mainnet, permanently, paid. +- **An unrecognised option throws** rather than being ignored, and the error names the key you meant. + + Do not spread `PRODUCTION` or `TESTNET` into the constructor. Those records also carry `name` and + `gatewayUrl`, which are not constructor options. Use `TurboUpload.production()` and + `TurboUpload.testnet()`. + +## Endpoints + +| | upload | payment | gateway | +|---|---|---|---| +| `PRODUCTION` | `https://upload.ardrive.io` | `https://payment.ardrive.io` | `https://turbo-gateway.com` | +| `TESTNET` | `https://upload.services.ar-io.dev` | `https://payment.services.ar-io.dev` | `https://ar-io.dev` | + +Exported as constants. Import `PRODUCTION` or `TESTNET` rather than typing a hostname: a near-miss on a testnet host can resolve to something that answers `200` with HTML, which fails much less obviously than a bad hostname should. + +## Requirements + +Node 18.17 or later, tested on 18.17, 18, 20, 22 and 24. Server-side only, because a raw Arweave key does not belong in a browser. Published with npm provenance. + +Every error extends `TurboError`. Catch `TurboPaymentError` separately: it means the wallet cannot pay, and retrying never helps. + +## Next Steps + # Wayfinder SDK's (/sdks/wayfinder) **For AI and LLM users**: Access the complete Wayfinder documentation in plain text format at{" "} diff --git a/public/sdks/turbo-sdk/llm.txt b/public/sdks/turbo-sdk/llm.txt index 224094d48..823e7dee7 100644 --- a/public/sdks/turbo-sdk/llm.txt +++ b/public/sdks/turbo-sdk/llm.txt @@ -221,6 +221,49 @@ await turbo.uploadFile({ }); ``` +Large items are uploaded in chunks and paid for when the upload is created, so +the payload is never sent just to discover its price. Smaller items go in a +single request, which is buffered in memory so its length can be declared — the +service prices an x402 upload from `Content-Length`, and a streamed body has +none. The service URL must be HTTPS: an x402 authorization is a bearer +credential, so the SDK refuses to send one over cleartext. Loopback is the +exception — `localhost`, `127.0.0.1` and `::1` are allowed over plain HTTP, so +local development against a bundler on your own machine still works. + +#### Pricing an x402 Upload Before Sending It + +`getX402PriceForDataItem` prices a signed data item from its byte count, so you +can learn the cost without transmitting the payload. Without it the only way to +get a price is to POST the data and read the 402 challenge. + +```typescript +const turbo = TurboFactory.unauthenticated({ token: 'base-usdc' }); + +const quote = await turbo.getX402PriceForDataItem({ + byteCount: signedDataItemByteCount, // the SIGNED item, not the payload inside it +}); +console.log(quote.usdcAmount); // amount to pay, in USDC's smallest unit +``` + +`getX402PriceForRawData` prices raw data that Turbo will wrap into a data item +itself, and reports the wrapping overhead — a data item is larger than its +payload by its header, signature and tags, which a caller cannot compute. + +```typescript +const quote = await turbo.getX402PriceForRawData({ + byteCount: myRawData.byteLength, + tagCount: 3, // tags you intend to attach; they change the overhead + contentType: 'image/png', +}); +console.log(quote.overhead, quote.estimatedDataItemSize); +``` + +Both take an optional `network`, defaulting to `base`. **This is the x402 +network, not the SDK token type**: the route builds its token as +`usdc-{network}`, so `base-usdc` is accepted on mainnet only because the +network there is literally `base`. Against a testnet service, pass +`network: 'base-sepolia'`. + #### Raw x402 Data Uploads Using the x402 protocol, you can also upload raw data to Turbo without signing a data item. This method is ideal for quick agent workflows where the ownership of the data is not required to be tied to a specific wallet. The eventual data item on chain will be signed by Turbo's x402 EVM signer. @@ -370,6 +413,8 @@ const { manifest, fileResponses, manifestResponse } = await turbo.uploadFolder({ ##### Incremental Folder Uploads +A runnable version of everything below is in [`examples/folder-index`](examples/folder-index/index.mjs): it deploys the same folder three times and prints what each run uploaded and reused. + An Arweave upload is permanent, so paying twice for byte identical files buys nothing. Pass a `folderIndex` and `uploadFolder` hashes every file, asks the index which of those files already have a data item on Arweave, and signs, @@ -1126,6 +1171,59 @@ await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); | `setArNSRecord` / `removeArNSRecord` / `setArNSRecordMetadata` / `removeArNSRecordMetadata` / `transferArNSRecord` | yes — small flat/derived margin | only after you revoke Turbo | | `addArNSController` / `removeArNSController` / `transferArNSAnt` | yes — small flat/derived margin | yes | +#### Point the name at your content while you buy it + +Pass `antState` and the ANT's opening record is written by the +`ario_ant::initialize` that runs inside the transaction you already sign — free +and atomic. No second action, no second signature, no second debit. Without it a +fresh name resolves to the AR.IO logo, which is the on-chain default. + +```typescript +await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'permabuy', + antState: { + transactionId: '\', // the root `@` target + targetProtocol: 0, // 0 = Arweave (default), 1 = IPFS CID + ticker: 'MYSITE', + }, +}); +``` + +`antState` is accepted on `buyArNSName` only — the service rejects it on every +other action. Note it is nested: a **top-level** `transactionId` on a buy is a +400 by design, because that spelling means the set-record target and silently +accepting it would point the name at the logo while the caller believed +otherwise. + +**Mind the size budget.** The sponsored mint is ONE Solana transaction against +the 1232-byte packet limit, shared with Turbo's fee-payer transfer and an +`add_controller` grant. At a worst-case 51-character name only ~71 bytes are +spare: + +| Fields | Cost | Fits? | +| ------------------------------------------- | --------- | ------ | +| `transactionId` + `targetProtocol` | ~1 byte | always | +| `ticker` (16) + `logo` (43) | ~65 bytes | yes | +| `description` (512), or a full keyword list | — | **no** | + +The budget is dynamic — a shorter name buys headroom — so this SDK imposes no +client-side cap. The server measures the real transaction and returns a 400 +naming Solana's 1232-byte limit _before_ you are handed anything to sign, with +the credit debit refunded inline. That error is deterministic: do not retry it, +and surface the server's message rather than replacing it, because it names +which fields to drop. + +Field limits, all rejected at the service edge before any debit: `description` +≤ 512 characters, `keywords` ≤ 16 entries, and `logo` (plus `transactionId` +when `targetProtocol` is 0 or unset) must be 43-character Arweave ids. When +`targetProtocol` is 1 the target is an IPFS CID and is not shape-checked as an +Arweave id. + +See [`ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state](https://github.com/ar-io/ar-io-bundler/blob/main/docs/architecture/ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state) +in `ar-io/ar-io-bundler` for the measured byte table. + Every action costs credits — gas sponsorship was never meant to be _free_ sponsorship. The four purchase actions charge the ARIO cost (plus, for `buyArNSName`, a rent-derived surcharge for the ANT it mints); the other eight