diff --git a/docs/content/console/api-reference.mdx b/docs/content/console/api-reference.mdx new file mode 100644 index 0000000000..781c556c1e --- /dev/null +++ b/docs/content/console/api-reference.mdx @@ -0,0 +1,295 @@ +--- +title: API Reference +description: The Walrus Console external API surface for spaces, buckets, files, and Seal sponsorship, including authentication, conventions, and error codes. +keywords: [walrus console, api reference, spaces, buckets, files, seal, api keys] +--- + +This reference covers the Walrus Console external API: the endpoints third-party developers use to manage spaces, buckets, and files, plus the [Seal](/docs/data-security) sponsorship endpoints that back private-bucket access control. + +:::info + +This documents the current Testnet surface. The API is in alpha and currently only available on Testnet at `https://api.testnet.harbor.walrus.xyz`. The endpoint structure might change before Mainnet GA. For a guided walkthrough of the full encrypted flow, start with the [Quick Start](./quickstart). For the product model behind these endpoints, see the [concepts and overview](./overview). + +::: + +## Authentication + +Every request carries an API key as a bearer token: + +```http +Authorization: Bearer hbr_… +``` + +Mint keys through the Console web interface. Each key has one of two roles: + +- `read_write` keys can change state: create and delete buckets, upload and delete files, and finalize private buckets. +- `read_only` keys can list, check status, and download only. Any write with a read-only key returns `403` with code `read_only_api_key`. + +A request with no valid key returns `401`. A key without access to the target resource returns `403`. + +## Base URL and conventions + +All paths are relative to the base URL and prefixed with `/api/v1`: + +``` +https://api.testnet.harbor.walrus.xyz +``` + +Request and response bodies are JSON unless noted (file upload uses `multipart/form-data`, and download returns a raw byte stream). Resource identifiers are UUIDs. + +### Pagination + +List endpoints return an opaque cursor. Pass `limit` to set page size and `cursor` to fetch the next page. When there are no further results, the cursor field returns `null`. Bucket lists return the cursor as `next_cursor`; file lists return it under `pagination.nextCursor`. + +### Asynchronous operations + +Upload and delete operations are asynchronous. Upload returns `202` with a file ID, then you poll the file status endpoint until the state is `completed`. Delete returns `204` immediately and releases storage after a background worker confirms the removal. + +## Errors + +Error responses use the shape: + +```json +{ "error": "Human-readable message", "code": "machine_readable_code" } +``` + +The `code` field is present when a machine-readable value applies. Common error codes include: + +| Code | Meaning | +| --- | --- | +| `unauthorized` | Missing or invalid API key. | +| `read_only_api_key` | A write was attempted with a read-only key. | +| `api_key_registering` / `api_key_revoking` / `api_key_revoked` | The key is not currently usable. | +| `bucket_not_in_scope` | The key cannot access the target bucket. | +| `bucket_not_finalized` | The bucket has not completed the finalize step. | +| `mirror_missing_grant` | The onchain access grant has not yet mirrored into the access index. Retry. | +| `quota_exceeded` | The operation exceeds the plan or storage quota. | +| `payload_too_large` | The upload exceeds the size limit. | +| `bad_request` | The request failed validation. | +| `USED_NONCE` / `EXPIRED_TIMESTAMP` / `ADDRESS_MISMATCH` / `INVALID_CHALLENGE` | Wallet challenge errors, returned by the signature-authentication flow. | + +Create-bucket requests that exceed a plan limit return `422` with a distinct shape: + +```json +{ "code": "PLAN_LIMIT_EXCEEDED", "limit": "storage", "currentTier": "free" } +``` + +The `limit` value is one of `storage`, `users`, or `buckets`. + +## Spaces + +A space is the top-level container for your data. Console creates a Personal Space at sign-up. + +### List spaces + +`GET /api/v1/spaces` + +Lists all spaces accessible to the authenticated user. + +| Parameter | Location | Required | Description | +| --- | --- | --- | --- | +| `type` | query | No | Filter by space type: `personal` or `team`. | + +Returns `200` with a `data` array of spaces. Each space includes `id`, `type`, `name`, `storage_used`, `storage_cap`, `bucket_count`, `role`, and `created_at`. Team Spaces also include `member_count`. + +### List buckets in a space + +`GET /api/v1/spaces/{id}/buckets` + +| Parameter | Location | Required | Description | +| --- | --- | --- | --- | +| `id` | path | Yes | Space UUID. | +| `limit` | query | No | Page size, 1 to 1000. Default 100. | +| `cursor` | query | No | Cursor from a previous page. | +| `q` | query | No | Case-insensitive substring match on bucket name. | +| `visibility` | query | No | Filter by `public` or `private`. | +| `sortField` | query | No | `name`, `date`, or `storage`. Default `date`. | +| `sortOrder` | query | No | `asc` or `desc`. Default `desc`. | + +Returns `200` with `buckets` and `next_cursor`. + +### Create a bucket + +`POST /api/v1/spaces/{id}/buckets` + +Reserves a private bucket. This is the first step of the reserve, sign, and finalize handshake. In the current alpha, `scope` accepts `private` only. + +Body: + +```json +{ "name": "secrets", "scope": "private" } +``` + +Returns `201` with the reservation: + +```json +{ + "bucket_id": "…", + "bytes": "", + "digest": "…", + "state": "pending_policy" +} +``` + +Sign `bytes` locally with your service key, then call the finalize endpoint. The bucket stays in `pending_policy` and accepts no uploads until finalize succeeds. A name conflict returns `409`. A plan-limit breach returns `422`. See the [Quick Start](./quickstart) for the full signing flow. + +### List files in a space + +`GET /api/v1/spaces/{id}/files` + +Searches files across every bucket in the space. + +| Parameter | Location | Required | Description | +| --- | --- | --- | --- | +| `id` | path | Yes | Space UUID. | +| `limit` | query | No | Page size, 1 to 100. Default 20. | +| `cursor` | query | No | Cursor from a previous page. | +| `q` | query | No | Case-insensitive substring match on file name. | +| `sortField` | query | No | `name`, `date`, `size`, or `type`. Default `date`. | +| `sortOrder` | query | No | `asc` or `desc`. Default `desc`. | + +Returns `200` with `data` and a `pagination` object (`limit`, `hasMore`, `nextCursor`). + +## Buckets + +A bucket holds files inside a space. Private buckets store [Seal](/docs/data-security) ciphertext only. + +### Get a bucket + +`GET /api/v1/buckets/{id}` + +Returns `200` with the bucket under `data`: `id`, `space_id`, `name`, `oyster_bucket_name`, `visibility`, `seal_policy_id`, `storage_used`, `created_at`, and `updated_at`. + +### Update a bucket + +`PUT /api/v1/buckets/{id}` + +| Field | Required | Description | +| --- | --- | --- | +| `name` | Yes | New bucket name, 1 to 100 characters in length. | +| `sealPolicyId` | No | Seal policy ID. Pass `null` to clear. | +| `visibility` | No | Immutable in v1. Changing it returns `403`. | + +Returns `200` with the updated bucket (`id`, `name`, `visibility`, `updated_at`). A name conflict returns `409`. + +### Delete a bucket + +`DELETE /api/v1/buckets/{id}?confirm=true` + +Requires the `confirm=true` query parameter, and the bucket must be empty. Returns `204` with an `X-Deleted-Id` header. A missing confirmation, a validation error, or a non-empty bucket returns `400`. + +### Finalize a private bucket + +`POST /api/v1/buckets/{id}/finalize` + +Submits the signature over the reserved transaction bytes to activate a pending private bucket. + +Body: + +```json +{ "signature": "" } +``` + +Returns `200`: + +```json +{ "bucket_id": "…", "seal_policy_id": "…", "state": "active" } +``` + +`seal_policy_id` is the onchain bucket-policy object ID used by Seal for access checks. The bucket is now ready for uploads. + +### List files in a bucket + +`GET /api/v1/buckets/{id}/files` + +Takes the same `limit`, `cursor`, `q`, `sortField`, and `sortOrder` parameters as the space file listing. Returns `200` with `data` and `pagination`. + +### Upload a file + +`POST /api/v1/buckets/{id}/files` + +Uploads a file asynchronously using `multipart/form-data`. + +| Field | Required | Description | +| --- | --- | --- | +| `file` | Yes | The file bytes. For private buckets, upload the Seal-encrypted object. | +| `name` | No | File name, up to 255 characters in length. | +| `metadata` | No | JSON object string persisted with the file. Maximum 8 KiB. | + +Returns `202` with the file summary under `data`. Poll the status endpoint until the state is `completed`. + +:::info + +After finalize, the onchain access grant takes a few seconds to propagate to the access index. During this window, uploads fail with a `403` and the error code `mirror_missing_grant`. Retry every few seconds until the grant appears. + +::: + +Other responses include `409` (bucket not finalized or duplicate file name), `413` (payload too large), `422` (quota exceeded), and `429` (rate limited). + +## Files + +### Get file metadata + +`GET /api/v1/buckets/{id}/files/{fileId}` + +Returns `200` with the file summary under `data`: `id`, `bucket_id`, `name`, `blob_id`, `oyster_object_id`, `size`, `mime_type`, `status`, `is_private`, `metadata`, `created_at`, `updated_at`, and `deleted_at`. + +### Delete a file + +`DELETE /api/v1/buckets/{id}/files/{fileId}` + +Marks the file as `deleting` and enqueues a background job to remove the underlying blob. Returns `204` immediately. Storage is released after the worker confirms the delete. Repeated calls for the same file ID are deduplicated. + +### Get upload status + +`GET /api/v1/buckets/{id}/files/{fileId}/status` + +Polls the state of an in-flight upload after the upload endpoint returns `202`. + +Returns `200` with `data.state`, one of `queued`, `active`, `completed`, or `failed`. When the worker reports it, `data.progress` gives fractional progress from 0 to 1. When the state is `failed`, `data.error` includes a `code` and `message`. Once the job leaves the queue this endpoint returns `404`, which does not mean the upload failed. If you get a `404` after uploading, check the file metadata endpoint to confirm the final status before treating it as an error. + +### Download a file + +`GET /api/v1/buckets/{id}/files/{fileId}/download` + +Streams the raw file content with `Content-Type` and `Content-Disposition` headers. For a private bucket, this returns the Seal ciphertext, which you decrypt client-side. See the [Quick Start](./quickstart) for the decrypt flow. + +A file in `deleting` or `deleted` status returns `423`. An unavailable blob stream returns `500`. + +## Seal sponsorship + +These endpoints build and sponsor the onchain bucket-policy transactions that manage private access. Console sponsors the gas through [Enoki](https://docs.enoki.mystenlabs.com/), so your service key signs but does not need a token balance. They are typically driven through the SDK patterns shown in the [Quick Start](./quickstart). + +### Sponsor a transaction + +`POST /api/v1/seal/sponsor` + +Builds a sponsored bucket-policy transaction and returns the bytes to sign plus a digest to echo to the execute endpoint. The body's `kind` selects the operation: + +| `kind` | Purpose | Additional fields | +| --- | --- | --- | +| `bucket_group_create` | Create a bucket access group. | `bucketId` | +| `grant_bucket_access` | Grant access to recipients. | `groupIds`, `recipientAddress`, `scope` (`read` or `readwrite`) | +| `share_admin` | Add an admin to a group. | `groupId`, `member` | +| `unshare` | Remove a member from a group. | `groupId`, `member` | +| `unshare_bucket_access` | Revoke a service signer's access. | `groupId`, `serviceSignerAddress` | + +Returns `200` with `bytes` and `digest`. + +### Execute a sponsored transaction + +`POST /api/v1/seal/sponsor/{digest}/execute` + +Submits the signature over the sponsored bytes. Console broadcasts the transaction and returns the onchain digest. + +| Parameter | Location | Required | Description | +| --- | --- | --- | --- | +| `digest` | path | Yes | The sponsor digest from the sponsor endpoint. | + +Body: + +```json +{ "signature": "" } +``` + +Returns `200` with the onchain `digest`. An expired or unknown digest returns `404`. diff --git a/docs/content/console/auth.mdx b/docs/content/console/auth.mdx new file mode 100644 index 0000000000..20d97ccaf5 --- /dev/null +++ b/docs/content/console/auth.mdx @@ -0,0 +1,43 @@ +--- +title: Authentication and Accounts +description: How Walrus Console sign-in works with Google and Apple through Sui zkLogin, how your Pearl wallet is provisioned, and how accounts stay separate per identity. +keywords: [walrus console, sign in, zklogin, pearl wallet, apple, google, accounts] +--- + +Walrus Console uses the same kind of login you already know using Google or Apple; no wallet, no seed phrase, no crypto setup. Just sign in and start storing data. + +:::info + +Walrus Console is available on Mainnet through a closed, invite-only beta. It currently supports Google sign-in, and plans to support Apple soon. For the product model, see the [concepts and overview](./overview). + +::: + +## How sign-in works + +Console uses Sui [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin). You authenticate with an identity provider you already have, and Console derives a [Sui address](https://docs.sui.io/concepts/cryptography/transaction-auth/keys-addresses) from that identity without exposing your provider account onchain and without asking you to manage a private key. + +## Sign in with Google + +1. Visit the [Walrus Console app](https://testnet.harbor.walrus.xyz/). +2. Choose **Continue with Google** and complete the Google sign-in. +3. Console provisions your account and a [Personal Space](./overview#core-concepts-spaces-buckets-and-files), then takes you to the dashboard. + +Apple sign-in joins soon and works the same way, deriving your Sui address from your Apple identity. It accepts Apple's private email relay, so you can use the **Hide My Email** option. + +## Your Pearl wallet + +On first sign-in, Console silently provisions a Pearl wallet, the embedded wallet Console manages on your behalf, for your derived Sui address. This wallet holds the storage resources your data uses. Console manages the wallet and paying for transactions for you, so you do not need to fund it or sign transactions to get started. More advanced workflows require explicit signing steps. + +## Accounts stay separate per identity + +:::warning + +Each identity provider maps to a separate Console account and a separate Sui address. If you sign in with Google and later sign in with Apple, you get two independent accounts, not one merged account. Data stored under one is not visible under the other. + +::: + +Console shows an account-separation notice the first time you sign in so this is clear before you store anything. Choose one provider and use it consistently. Linking multiple providers to a single account is planned for a later release. + +## Wallet sign-in + +Signing in with an existing Sui wallet is planned for a later release. For now, sign in with Google or Apple through zkLogin. If you hold assets in a personal Sui wallet, note that data you store through Console lives under your zkLogin-derived address, which is separate from a wallet you connect later. diff --git a/docs/content/console/overview.mdx b/docs/content/console/overview.mdx new file mode 100644 index 0000000000..7e75d73bb6 --- /dev/null +++ b/docs/content/console/overview.mdx @@ -0,0 +1,91 @@ +--- +title: What is Walrus Console? +description: What Walrus Console is, how it relates to Walrus, and the core concepts you work with (spaces, buckets, files, and asset types). +keywords: [walrus console, spaces, buckets, files, asset types, zklogin, api keys, seal] +--- + +Walrus Console is the developer-first interface for interacting with the Walrus network. It gives you a hosted place to store, manage, and serve data without running your own Walrus client or handling wallets and tokens directly. You sign in, get an API key, and upload your first file in minutes, then manage everything from one place. + +:::info + +Walrus Console is available on Mainnet through a closed, invite-only beta. Some capabilities described in this documentation are planned to ship at general availability or later, and the structure might change before GA. Availability is called out per feature below. + +::: + +## How Console relates to Walrus + +Walrus is the underlying protocol: a decentralized network that stores data as blobs, coordinated onchain through Sui. You can use Walrus directly through the CLI and SDKs, which give you low-level control over blobs, storage epochs, and payments. + +Walrus Console sits on top of that protocol as a managed developer surface. It handles the parts that are otherwise manual: account and wallet provisioning, storage payment, metadata, and a dashboard and API for organizing your data. Use the CLI and SDKs when you want direct protocol access, and use Console when you want a hosted, managed experience. + +## Core concepts: spaces, buckets, and files + +Console organizes your data in three levels. + +A **space** is the top-level container tied to your account. Console creates a **Personal Space** for you automatically when you sign up. A space tracks how much storage you have used against your storage cap. Team Spaces, which let a group share storage under one managed API key, arrive at general availability. + +A **bucket** is a named container inside a space that holds files. Every bucket has a visibility setting. In the current beta, all buckets are private and encrypted with [Seal](/docs/data-security); public buckets are planned for a later release. + +A **file** is an individual object stored inside a bucket. Uploads are asynchronous: you upload a file, then poll its status until Console confirms it is stored on Walrus. Each file can carry metadata you define, which you can use later to search and organize your data. + +## Encryption and privacy + +:::warning + +On Walrus, stored blobs are public by default. Anyone who has a blob ID can read the bytes. Do not rely on obscurity for sensitive data. + +::: + +Console private buckets solve this by encrypting every file client-side with [Seal](/docs/data-security) before upload. Console stores ciphertext only and never sees your plaintext or your decryption keys. Setting up a private bucket uses a short reserve, sign, and finalize handshake that provisions the bucket's Seal access policy onchain. Encryption on upload and decryption on download both happen on your machine. + +## Asset types + +Console is built around a single navigation shell that treats your data as typed assets. Files, memory, and datasets are all first-class asset types managed the same way. + +**Files** are general-purpose objects you upload and retrieve. This is the asset type available in the current beta. + +**Memory** refers to Walrus Memory namespaces, the portable memory layer for AI agents. At GA you can browse, search, and manage your agent memory and renew its storage directly in Console, without touching the SDK. + +**Datasets** are published collections with metadata and an access model. You can set a dataset to public, time-gated, or perpetually gated access, and later list it on the Walrus Marketplace. + +## Accounts and sign-in + +You sign in through Google, with Apple joining soon, using Sui [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin) capabilities. Console derives a Sui address from your identity and silently provisions a Pearl wallet (the embedded wallet Console manages for you), so you do not manage private keys or hold tokens to get started. + +Identities from different providers map to separate accounts. Console shows an account-separation notice the first time you sign in so it is clear which identity owns which data. + +You own your data. Console does not migrate data you previously stored on Walrus outside the product; you re-upload it. + +## API keys and roles + +You mint API keys in the Console web app, choosing a `read_write` or `read_only` role for each. Console shows the full key (prefixed `hbr_`) once, at creation, and cannot recover it afterward, so store it like a cloud secret access key. For what each role can do, see the [API reference](./api-reference). + +When you create an encrypted-capable key, Console also returns a service private key (prefixed `suiprivkey1`). You keep this locally and use it to sign the transaction that finalizes a private bucket and to authenticate decrypt sessions with Seal. It does not need a token balance. + +### Connect AI clients with the MCP server + +Console publishes an open-source [MCP](https://modelcontextprotocol.io/) server that exposes file and bucket operations as tools for AI clients. You connect Claude Code, Cursor, or any MCP-compliant client using your existing API key, with no separate credential. This is available in beta. + +## Storage, epochs, and renewal + +Walrus storage is time-bound. You pay to store data for a number of storage epochs, and data expires when its storage runs out. Console manages epochs and payment for you rather than asking you to track them by hand. + +After GA, storage renews automatically for wallets that stayed active, meaning at least one Walrus transaction within a recent activity window. Active developers keep their data without manual renewal, and dormant accounts expire naturally. + +## Billing and the free tier + +Console keeps a perpetual free tier so new developers are not paywalled. A free storage cap, tentatively 5 GB, is planned pending analysis of real Mainnet usage. Usage-based billing for reads and egress, along with a paid top-up path, follows at and after GA. Console manages WAL token handling on your behalf. + +## What is available in beta + +Capabilities roll out in phases. The current beta is a subset of the full product. + +| Capability | Availability | +| --- | --- | +| Google sign-in, Personal Space | Beta | +| Apple sign-in | Soon | +| Private, Seal-encrypted buckets and file upload or download | Beta | +| API keys and the MCP server | Beta | +| Memory and datasets as asset types | GA | +| Mainnet billing and free tier | GA | +| Auto-renewal, existing-blob discovery, Team Spaces, Marketplace sync | After GA | diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx new file mode 100644 index 0000000000..e34eb6c3bf --- /dev/null +++ b/docs/content/console/quickstart.mdx @@ -0,0 +1,163 @@ +--- +title: Quick Start +description: Sign up for Walrus Console, mint an API key, then create a Seal-encrypted bucket and upload and download your first file. +keywords: [walrus console, quickstart, api key, seal, encrypted bucket, upload] +--- + +This quickstart takes you from sign-up to a working encrypted upload: create an account, mint an API key, then create a Seal-encrypted bucket and upload, download, and decrypt a file. + +:::info + +The Console developer API is in alpha and currently available on Testnet only, hosted under the Harbor name at `api.testnet.harbor.walrus.xyz`. Endpoint shapes might change before it reaches Mainnet at GA. In alpha, all bucket creation goes through the private, Seal-encrypted flow; public bucket creation is disabled at the API boundary. For the full endpoint surface, see the [API reference](./api-reference); for the product model, see the [concepts and overview](./overview). + +::: + +## Prerequisites + +
+ + + +- [x] A Google account for sign-in. +- [x] Node.js with [`@mysten/sui`](https://www.npmjs.com/package/@mysten/sui) and [`@mysten/seal`](https://www.npmjs.com/package/@mysten/seal) installed, for the signing and encryption steps. + + + +
+ +Every request below carries your API key as a bearer token: + +```http +Authorization: Bearer hbr_… +``` + +:::info + +The code in this guide is pulled directly from the runnable [`walrus-harbor-quickstart`](https://github.com/MystenLabs/walrus-harbor-quickstart) example, so the package IDs and Seal key-server IDs stay current with the source. Clone it and run the `pnpm` scripts to try the flow end to end. + +::: + +## Sign up and create an API key + +1. Visit the [Walrus Console app](https://testnet.harbor.walrus.xyz/) and sign in with Google. zkLogin provisions your account and a Personal Space automatically. +2. Open **Settings → API Keys → New API key**, give it a name, pick role **`read_write`**, and tick **Create** so the key is encryption-capable. Submit. +3. On the reveal screen, copy both secrets. Console shows them once and cannot recover them afterward: + - `hbr_…`, the API key you send as `Authorization: Bearer …` on every request. + - `suiprivkey1…`, the service private key: an Ed25519 secret in Sui keytool format, bound to this API key. Console stores only the derived public address, and it does not need a token balance. You use it to sign the finalize transaction and to authenticate decrypt sessions with Seal. + + Store both like cloud secret access keys. Paste them into your `.env` as your bearer token and `HARBOR_SERVICE_PRIVKEY`, or into Postman. + +A `read_only` key (listing, status, and download only) is also available for downstream consumers that should not change your data; every write endpoint returns `403` with code `read_only_api_key` for those keys. The encrypted flow below needs the `read_write` encryption-capable key created above. + +## Create an encrypted bucket and upload a file + +Private buckets are encrypted client-side, so Console stores ciphertext only and never sees your plaintext or decryption material. With your key in hand, creation goes through a reserve, sign, and finalize handshake, then a local encrypt on upload and decrypt on download: + +``` +get space → reserve → sign → finalize → encrypt → upload → poll → download → decrypt +``` + +### Step 1: Get your space ID + +```http +GET /api/v1/spaces +``` + +The response's `data[]` array holds your spaces. Copy the `id` of the Personal Space created at sign-up. + +### Step 2: Reserve the bucket + +```http +POST /api/v1/spaces/{spaceId}/buckets +Content-Type: application/json + +{ "name": "secrets", "scope": "private" } +``` + +Response (`201`): + +```json +{ + "bucket_id": "…", + "bytes": "", + "digest": "…", + "state": "pending_policy" +} +``` + +`bytes` is the sponsored Sui transaction that creates the bucket's [Seal](/docs/data-security) access policy, with your service key's address as the sender. Console has already attached the gas sponsor's signature, so your service key only needs to add its own. `digest` is the sponsor digest, which Console uses at finalize to look up the sponsored transaction. The bucket stays in `pending_policy` and accepts no uploads until finalize succeeds. + +### The example code + +The signing, encryption, and decryption steps below come from the runnable [`walrus-harbor-quickstart`](https://github.com/MystenLabs/walrus-harbor-quickstart) example. Two files hold everything: `config.ts` pins the Testnet package and Seal key-server IDs, and `lib/seal.ts` wraps the signing and Seal encrypt/decrypt helpers. Clone the repo, copy `app/.env.example` to `app/.env`, and set `HARBOR_SERVICE_PRIVKEY` to your service private key. + + + + + +### Step 3: Sign the bytes with the service key + +Sign the reserved `bytes` with your service key. `sign-reserve.ts` loads the key with `loadKeypair` and calls `signReserveBytes`, returning the base64 signature you pass to finalize. Run it with `pnpm sign-reserve `: + + + +### Step 4: Finalize + +```http +POST /api/v1/buckets/{bucketId}/finalize +Content-Type: application/json + +{ "signature": "" } +``` + +Console combines your signature with the gas-sponsor signature and broadcasts the transaction. Response (`200`): + +```json +{ "bucket_id": "…", "seal_policy_id": "…", "state": "active" } +``` + +`seal_policy_id` is the onchain bucket-policy object ID that Seal uses for access checks. The bucket is now usable. + +### Step 5: Encrypt the file with Seal + +Encrypt locally against the `seal_policy_id` from finalize. `encryptBytes` (in `lib/seal.ts`) derives a per-file Seal identity and encrypts against the bucket policy using `ORIGINAL_PACKAGE_ID` from `config.ts`. Seal pins identity derivation to the original package ID, so this value must stay fixed across package upgrades, otherwise an upgrade would invalidate every previously encrypted blob's key. `encrypt-file.ts` wires it up; run it with `pnpm encrypt-file `: + + + +It writes `.enc`, the byte stream you upload next. + +### Step 6: Upload + +```http +POST /api/v1/buckets/{bucketId}/files +Content-Type: multipart/form-data + +file=@ +``` + +Just after finalize, the onchain access grant needs a few seconds to propagate into Console's access index. Until it does, this endpoint returns `403` with code `mirror_missing_grant`. Retry every few seconds; usually 20 attempts is plenty in practice. Once the grant mirrors, the response is `202` with `data.id`. + +### Step 7: Poll status + +```http +GET /api/v1/buckets/{bucketId}/files/{fileId}/status +``` + +Returns `data.state`, one of `queued`, `active`, `completed`, or `failed`. Poll every second or two until the state is `completed`. Completion is typically under 30 seconds on Testnet. + +### Step 8: Download and decrypt + +```http +GET /api/v1/buckets/{bucketId}/files/{fileId}/download +``` + +The response is the raw Seal ciphertext. `decryptBytes` (in `lib/seal.ts`) builds the `seal_approve` access-check transaction, signs it with a short-lived session key, and decrypts client-side. `decrypt-file.ts` runs the download-to-plaintext round trip and checks the result against the original; run it with `pnpm decrypt-file `: + + + +Decryption is fully client-side and never touches Console's backend. + +## Get help + +- Ask the team and other developers in the [Walrus Discord](https://discord.gg/walrusprotocol). +- File an issue on the [`walrus-harbor-quickstart`](https://github.com/MystenLabs/walrus-harbor-quickstart/issues) example repo. Include the endpoint and method, the HTTP status, and the `code` field from any error response. diff --git a/docs/content/console/storage-epochs.mdx b/docs/content/console/storage-epochs.mdx new file mode 100644 index 0000000000..56d71be154 --- /dev/null +++ b/docs/content/console/storage-epochs.mdx @@ -0,0 +1,43 @@ +--- +title: Storage, Epochs, and Renewal +description: How Walrus Console measures storage in epochs, how automatic renewal keeps active accounts from losing data, and how dormant accounts expire. +keywords: [walrus console, storage, epochs, renewal, auto-renewal, expiry] +--- + +Walrus storage is time-bound. Your data stays available for a set amount of storage, measured in epochs, and expires when that storage runs out. Walrus Console tracks and pays for this so you do not manage epochs by hand, and it is built to renew storage automatically for accounts that stay active. + +:::info + +Storage and epochs work today. Automatic renewal ships at and after GA. This page explains both so you understand how your data's lifetime works and how to avoid losing it. For related cost details, see the [concepts and overview](./overview). + +::: + +## How storage epochs work + +Walrus measures storage in epochs, which are fixed periods defined by the Walrus network. When you store a file, you reserve storage for a number of epochs. While that storage is funded, the network keeps your data available. When it runs out and you do not renew it, the data expires and is no longer retrievable. + +Console handles the underlying payment and epoch accounting for you through your [Pearl wallet](./auth#your-pearl-wallet). You store a file, and Console reserves and funds its storage. You do not buy epochs or sign renewal transactions yourself. + +## Automatic renewal + +To keep active developers from losing data because they forgot to renew, Console renews storage automatically for wallets that stayed active. A wallet counts as active when it has at least one Walrus transaction within a recent activity window, planned as 14 days. Renewal runs on your behalf, so data you keep using stays available without any manual step. + +Automatic renewal ships at and after GA, alongside Mainnet billing. + +## Dormant accounts and expiry + +:::warning + +If a wallet goes dormant, its storage lapses and its data expires. Automatic renewal covers active wallets only. If you store data you want to keep, make sure the account stays active within the activity window, or plan to re-upload. + +::: + +Expiring dormant storage is deliberate. It keeps the network from paying indefinitely to store data for accounts that are no longer in use. An account returns to active status as soon as it records a new Walrus transaction within the window. + +## Checking storage status + +Your dashboard shows how much storage each space is using and surfaces upcoming expiry so you can act before data lapses. Storage used and your storage cap also appear per space when you list spaces through the API. + +## Storage and the free tier + +Console keeps a perpetual free tier with a storage cap, tentatively 5 GB, so new developers are not paywalled. Usage-based billing for reads and egress, along with a paid path, follows at and after GA. Console manages WAL token handling for storage on your behalf. For the model behind spaces and storage, see the [concepts and overview](./overview). diff --git a/docs/site/sidebars.js b/docs/site/sidebars.js index 2d304aa2be..7b4e36b0a0 100644 --- a/docs/site/sidebars.js +++ b/docs/site/sidebars.js @@ -81,6 +81,21 @@ const sidebars = { 'http-api/quilt-http-apis', ], }, + { + type: 'category', + label: 'Walrus Console', + collapsed: true, + link: { + type: 'doc', + id: 'console/overview', + }, + items: [ + 'console/auth', + 'console/quickstart', + 'console/storage-epochs', + 'console/api-reference', + ], + }, { type: 'category', label: 'Troubleshooting',