---
name: castrater-storage-rental
description: Rent short-lived Castrater file storage with an x402-capable Base wallet. Use for one-off paid uploads that should return a public link or private-file metadata without creating an account.
---

# Castrater storage rental

Use Castrater's HTTP API to purchase storage for one file. This flow is accountless: do not create a human account, request an email code, register a passkey, or initialize a rewards wallet.

## Service

- Base URL: `https://storage.castrater.xyz`
- Provider manifest: `GET /.well-known/simsala-provider.json`
- Live capabilities and limits: `GET /v1/capabilities`
- File contract: `GET /v1/contracts/file`
- OpenAPI document: `GET /openapi.yaml`

Read live capabilities before relying on price, maximum size, retention, or encryption availability. Use an x402 v2 client that supports the `exact` EVM scheme on Base (`eip155:8453`) and has access to a wallet funded with enough Base USDC and ETH for gas. The x402 route also requires the official `payment-identifier` extension advertised in `PAYMENT-REQUIRED`; append/populate that extension before signing. A standard exact-EVM x402 signature without `payment-identifier` will be rejected. Do not construct payment headers by hand when a compatible x402 client is available.

## Rent and upload one file

1. Read the file bytes. Compute their byte length and lowercase hexadecimal SHA-256 digest.
2. Send `POST /v1/files` through the x402-capable fetch client with JSON metadata:

   ```json
   {
     "filename": "research.json",
     "content_type": "application/json",
     "size_bytes": 2048,
     "visibility": "public",
     "encryption_mode": "none"
   }
   ```

   `filename` must be a basename, and `size_bytes` must equal the exact bytes later uploaded. Use `visibility: "private"` with `encryption_mode: "provider_envelope"` only when live capabilities report that mode as enabled.

3. On `402 Payment Required`, let the x402 client select the advertised Base USDC requirement, append/populate the required `payment-identifier` extension, sign the exact authorization, and retry the identical method, URL, headers, and JSON body with the payment signature added. Do not change the file metadata between the challenge and paid retry.
4. From the successful `201` response, retain `file_id`, `access_token`, `upload`, and `retention_expires_at`. Treat `access_token` as a secret bearer capability for this file only.
5. Upload the original bytes to `upload.url` with `PUT`, these headers, and no JSON encoding:

   ```http
   Authorization: Bearer <access_token>
   Content-Type: application/octet-stream
   ```

   Honor any additional `upload.required_headers` returned by the service. The body length must exactly match the declared `size_bytes`.

6. Finalize the upload:

   ```http
   POST /v1/files/{file_id}/finalize
   Authorization: Bearer <access_token>
   Content-Type: application/json
   Idempotency-Key: <new UUID>

   { "size_bytes": 2048, "sha256": "<lowercase SHA-256 of the original bytes>" }
   ```

7. Return the finalized `url` for a public file. For a private file, return the provider's file metadata and use the capability-authorized download flow described by the live OpenAPI document.

## Capability handling

- The access token expires with the rental and is scoped to one file. It can upload, finalize, inspect, download, or delete only that file.
- Keep the token out of logs, public output, URLs, and file contents. Store it in the caller's secret storage if later management is needed.
- A public file URL is shareable; anyone with that URL can read the file until retention expires.
- Preserve the same idempotency key when retrying an uncertain finalize request. Never reuse a payment authorization or file capability for a different file.
- If payment succeeds but a later request is uncertain, inspect the original response or retry safely before authorizing another payment.

This accountless flow does not create a persistent file list and does not route rewards to a human account's managed rewards wallet.

## Juicebox fallback for browser-style wallets

Prefer the x402 route above for autonomous agents. Use `POST /v1/files/juicebox` only when the caller can execute the exact Castrater Juicebox file-payment flow, because an arbitrary Juicebox contribution receipt is not enough to create upload credentials.

1. Read `GET /v1/capabilities` and use the returned `juicebox` fields exactly: Base mainnet, USDC token, configured project ID, configured directory, exact price, and beneficiary policy.
2. Build the file body first and keep its key order unchanged. Castrater computes `sha256(UTF-8 JSON.stringify(file))` over the `file` object it receives at `/v1/files/juicebox`.
3. Resolve the configured project's current USDC terminal through `JBDirectory.primaryTerminalOf(project_id, token_address)`.
4. Approve only the exact USDC price to that resolved terminal when allowance is insufficient.
5. Call `JBMultiTerminal.pay(project_id, token_address, amount, beneficiary, minReturnedTokens, memo, metadata)` with:
   - `beneficiary`: the account rewards wallet when the caller is authenticated and Castrater reports one; otherwise the paying wallet.
   - `memo`: exactly `Castrater Storage:<request_sha256>` where `request_sha256` is the digest from step 2.
   - `metadata`: `0x`.
6. Wait for confirmation, then submit `POST /v1/files/juicebox` with the confirmed `transaction_hash` and the identical `file` object. A confirmed transaction hash can be retried with the same file body, but cannot be reused for another file.

If using Juicebox Center MCP, follow its V6 transaction-review journey: use the relevant V6 read/quote/prepare tool, inspect the returned unsigned plan and preflight, have an external wallet sign and broadcast each exact step, verify the receipt/outcome afterward, and keep prerequisite transaction hashes attached to dependent steps. MCP plan tokens, Center REST plans, and generic contribution receipts are not Castrater payment receipts. Do not send `/v1/files/juicebox` a transaction unless the underlying confirmed call is the exact terminal `pay` call above or a supported delegated wallet execution containing that exact call.
