From f5ce251eee5bdf1a22890c4b1164eda4732226e0 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Mon, 13 Jul 2026 15:25:11 -0400 Subject: [PATCH 1/9] docs: add Walrus Console beta documentation --- docs/content/console/_category_.json | 10 + docs/content/console/api-reference.mdx | 344 ++++++++++++++++++++++++ docs/content/console/auth.mdx | 78 ++++++ docs/content/console/overview.mdx | 152 +++++++++++ docs/content/console/quickstart.mdx | 270 +++++++++++++++++++ docs/content/console/storage-epochs.mdx | 70 +++++ 6 files changed, 924 insertions(+) create mode 100644 docs/content/console/_category_.json create mode 100644 docs/content/console/api-reference.mdx create mode 100644 docs/content/console/auth.mdx create mode 100644 docs/content/console/overview.mdx create mode 100644 docs/content/console/quickstart.mdx create mode 100644 docs/content/console/storage-epochs.mdx diff --git a/docs/content/console/_category_.json b/docs/content/console/_category_.json new file mode 100644 index 0000000000..2f213d280a --- /dev/null +++ b/docs/content/console/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Walrus Console", + "position": 5, + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "description": "Store, manage, and serve data on Walrus through a hosted developer console." + } +} diff --git a/docs/content/console/api-reference.mdx b/docs/content/console/api-reference.mdx new file mode 100644 index 0000000000..20873156f8 --- /dev/null +++ b/docs/content/console/api-reference.mdx @@ -0,0 +1,344 @@ +--- +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 sponsorship endpoints that back +private-bucket access control. + +:::note + +This documents the current Testnet surface, hosted at `https://api.testnet.harbor.walrus.xyz`. +The API is in alpha and endpoint shapes may change before Mainnet GA. For a guided walkthrough +of the full encrypted flow, start with the [quickstart](./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_… +``` + +You mint keys in the Console web app. 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 more results, the cursor field is `null`. Bucket lists return +the cursor as `next_cursor`; file lists return it under `pagination.nextCursor`. + +### Asynchronous operations + +Upload and delete 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 a consistent shape: + +```json +{ "error": "Human-readable message", "code": "machine_readable_code" } +``` + +The `code` field is present when a machine-readable value applies. Common codes: + +| 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 | In | 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 | In | 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 [quickstart](./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 | In | 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 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. | +| `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 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. | +| `metadata` | No | JSON object string persisted with the file. Maximum 8 KB. | + +Returns `202` with the file summary under `data`. Poll the status endpoint until the state is +`completed`. + +:::note + +Just after finalize, the onchain access grant needs a few seconds to mirror into the access +index. Until it does, upload returns `403` with code `mirror_missing_grant`. Retry every few +seconds. + +::: + +Other responses: `409` when the bucket is not finalized or a file name is duplicated, `413` +when the payload is too large, `422` when a quota is exceeded, and `429` when 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`; confirm completion through the file metadata endpoint after that. + +### 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 +[quickstart](./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, so your service key signs but does not need a +token balance. They are typically driven through the SDK patterns shown in the +[quickstart](./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 | In | 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`. + +## Next steps + +- Work through the [quickstart](./quickstart) to see these endpoints in a full encrypted flow. +- Review the [concepts and overview](./overview) for the space, bucket, and file model. diff --git a/docs/content/console/auth.mdx b/docs/content/console/auth.mdx new file mode 100644 index 0000000000..4ddeb60225 --- /dev/null +++ b/docs/content/console/auth.mdx @@ -0,0 +1,78 @@ +--- +title: Sign-in 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 signs you in with a familiar identity provider and provisions everything you +need to start storing data. You do not create a wallet, manage a seed phrase, or hold tokens +to sign up. + +:::note + +Walrus Console is in an invited beta on Mainnet. Google sign-in is available today, and Apple +sign-in joins at beta. For what happens after you sign in, see the +[quickstart](./quickstart). For the product model, see the [concepts and overview](./overview). + +::: + +## How sign-in works + +Console uses Sui zkLogin. You authenticate with an identity provider you already have, and +Console derives a Sui address from that identity without exposing your provider account onchain +and without asking you to manage a private key. The result is a standard Sui address that owns +your data, backed by a sign-in you already know how to use. + +## Sign in with Google + +1. Visit [testnet.harbor.walrus.xyz](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, then takes you to the dashboard. + +## Sign in with Apple + +Apple sign-in joins at beta and works the same way. Console derives your Sui address from your +Apple identity and provisions your account and Personal Space. + +Console accepts Apple's private email relay, so you can use Apple's **Hide My Email** option. +Console treats the relay address as your account email and delivers any account email through +it. + +## Your Pearl wallet + +On first sign-in, Console silently provisions a Pearl wallet for your derived Sui address. This +wallet holds the storage resources your data uses. Console sponsors gas and manages the wallet +for you, so you do not fund it or sign transactions to get started. The exceptions are the +explicit signing steps in the encrypted-bucket flow, where you sign with your own service key. + +## 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. + +## After you sign in + +You land on the dashboard with a Personal Space ready to use. The next step is to mint an API +key and make your first request. Follow the [quickstart](./quickstart) to create an encrypted +bucket and upload a file. + +## Next steps + +- Continue to the [quickstart](./quickstart) to mint a key and upload your first file. +- See the [concepts and overview](./overview) for spaces, buckets, and asset types. diff --git a/docs/content/console/overview.mdx b/docs/content/console/overview.mdx new file mode 100644 index 0000000000..90bd2d01e3 --- /dev/null +++ b/docs/content/console/overview.mdx @@ -0,0 +1,152 @@ +--- +title: Concepts and overview +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 [Walrus](/), the decentralized +storage 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. + +:::note + +Walrus Console is in an invited beta on Mainnet. Some capabilities described here ship at +general availability (GA) or later, and the surface may change before GA. Availability is +called out per feature below. The current Testnet preview and its API are hosted under the +Harbor name (for example, `testnet.harbor.walrus.xyz`); Walrus Console is the product name. + +::: + +## How Console relates to Walrus + +Walrus is the underlying protocol: a decentralized network that stores data as blobs, +coordinated onchain through Sui. You can already 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 GA. + +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; +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 use later to search and organize your data. + +## Asset types + +Console is built around a single navigation shell that treats your data as typed assets. At +GA, 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. At GA 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 with Gmail today, with Apple joining at beta, both through Sui zkLogin. Console +derives a Sui address from your identity and silently provisions a Pearl wallet, 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. After GA, Console will surface blobs already associated with your +address when you first sign in. + +## API keys and roles + +You mint API keys in the Console web app. Each key carries 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`. Use this role for a downstream consumer that + should read your data but never change it. + +Console shows the full key (prefixed `hbr_`) once, at creation, and cannot recover it +afterward. Store it like a cloud secret access key. + +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, since Console sponsors the gas. + +### Connect AI clients with the MCP server + +Console publishes an open-source MCP 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. + +## 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](/) 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. + +## 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 | +| --- | --- | +| Gmail and Apple sign-in, Personal Space | Beta | +| 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 | + +## Next steps + +- Follow the [quickstart](./quickstart) to sign up, create an encrypted bucket, and upload + your first file. +- See the [API reference](./api-reference) for the full endpoint surface. diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx new file mode 100644 index 0000000000..9f417f9762 --- /dev/null +++ b/docs/content/console/quickstart.mdx @@ -0,0 +1,270 @@ +--- +title: Quickstart +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. + +:::note + +Walrus Console is in alpha on Testnet, hosted under the Harbor name at +`testnet.harbor.walrus.xyz`, with its API at `https://api.testnet.harbor.walrus.xyz`. Endpoint +shapes may change before Mainnet 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 + +- A Google account for sign-in. +- 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_… +``` + +## Sign up and create an API key + +1. Visit [testnet.harbor.walrus.xyz](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 a role, and submit. + - `read_write` is required for any state change: create and delete buckets, upload, rename, + and delete files, and finalize private buckets. + - `read_only` covers listing, status, and download only. Every write endpoint returns `403` + with code `read_only_api_key` for these keys. Use this role when you hand a key to a + downstream consumer that should not change your data. +3. On the reveal screen, copy the `hbr_…` key. Console shows it once and cannot recover it + afterward. Store it like a cloud secret access key. + +Pick `read_write` if you intend to follow the encrypted flow below end to end. + +## 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. Creation goes through a reserve, sign, and finalize +handshake, with a one-time service-key setup beforehand and a local decrypt step on download: + +``` +service key setup → get space → reserve → sign → finalize → +encrypt → upload → poll → download → decrypt +``` + +:::warning + +You end this section holding two secrets: an `hbr_…` API key, sent as +`Authorization: Bearer …` on every request, and a `suiprivkey1…` service private key, kept +locally and used to sign the finalize transaction and to authenticate decrypt sessions with +Seal. Console shows both once. Store them like cloud access keys. + +::: + +### Step 1: Create an encrypted-capable API key + +In **Settings → API Keys → New API key**, pick role `read_write` and tick **Create**. The +reveal screen now exposes two secrets: + +- `hbr_…`, the API key. +- `suiprivkey1…`, the service private key: an Ed25519 secret in Sui keytool format. It is bound + to this API key, and Console stores only the derived public address. It does not need a token + balance, since Console sponsors gas. + +Paste them into your `.env` as `HARBOR_SERVICE_PRIVKEY` and your bearer token, or into Postman. + +### Step 2: 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 3: 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 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. + +### Step 4: Sign the bytes with the service key + +`@mysten/sui` handles the Bech32 decode and the Sui signature envelope for you: + +```ts +import { decodeSuiPrivateKey } from '@mysten/sui/cryptography'; +import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'; +import { fromBase64 } from '@mysten/sui/utils'; + +const { secretKey } = decodeSuiPrivateKey(process.env.HARBOR_SERVICE_PRIVKEY); +const keypair = Ed25519Keypair.fromSecretKey(secretKey); +const { signature } = await keypair.signTransaction(fromBase64(bytes)); +``` + +### Step 5: 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 6: Encrypt the file with Seal + +Encrypt locally against the `seal_policy_id` returned by finalize. `ORIGINAL` here means the +original id of the upgradeable package. Seal pins identity derivation to the original package +id, so encryption must use this value even after the package upgrades, otherwise an upgrade +would invalidate every previously encrypted blob's key. + +```ts +import { SealClient } from '@mysten/seal'; +import { SuiGrpcClient } from '@mysten/sui/grpc'; +import { bcs } from '@mysten/sui/bcs'; + +const HARBOR_ORIGINAL_PACKAGE_ID = + '0x8b2429358e9b0f005b69fe8ad3cbd1268ad87f35047a21612e082c64824faf8d'; +const SEAL_KEY_SERVER_OBJECT_IDS = [ + '0x6068c0acb197dddbacd4746a9de7f025b2ed5a5b6c1b1ab44dade4426d141da2', + '0x164ac3d2b3b8694b8181c13f671950004765c23f270321a45fdd04d40cccf0f2', + '0x9c949e53c36ab7a9c484ed9e8b43267a77d4b8d70e79aa6b39042e3d4c434105', +]; + +const sui = new SuiGrpcClient({ + network: 'testnet', + baseUrl: 'https://fullnode.testnet.sui.io:443', +}); +const seal = new SealClient({ + suiClient: sui, + serverConfigs: SEAL_KEY_SERVER_OBJECT_IDS.map((objectId) => ({ objectId, weight: 1 })), + verifyKeyServers: false, +}); + +// Each file's Seal id = (bucket policy id, 32 random bytes). +const SealIdentity = bcs.struct('SealIdentity', { + policyObjectId: bcs.Address, + nonce: bcs.fixedArray(32, bcs.u8()), +}); +const nonce = Array.from(crypto.getRandomValues(new Uint8Array(32))); +const id = SealIdentity.serialize({ policyObjectId: sealPolicyId, nonce }).toHex(); + +const { encryptedObject } = await seal.encrypt({ + threshold: 2, + packageId: HARBOR_ORIGINAL_PACKAGE_ID, + id, + data: plaintextBytes, // Uint8Array +}); +``` + +`encryptedObject` is the byte stream you upload next. + +### Step 7: 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 mirror into Console's +access index. Until it does, this endpoint returns `403` with code `mirror_missing_grant`. +Retry every few seconds; 20 attempts is plenty in practice. Once the grant mirrors, the +response is `202` with `data.id`. + +### Step 8: 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 9: Download and decrypt + +```http +GET /api/v1/buckets/{bucketId}/files/{fileId}/download +``` + +The response is the raw Seal ciphertext. Reusing the `sui` and `seal` clients from step 6, +decrypt by building the bucket's access-check transaction, signing it with a session key, and +passing both the ciphertext and the transaction to `SealClient.decrypt`: + +```ts +import { EncryptedObject, SessionKey } from '@mysten/seal'; +import { Transaction } from '@mysten/sui/transactions'; +import { fromHex } from '@mysten/sui/utils'; + +// Latest bucket-policy package, host of the seal_approve move call. +const HARBOR_LATEST_PACKAGE_ID = + '0xc11d875481544e9b6c616f7d6704266e1633b4034eab7ed76626dc25ebfcd506'; + +// ciphertext = bytes from GET /download +const parsed = EncryptedObject.parse(ciphertext); +const idBytes = fromHex(parsed.id.startsWith('0x') ? parsed.id : '0x' + parsed.id); + +// 1. Build the access-check transaction (transaction kind only, never broadcast). +const tx = new Transaction(); +tx.moveCall({ + target: `${HARBOR_LATEST_PACKAGE_ID}::bucket_policy::seal_approve`, + arguments: [tx.pure.vector('u8', idBytes), tx.object(sealPolicyId)], +}); +const txBytes = await tx.build({ client: sui, onlyTransactionKind: true }); + +// 2. A session key lets the Seal key servers verify the caller without re-signing per request. +const sessionKey = await SessionKey.create({ + address: keypair.toSuiAddress(), + packageId: HARBOR_ORIGINAL_PACKAGE_ID, + ttlMin: 10, + suiClient: sui, + signer: keypair, +}); + +// 3. Decrypt. SealClient fetches threshold key shares and reconstructs the key locally. +const plaintext = await seal.decrypt({ data: ciphertext, sessionKey, txBytes }); +``` + +Decryption is fully client-side and never touches Console's backend. + +## Get help + +Open an issue at [github.com/MystenLabs/harbor/issues](https://github.com/MystenLabs/harbor/issues) +with the `developer-docs` label. 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..f7f37159ad --- /dev/null +++ b/docs/content/console/storage-epochs.mdx @@ -0,0 +1,70 @@ +--- +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. + +:::note + +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 is not renewed, the data expires and is +no longer retrievable. + +Console handles the underlying payment and epoch accounting for you through 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 is allowed to lapse 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). + +## Next steps + +- Follow the [quickstart](./quickstart) to store your first file. +- See the [concepts and overview](./overview) for spaces, buckets, and asset types. From 9fd89720e06beb0cf47917af3716b5cd49ad7260 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Mon, 13 Jul 2026 15:39:16 -0400 Subject: [PATCH 2/9] docs: add Walrus Console to sidebar --- docs/content/console/_category_.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 docs/content/console/_category_.json diff --git a/docs/content/console/_category_.json b/docs/content/console/_category_.json deleted file mode 100644 index 2f213d280a..0000000000 --- a/docs/content/console/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Walrus Console", - "position": 5, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Store, manage, and serve data on Walrus through a hosted developer console." - } -} From fc1d92f3a3b2208981e0d8bd84cb9efee706c831 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Mon, 13 Jul 2026 15:41:41 -0400 Subject: [PATCH 3/9] docs: add Walrus Console to sidebar --- docs/site/sidebars.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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', From 4bbe6c01aec8880c6b2ef190d487ee098a4761b1 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Tue, 14 Jul 2026 21:09:14 +0000 Subject: [PATCH 4/9] docs: address Console docs review feedback Apply review feedback on the Walrus Console beta docs: - Standardize launch framing: product pages describe a closed, invite-only Mainnet beta; the API reference and quickstart keep the API as alpha / Testnet-only, resolving the alpha-vs-beta inconsistency across pages. - Title-case all page titles; use "Quick Start" consistently. - Switch all :::note admonitions to :::info. - Add Seal (/docs/data-security), Sui zkLogin, Sui address, Enoki, MCP, and Discord links; replace placeholder "/" links. - Capitalize "ID" in prose and clarify the API parameter tables' "In" column as "Location". - Move the encryption/privacy warning higher on the overview page and trim the duplicated API-key role details. - Condense the not-yet-available Apple sign-in section. - Apply reviewer inline suggestions (error shape, async wording, KiB, retry guidance, character-length clarifications) and remove redundant manual "Next steps" sections in favor of Docusaurus pagination. - Add a canonical-source pointer for the quickstart snippets. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GQ8Q5FL8je9mpXv93ff23R --- docs/content/console/api-reference.mdx | 133 ++++++++---------------- docs/content/console/auth.mdx | 57 ++-------- docs/content/console/overview.mdx | 128 ++++++----------------- docs/content/console/quickstart.mdx | 96 ++++++----------- docs/content/console/storage-epochs.mdx | 49 ++------- 5 files changed, 129 insertions(+), 334 deletions(-) diff --git a/docs/content/console/api-reference.mdx b/docs/content/console/api-reference.mdx index 20873156f8..54aa82a09e 100644 --- a/docs/content/console/api-reference.mdx +++ b/docs/content/console/api-reference.mdx @@ -1,19 +1,14 @@ --- -title: API reference +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 sponsorship endpoints that back -private-bucket access control. +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. -:::note +:::info -This documents the current Testnet surface, hosted at `https://api.testnet.harbor.walrus.xyz`. -The API is in alpha and endpoint shapes may change before Mainnet GA. For a guided walkthrough -of the full encrypted flow, start with the [quickstart](./quickstart). For the product model -behind these endpoints, see the [concepts and overview](./overview). +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 may 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). ::: @@ -25,15 +20,12 @@ Every request carries an API key as a bearer token: Authorization: Bearer hbr_… ``` -You mint keys in the Console web app. Each key has one of two roles: +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`. +- `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`. +A request with no valid key returns `401`. A key without access to the target resource returns `403`. ## Base URL and conventions @@ -43,30 +35,25 @@ 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. +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 more results, the cursor field is `null`. Bucket lists return -the cursor as `next_cursor`; file lists return it under `pagination.nextCursor`. +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 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. +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 a consistent shape: +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 codes: +The `code` field is present when a machine-readable value applies. Common error codes include: | Code | Meaning | | --- | --- | @@ -99,19 +86,17 @@ A space is the top-level container for your data. Console creates a Personal Spa Lists all spaces accessible to the authenticated user. -| Parameter | In | Required | Description | +| 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`. +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 | In | Required | Description | +| Parameter | Location | Required | Description | | --- | --- | --- | --- | | `id` | path | Yes | Space UUID. | | `limit` | query | No | Page size, 1 to 1000. Default 100. | @@ -127,8 +112,7 @@ Returns `200` with `buckets` and `next_cursor`. `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. +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: @@ -147,10 +131,7 @@ Returns `201` with the reservation: } ``` -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 [quickstart](./quickstart) for the full -signing flow. +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 @@ -158,7 +139,7 @@ signing flow. Searches files across every bucket in the space. -| Parameter | In | Required | Description | +| Parameter | Location | Required | Description | | --- | --- | --- | --- | | `id` | path | Yes | Space UUID. | | `limit` | query | No | Page size, 1 to 100. Default 20. | @@ -171,14 +152,13 @@ Returns `200` with `data` and a `pagination` object (`limit`, `hasMore`, `nextCu ## Buckets -A bucket holds files inside a space. Private buckets store Seal ciphertext only. +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`. +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 @@ -186,20 +166,17 @@ Returns `200` with the bucket under `data`: `id`, `space_id`, `name`, `oyster_bu | Field | Required | Description | | --- | --- | --- | -| `name` | Yes | New bucket name, 1 to 100 characters. | -| `sealPolicyId` | No | Seal policy id. Pass `null` to clear. | +| `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`. +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`. +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 @@ -219,15 +196,13 @@ Returns `200`: { "bucket_id": "…", "seal_policy_id": "…", "state": "active" } ``` -`seal_policy_id` is the onchain bucket-policy object used by Seal for access checks. The bucket -is now ready for uploads. +`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`. +Takes the same `limit`, `cursor`, `q`, `sortField`, and `sortOrder` parameters as the space file listing. Returns `200` with `data` and `pagination`. ### Upload a file @@ -238,22 +213,18 @@ 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. | -| `metadata` | No | JSON object string persisted with the file. Maximum 8 KB. | +| `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`. +Returns `202` with the file summary under `data`. Poll the status endpoint until the state is `completed`. -:::note +:::info -Just after finalize, the onchain access grant needs a few seconds to mirror into the access -index. Until it does, upload returns `403` with code `mirror_missing_grant`. Retry every few -seconds. +After finalize, the on-chain 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: `409` when the bucket is not finalized or a file name is duplicated, `413` -when the payload is too large, `422` when a quota is exceeded, and `429` when rate limited. +Other responses include `409` (bucket not finalized or duplicate file name), `413` (payload too large), `422` (quota exceeded), and `429` (rate limited). ## Files @@ -261,17 +232,13 @@ when the payload is too large, `422` when a quota is exceeded, and `429` when ra `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`. +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. +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 @@ -279,35 +246,25 @@ calls for the same file id are deduplicated. 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`; confirm completion through the file metadata endpoint after that. +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`; confirm completion through the file metadata endpoint after that. ### 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 -[quickstart](./quickstart) for the decrypt flow. +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`. +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, so your service key signs but does not need a -token balance. They are typically driven through the SDK patterns shown in the -[quickstart](./quickstart). +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: +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 | | --- | --- | --- | @@ -323,10 +280,9 @@ Returns `200` with `bytes` and `digest`. `POST /api/v1/seal/sponsor/{digest}/execute` -Submits the signature over the sponsored bytes. Console broadcasts the transaction and returns -the onchain digest. +Submits the signature over the sponsored bytes. Console broadcasts the transaction and returns the onchain digest. -| Parameter | In | Required | Description | +| Parameter | Location | Required | Description | | --- | --- | --- | --- | | `digest` | path | Yes | The sponsor digest from the sponsor endpoint. | @@ -337,8 +293,3 @@ Body: ``` Returns `200` with the onchain `digest`. An expired or unknown digest returns `404`. - -## Next steps - -- Work through the [quickstart](./quickstart) to see these endpoints in a full encrypted flow. -- Review the [concepts and overview](./overview) for the space, bucket, and file model. diff --git a/docs/content/console/auth.mdx b/docs/content/console/auth.mdx index 4ddeb60225..15bf73ba4e 100644 --- a/docs/content/console/auth.mdx +++ b/docs/content/console/auth.mdx @@ -1,78 +1,43 @@ --- -title: Sign-in and accounts +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 signs you in with a familiar identity provider and provisions everything you -need to start storing data. You do not create a wallet, manage a seed phrase, or hold tokens -to sign up. +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. -:::note +:::info -Walrus Console is in an invited beta on Mainnet. Google sign-in is available today, and Apple -sign-in joins at beta. For what happens after you sign in, see the -[quickstart](./quickstart). For the product model, see the [concepts and overview](./overview). +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. You authenticate with an identity provider you already have, and -Console derives a Sui address from that identity without exposing your provider account onchain -and without asking you to manage a private key. The result is a standard Sui address that owns -your data, backed by a sign-in you already know how to use. +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 [testnet.harbor.walrus.xyz](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, then takes you to the dashboard. +3. Console provisions your account and a [Personal Space](./overview#core-concepts-spaces-buckets-and-files), then takes you to the dashboard. -## Sign in with Apple - -Apple sign-in joins at beta and works the same way. Console derives your Sui address from your -Apple identity and provisions your account and Personal Space. - -Console accepts Apple's private email relay, so you can use Apple's **Hide My Email** option. -Console treats the relay address as your account email and delivers any account email through -it. +Apple sign-in joins soon and works the same way, deriving your Sui address from your Apple identity. It will accept 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 for your derived Sui address. This -wallet holds the storage resources your data uses. Console sponsors gas and manages the wallet -for you, so you do not fund it or sign transactions to get started. The exceptions are the -explicit signing steps in the encrypted-bucket flow, where you sign with your own service key. +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. +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. +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. - -## After you sign in - -You land on the dashboard with a Personal Space ready to use. The next step is to mint an API -key and make your first request. Follow the [quickstart](./quickstart) to create an encrypted -bucket and upload a file. - -## Next steps - -- Continue to the [quickstart](./quickstart) to mint a key and upload your first file. -- See the [concepts and overview](./overview) for spaces, buckets, and asset types. +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 index 90bd2d01e3..a2b99f5949 100644 --- a/docs/content/console/overview.mdx +++ b/docs/content/console/overview.mdx @@ -1,136 +1,80 @@ --- -title: Concepts and overview +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 [Walrus](/), the decentralized -storage 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. +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. -:::note +:::info -Walrus Console is in an invited beta on Mainnet. Some capabilities described here ship at -general availability (GA) or later, and the surface may change before GA. Availability is -called out per feature below. The current Testnet preview and its API are hosted under the -Harbor name (for example, `testnet.harbor.walrus.xyz`); Walrus Console is the product name. +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 may 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 already use Walrus directly through the CLI and -SDKs, which give you low-level control over blobs, storage epochs, and payments. +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. +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 GA. +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, will be part of the general availability release. -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; -public buckets are planned for a later release. +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 use later to search and organize your data. +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. -## Asset types - -Console is built around a single navigation shell that treats your data as typed assets. At -GA, 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. +## Encryption and privacy -**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. +:::warning -**Datasets** are published collections with metadata and an access model. At GA you can set a -dataset to public, time-gated, or perpetually gated access, and later list it on the Walrus -Marketplace. +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. -## Accounts and sign-in +::: -You sign in with Gmail today, with Apple joining at beta, both through Sui zkLogin. Console -derives a Sui address from your identity and silently provisions a Pearl wallet, so you do -not manage private keys or hold tokens to get started. +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. -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. +## Asset types -You own your data. Console does not migrate data you previously stored on Walrus outside the -product; you re-upload it. After GA, Console will surface blobs already associated with your -address when you first sign in. +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. -## API keys and roles +**Files** are general-purpose objects you upload and retrieve. This is the asset type available in the current beta. -You mint API keys in the Console web app. Each key carries one of two roles: +**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. -- `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`. Use this role for a downstream consumer that - should read your data but never change it. +**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. -Console shows the full key (prefixed `hbr_`) once, at creation, and cannot recover it -afterward. Store it like a cloud secret access key. +## Accounts and sign-in -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, since Console sponsors the gas. +You sign in through Google or Apple 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. -### Connect AI clients with the MCP server +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. -Console publishes an open-source MCP 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. +You own your data. Console does not migrate data you previously stored on Walrus outside the product; you re-upload it. -## Encryption and privacy +## API keys and roles -:::warning +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). -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. +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 private buckets solve this by encrypting every file client-side with -[Seal](/) 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. +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. +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. +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. +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 @@ -138,15 +82,9 @@ Capabilities roll out in phases. The current beta is a subset of the full produc | Capability | Availability | | --- | --- | -| Gmail and Apple sign-in, Personal Space | Beta | +| Google and Apple sign-in, Personal Space | Beta | | 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 | - -## Next steps - -- Follow the [quickstart](./quickstart) to sign up, create an encrypted bucket, and upload - your first file. -- See the [API reference](./api-reference) for the full endpoint surface. +| 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 index 9f417f9762..5ad7cfc10a 100644 --- a/docs/content/console/quickstart.mdx +++ b/docs/content/console/quickstart.mdx @@ -1,29 +1,21 @@ --- -title: Quickstart +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. +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. -:::note +:::info -Walrus Console is in alpha on Testnet, hosted under the Harbor name at -`testnet.harbor.walrus.xyz`, with its API at `https://api.testnet.harbor.walrus.xyz`. Endpoint -shapes may change before Mainnet 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). +Walrus Console is in a closed, invite-only beta on Mainnet. This quickstart uses the current Testnet preview, hosted under the Harbor name at `testnet.harbor.walrus.xyz`, with its API at `https://api.testnet.harbor.walrus.xyz`. The external API is in alpha and its endpoint shapes may change before Mainnet GA. 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 - A Google account for sign-in. -- 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. +- 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: @@ -31,26 +23,25 @@ Every request below carries your API key as a bearer token: Authorization: Bearer hbr_… ``` +:::info + +The TypeScript snippets in this guide mirror the hosted quickstart. The canonical version, including the live package IDs and Seal key-server object IDs, lives in the [Harbor repository](https://github.com/MystenLabs/harbor); prefer copying from there so your values stay current across contract upgrades. + +::: + ## Sign up and create an API key -1. Visit [testnet.harbor.walrus.xyz](https://testnet.harbor.walrus.xyz/) and sign in with - Google. zkLogin provisions your account and a Personal Space automatically. +1. Visit [testnet.harbor.walrus.xyz](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 a role, and submit. - - `read_write` is required for any state change: create and delete buckets, upload, rename, - and delete files, and finalize private buckets. - - `read_only` covers listing, status, and download only. Every write endpoint returns `403` - with code `read_only_api_key` for these keys. Use this role when you hand a key to a - downstream consumer that should not change your data. -3. On the reveal screen, copy the `hbr_…` key. Console shows it once and cannot recover it - afterward. Store it like a cloud secret access key. + - `read_write` is required for any state change: create and delete buckets, upload, rename, and delete files, and finalize private buckets. + - `read_only` covers listing, status, and download only. Every write endpoint returns `403` with code `read_only_api_key` for these keys. Use this role when you hand a key to a downstream consumer that should not change your data. +3. On the reveal screen, copy the `hbr_…` key. Console shows it once and cannot recover it afterward. Store it like a cloud secret access key. Pick `read_write` if you intend to follow the encrypted flow below end to end. ## 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. Creation goes through a reserve, sign, and finalize -handshake, with a one-time service-key setup beforehand and a local decrypt step on download: +Private buckets are encrypted client-side, so Console stores ciphertext only and never sees your plaintext or decryption material. Creation goes through a reserve, sign, and finalize handshake, with a one-time service-key setup beforehand and a local decrypt step on download: ``` service key setup → get space → reserve → sign → finalize → @@ -59,33 +50,26 @@ encrypt → upload → poll → download → decrypt :::warning -You end this section holding two secrets: an `hbr_…` API key, sent as -`Authorization: Bearer …` on every request, and a `suiprivkey1…` service private key, kept -locally and used to sign the finalize transaction and to authenticate decrypt sessions with -Seal. Console shows both once. Store them like cloud access keys. +You end this section holding two secrets: an `hbr_…` API key, sent as `Authorization: Bearer …` on every request, and a `suiprivkey1…` service private key, kept locally and used to sign the finalize transaction and to authenticate decrypt sessions with Seal. Console shows both once. Store them like cloud access keys. ::: ### Step 1: Create an encrypted-capable API key -In **Settings → API Keys → New API key**, pick role `read_write` and tick **Create**. The -reveal screen now exposes two secrets: +In **Settings → API Keys → New API key**, pick role `read_write` and tick **Create**. The reveal screen now exposes two secrets: - `hbr_…`, the API key. -- `suiprivkey1…`, the service private key: an Ed25519 secret in Sui keytool format. It is bound - to this API key, and Console stores only the derived public address. It does not need a token - balance, since Console sponsors gas. +- `suiprivkey1…`, the service private key: an Ed25519 secret in Sui keytool format. It is bound to this API key, and Console stores only the derived public address. It does not need a token balance. Paste them into your `.env` as `HARBOR_SERVICE_PRIVKEY` and your bearer token, or into Postman. -### Step 2: Get your space id +### Step 2: 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. +The response's `data[]` array holds your spaces. Copy the `id` of the Personal Space created at sign-up. ### Step 3: Reserve the bucket @@ -107,15 +91,11 @@ Response (`201`): } ``` -`bytes` is the sponsored Sui transaction that creates the bucket's Seal 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. +`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. ### Step 4: Sign the bytes with the service key -`@mysten/sui` handles the Bech32 decode and the Sui signature envelope for you: +The signing step uses `@mysten/sui`, which handles the Bech32 private-key decode and the Sui signature envelope for you: ```ts import { decodeSuiPrivateKey } from '@mysten/sui/cryptography'; @@ -136,22 +116,17 @@ Content-Type: application/json { "signature": "" } ``` -Console combines your signature with the gas-sponsor signature and broadcasts the transaction. -Response (`200`): +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. +`seal_policy_id` is the onchain bucket-policy object ID that Seal uses for access checks. The bucket is now usable. ### Step 6: Encrypt the file with Seal -Encrypt locally against the `seal_policy_id` returned by finalize. `ORIGINAL` here means the -original id of the upgradeable package. Seal pins identity derivation to the original package -id, so encryption must use this value even after the package upgrades, otherwise an upgrade -would invalidate every previously encrypted blob's key. +Encrypt locally against the `seal_policy_id` returned by finalize. `ORIGINAL` here means the original ID of the upgradeable package. Seal pins identity derivation to the original package ID, so encryption must use this value even after the package upgrades, otherwise an upgrade would invalidate every previously encrypted blob's key. ```ts import { SealClient } from '@mysten/seal'; @@ -176,7 +151,7 @@ const seal = new SealClient({ verifyKeyServers: false, }); -// Each file's Seal id = (bucket policy id, 32 random bytes). +// Each file's Seal ID = (bucket policy ID, 32 random bytes). const SealIdentity = bcs.struct('SealIdentity', { policyObjectId: bcs.Address, nonce: bcs.fixedArray(32, bcs.u8()), @@ -203,10 +178,7 @@ Content-Type: multipart/form-data file=@ ``` -Just after finalize, the onchain access grant needs a few seconds to mirror into Console's -access index. Until it does, this endpoint returns `403` with code `mirror_missing_grant`. -Retry every few seconds; 20 attempts is plenty in practice. Once the grant mirrors, the -response is `202` with `data.id`. +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 8: Poll status @@ -214,8 +186,7 @@ response is `202` with `data.id`. 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. +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 9: Download and decrypt @@ -223,9 +194,7 @@ or two until the state is `completed`. Completion is typically under 30 seconds GET /api/v1/buckets/{bucketId}/files/{fileId}/download ``` -The response is the raw Seal ciphertext. Reusing the `sui` and `seal` clients from step 6, -decrypt by building the bucket's access-check transaction, signing it with a session key, and -passing both the ciphertext and the transaction to `SealClient.decrypt`: +The response is the raw Seal ciphertext. Reusing the `sui` and `seal` clients from step 6, decrypt by building the bucket's access-check transaction, signing it with a session key, and passing both the ciphertext and the transaction to `SealClient.decrypt`: ```ts import { EncryptedObject, SessionKey } from '@mysten/seal'; @@ -265,6 +234,5 @@ Decryption is fully client-side and never touches Console's backend. ## Get help -Open an issue at [github.com/MystenLabs/harbor/issues](https://github.com/MystenLabs/harbor/issues) -with the `developer-docs` label. Include the endpoint and method, the HTTP status, and the -`code` field from any error response. +- Open an issue at [github.com/MystenLabs/harbor/issues](https://github.com/MystenLabs/harbor/issues) with the `developer-docs` label. Include the endpoint and method, the HTTP status, and the `code` field from any error response. +- Ask the team and other developers in the [Walrus Discord](https://discord.gg/walrusprotocol). diff --git a/docs/content/console/storage-epochs.mdx b/docs/content/console/storage-epochs.mdx index f7f37159ad..ad785a6768 100644 --- a/docs/content/console/storage-epochs.mdx +++ b/docs/content/console/storage-epochs.mdx @@ -1,39 +1,26 @@ --- -title: Storage, epochs, and renewal +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. +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. -:::note +:::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). +Storage and epochs work today. Automatic renewal will ship 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 is not renewed, the data expires and is -no longer retrievable. +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 is not renewed, the data expires and is no longer retrievable. -Console handles the underlying payment and epoch accounting for you through your Pearl wallet. -You store a file, and Console reserves and funds its storage. You do not buy epochs or sign -renewal transactions yourself. +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. +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. @@ -41,30 +28,16 @@ Automatic renewal ships at and after GA, alongside Mainnet billing. :::warning -If a wallet goes dormant, its storage is allowed to lapse 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. +If a wallet goes dormant, its storage is allowed to lapse 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. +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. +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). - -## Next steps - -- Follow the [quickstart](./quickstart) to store your first file. -- See the [concepts and overview](./overview) for spaces, buckets, and asset types. +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). From c56891068bcc864e5b2d708751b221fcc609aa91 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Tue, 14 Jul 2026 23:03:06 +0000 Subject: [PATCH 5/9] docs: apply Sui style-guide audit fixes to Console docs - "may" -> "might" (api-reference, overview, quickstart) - "on-chain" -> "onchain" (api-reference) - present tense over future/passive: "will accept" -> "accepts" (auth), "will be part of the general availability release" -> "arrive at general availability" (overview), "will ship" -> "ships" (storage-epochs) Left the two "capitalize testnet" flags unchanged: both point at the testnet.harbor.walrus.xyz hostname in link text, not prose. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GQ8Q5FL8je9mpXv93ff23R --- docs/content/console/api-reference.mdx | 4 ++-- docs/content/console/auth.mdx | 2 +- docs/content/console/overview.mdx | 4 ++-- docs/content/console/quickstart.mdx | 2 +- docs/content/console/storage-epochs.mdx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/content/console/api-reference.mdx b/docs/content/console/api-reference.mdx index 54aa82a09e..e1c8eff8a3 100644 --- a/docs/content/console/api-reference.mdx +++ b/docs/content/console/api-reference.mdx @@ -8,7 +8,7 @@ This reference covers the Walrus Console external API: the endpoints third-party :::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 may 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). +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). ::: @@ -220,7 +220,7 @@ Returns `202` with the file summary under `data`. Poll the status endpoint until :::info -After finalize, the on-chain 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. +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. ::: diff --git a/docs/content/console/auth.mdx b/docs/content/console/auth.mdx index 15bf73ba4e..a5671972ba 100644 --- a/docs/content/console/auth.mdx +++ b/docs/content/console/auth.mdx @@ -22,7 +22,7 @@ Console uses Sui [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin). Y 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 will accept Apple's private email relay, so you can use the **Hide My Email** option. +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 diff --git a/docs/content/console/overview.mdx b/docs/content/console/overview.mdx index a2b99f5949..44a709ea9b 100644 --- a/docs/content/console/overview.mdx +++ b/docs/content/console/overview.mdx @@ -8,7 +8,7 @@ Walrus Console is the developer-first interface for interacting with the Walrus :::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 may change before GA. Availability is called out per feature below. +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. ::: @@ -22,7 +22,7 @@ Walrus Console sits on top of that protocol as a managed developer surface. It h 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, will be part of the general availability release. +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. diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx index 5ad7cfc10a..582ee063b7 100644 --- a/docs/content/console/quickstart.mdx +++ b/docs/content/console/quickstart.mdx @@ -8,7 +8,7 @@ This quickstart takes you from sign-up to a working encrypted upload: create an :::info -Walrus Console is in a closed, invite-only beta on Mainnet. This quickstart uses the current Testnet preview, hosted under the Harbor name at `testnet.harbor.walrus.xyz`, with its API at `https://api.testnet.harbor.walrus.xyz`. The external API is in alpha and its endpoint shapes may change before Mainnet GA. 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). +Walrus Console is in a closed, invite-only beta on Mainnet. This quickstart uses the current Testnet preview, hosted under the Harbor name at `testnet.harbor.walrus.xyz`, with its API at `https://api.testnet.harbor.walrus.xyz`. The external API is in alpha and its endpoint shapes might change before Mainnet GA. 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). ::: diff --git a/docs/content/console/storage-epochs.mdx b/docs/content/console/storage-epochs.mdx index ad785a6768..7be7a3f095 100644 --- a/docs/content/console/storage-epochs.mdx +++ b/docs/content/console/storage-epochs.mdx @@ -8,7 +8,7 @@ Walrus storage is time-bound. Your data stays available for a set amount of stor :::info -Storage and epochs work today. Automatic renewal will ship 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). +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). ::: From 951f558bfe254dc00348dd61fe8fdedd8ce35025 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Tue, 14 Jul 2026 23:51:40 +0000 Subject: [PATCH 6/9] docs: use Prerequisites tab component in Console quickstart Wrap the quickstart prerequisites in the shared pattern used elsewhere in the docs, per review feedback. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GQ8Q5FL8je9mpXv93ff23R --- docs/content/console/quickstart.mdx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx index 582ee063b7..e8f44307ba 100644 --- a/docs/content/console/quickstart.mdx +++ b/docs/content/console/quickstart.mdx @@ -14,8 +14,16 @@ Walrus Console is in a closed, invite-only beta on Mainnet. This quickstart uses ## Prerequisites -- A Google account for sign-in. -- 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. +
+ + + +- [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: From fe044deac492a048a59a615fda5f1526b092385e Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Wed, 15 Jul 2026 02:27:34 +0000 Subject: [PATCH 7/9] docs: resolve remaining style-audit flags in Console docs - Reword the two `testnet.harbor.walrus.xyz` link texts to "the Walrus Console app" so no lowercase hostname token appears in prose (URL unchanged). - Active voice in storage-epochs: "is not renewed" -> "you do not renew it"; "its storage is allowed to lapse" -> "its storage lapses". Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GQ8Q5FL8je9mpXv93ff23R --- docs/content/console/auth.mdx | 2 +- docs/content/console/quickstart.mdx | 2 +- docs/content/console/storage-epochs.mdx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/console/auth.mdx b/docs/content/console/auth.mdx index a5671972ba..20d97ccaf5 100644 --- a/docs/content/console/auth.mdx +++ b/docs/content/console/auth.mdx @@ -18,7 +18,7 @@ Console uses Sui [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin). Y ## Sign in with Google -1. Visit [testnet.harbor.walrus.xyz](https://testnet.harbor.walrus.xyz/). +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. diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx index e8f44307ba..8a72a5874d 100644 --- a/docs/content/console/quickstart.mdx +++ b/docs/content/console/quickstart.mdx @@ -39,7 +39,7 @@ The TypeScript snippets in this guide mirror the hosted quickstart. The canonica ## Sign up and create an API key -1. Visit [testnet.harbor.walrus.xyz](https://testnet.harbor.walrus.xyz/) and sign in with Google. zkLogin provisions your account and a Personal Space automatically. +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 a role, and submit. - `read_write` is required for any state change: create and delete buckets, upload, rename, and delete files, and finalize private buckets. - `read_only` covers listing, status, and download only. Every write endpoint returns `403` with code `read_only_api_key` for these keys. Use this role when you hand a key to a downstream consumer that should not change your data. diff --git a/docs/content/console/storage-epochs.mdx b/docs/content/console/storage-epochs.mdx index 7be7a3f095..56d71be154 100644 --- a/docs/content/console/storage-epochs.mdx +++ b/docs/content/console/storage-epochs.mdx @@ -14,7 +14,7 @@ Storage and epochs work today. Automatic renewal ships at and after GA. This pag ## 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 is not renewed, the data expires and is no longer retrievable. +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. @@ -28,7 +28,7 @@ Automatic renewal ships at and after GA, alongside Mainnet billing. :::warning -If a wallet goes dormant, its storage is allowed to lapse 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. +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. ::: From 47a9582c9e8788cf4eb27ce76f8af99e2d7f17e8 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Wed, 15 Jul 2026 09:09:42 +0000 Subject: [PATCH 8/9] docs: source Console quickstart code from walrus-harbor-quickstart Replace the inline TypeScript snippets (signing, Seal encrypt/decrypt) and the hardcoded package/key-server IDs with pulls from the public MystenLabs/walrus-harbor-quickstart example: - config.ts and lib/seal.ts introduced once as the canonical constants and helpers. - sign-reserve.ts, encrypt-file.ts, and decrypt-file.ts imported at Steps 4, 6, and 9, with pnpm run commands. This removes the copy/paste snippets flagged in review and keeps the package IDs and Seal key-server IDs in sync with the source repo. Also repoint the source note from the private harbor repo to the public quickstart repo. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GQ8Q5FL8je9mpXv93ff23R --- docs/content/console/quickstart.mdx | 105 +++++----------------------- 1 file changed, 18 insertions(+), 87 deletions(-) diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx index 8a72a5874d..6eeb1421b6 100644 --- a/docs/content/console/quickstart.mdx +++ b/docs/content/console/quickstart.mdx @@ -33,7 +33,7 @@ Authorization: Bearer hbr_… :::info -The TypeScript snippets in this guide mirror the hosted quickstart. The canonical version, including the live package IDs and Seal key-server object IDs, lives in the [Harbor repository](https://github.com/MystenLabs/harbor); prefer copying from there so your values stay current across contract upgrades. +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. ::: @@ -101,19 +101,19 @@ Response (`201`): `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. -### Step 4: Sign the bytes with the service key +### The example code -The signing step uses `@mysten/sui`, which handles the Bech32 private-key decode and the Sui signature envelope for you: +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. -```ts -import { decodeSuiPrivateKey } from '@mysten/sui/cryptography'; -import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'; -import { fromBase64 } from '@mysten/sui/utils'; + -const { secretKey } = decodeSuiPrivateKey(process.env.HARBOR_SERVICE_PRIVKEY); -const keypair = Ed25519Keypair.fromSecretKey(secretKey); -const { signature } = await keypair.signTransaction(fromBase64(bytes)); -``` + + +### Step 4: 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 5: Finalize @@ -134,48 +134,11 @@ Console combines your signature with the gas-sponsor signature and broadcasts th ### Step 6: Encrypt the file with Seal -Encrypt locally against the `seal_policy_id` returned by finalize. `ORIGINAL` here means the original ID of the upgradeable package. Seal pins identity derivation to the original package ID, so encryption must use this value even after the package upgrades, otherwise an upgrade would invalidate every previously encrypted blob's key. - -```ts -import { SealClient } from '@mysten/seal'; -import { SuiGrpcClient } from '@mysten/sui/grpc'; -import { bcs } from '@mysten/sui/bcs'; - -const HARBOR_ORIGINAL_PACKAGE_ID = - '0x8b2429358e9b0f005b69fe8ad3cbd1268ad87f35047a21612e082c64824faf8d'; -const SEAL_KEY_SERVER_OBJECT_IDS = [ - '0x6068c0acb197dddbacd4746a9de7f025b2ed5a5b6c1b1ab44dade4426d141da2', - '0x164ac3d2b3b8694b8181c13f671950004765c23f270321a45fdd04d40cccf0f2', - '0x9c949e53c36ab7a9c484ed9e8b43267a77d4b8d70e79aa6b39042e3d4c434105', -]; - -const sui = new SuiGrpcClient({ - network: 'testnet', - baseUrl: 'https://fullnode.testnet.sui.io:443', -}); -const seal = new SealClient({ - suiClient: sui, - serverConfigs: SEAL_KEY_SERVER_OBJECT_IDS.map((objectId) => ({ objectId, weight: 1 })), - verifyKeyServers: false, -}); - -// Each file's Seal ID = (bucket policy ID, 32 random bytes). -const SealIdentity = bcs.struct('SealIdentity', { - policyObjectId: bcs.Address, - nonce: bcs.fixedArray(32, bcs.u8()), -}); -const nonce = Array.from(crypto.getRandomValues(new Uint8Array(32))); -const id = SealIdentity.serialize({ policyObjectId: sealPolicyId, nonce }).toHex(); - -const { encryptedObject } = await seal.encrypt({ - threshold: 2, - packageId: HARBOR_ORIGINAL_PACKAGE_ID, - id, - data: plaintextBytes, // Uint8Array -}); -``` +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 `: + + -`encryptedObject` is the byte stream you upload next. +It writes `.enc`, the byte stream you upload next. ### Step 7: Upload @@ -202,41 +165,9 @@ Returns `data.state`, one of `queued`, `active`, `completed`, or `failed`. Poll GET /api/v1/buckets/{bucketId}/files/{fileId}/download ``` -The response is the raw Seal ciphertext. Reusing the `sui` and `seal` clients from step 6, decrypt by building the bucket's access-check transaction, signing it with a session key, and passing both the ciphertext and the transaction to `SealClient.decrypt`: - -```ts -import { EncryptedObject, SessionKey } from '@mysten/seal'; -import { Transaction } from '@mysten/sui/transactions'; -import { fromHex } from '@mysten/sui/utils'; - -// Latest bucket-policy package, host of the seal_approve move call. -const HARBOR_LATEST_PACKAGE_ID = - '0xc11d875481544e9b6c616f7d6704266e1633b4034eab7ed76626dc25ebfcd506'; - -// ciphertext = bytes from GET /download -const parsed = EncryptedObject.parse(ciphertext); -const idBytes = fromHex(parsed.id.startsWith('0x') ? parsed.id : '0x' + parsed.id); - -// 1. Build the access-check transaction (transaction kind only, never broadcast). -const tx = new Transaction(); -tx.moveCall({ - target: `${HARBOR_LATEST_PACKAGE_ID}::bucket_policy::seal_approve`, - arguments: [tx.pure.vector('u8', idBytes), tx.object(sealPolicyId)], -}); -const txBytes = await tx.build({ client: sui, onlyTransactionKind: true }); - -// 2. A session key lets the Seal key servers verify the caller without re-signing per request. -const sessionKey = await SessionKey.create({ - address: keypair.toSuiAddress(), - packageId: HARBOR_ORIGINAL_PACKAGE_ID, - ttlMin: 10, - suiClient: sui, - signer: keypair, -}); - -// 3. Decrypt. SealClient fetches threshold key shares and reconstructs the key locally. -const plaintext = await seal.decrypt({ data: ciphertext, sessionKey, txBytes }); -``` +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. From 476d3fd20ee49c746ec1d6f09822ac218131fb35 Mon Sep 17 00:00:00 2001 From: Reem Sabawi Date: Wed, 15 Jul 2026 15:43:12 +0000 Subject: [PATCH 9/9] docs: address second-round Console docs review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Get Help: drop the private harbor/issues link; point to the Walrus Discord and the public walrus-harbor-quickstart issues. - Apple availability: overview no longer lists Apple sign-in as Beta; the capability table shows Google (Beta) and Apple (Soon), matching auth.mdx. - Quickstart: collapse the two API-key steps into one explicit "create a read_write encryption-capable key, save both hbr_ and suiprivkey1" path; renumber the remaining steps (now 1-8). - API reference: clarify the upload-status 404 guidance — a 404 after upload means check file metadata before treating it as a failure. - Framing: on the quickstart, lead with the Testnet-alpha API framing instead of juxtaposing it with the Mainnet closed-beta product framing (which stays on the overview and sign-in pages). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GQ8Q5FL8je9mpXv93ff23R --- docs/content/console/api-reference.mdx | 2 +- docs/content/console/overview.mdx | 5 ++- docs/content/console/quickstart.mdx | 54 ++++++++++---------------- 3 files changed, 24 insertions(+), 37 deletions(-) diff --git a/docs/content/console/api-reference.mdx b/docs/content/console/api-reference.mdx index e1c8eff8a3..781c556c1e 100644 --- a/docs/content/console/api-reference.mdx +++ b/docs/content/console/api-reference.mdx @@ -246,7 +246,7 @@ Marks the file as `deleting` and enqueues a background job to remove the underly 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`; confirm completion through the file metadata endpoint after that. +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 diff --git a/docs/content/console/overview.mdx b/docs/content/console/overview.mdx index 44a709ea9b..7e75d73bb6 100644 --- a/docs/content/console/overview.mdx +++ b/docs/content/console/overview.mdx @@ -50,7 +50,7 @@ Console is built around a single navigation shell that treats your data as typed ## Accounts and sign-in -You sign in through Google or Apple 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. +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. @@ -82,7 +82,8 @@ Capabilities roll out in phases. The current beta is a subset of the full produc | Capability | Availability | | --- | --- | -| Google and Apple sign-in, Personal Space | Beta | +| 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 | diff --git a/docs/content/console/quickstart.mdx b/docs/content/console/quickstart.mdx index 6eeb1421b6..e34eb6c3bf 100644 --- a/docs/content/console/quickstart.mdx +++ b/docs/content/console/quickstart.mdx @@ -8,7 +8,7 @@ This quickstart takes you from sign-up to a working encrypted upload: create an :::info -Walrus Console is in a closed, invite-only beta on Mainnet. This quickstart uses the current Testnet preview, hosted under the Harbor name at `testnet.harbor.walrus.xyz`, with its API at `https://api.testnet.harbor.walrus.xyz`. The external API is in alpha and its endpoint shapes might change before Mainnet GA. 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). +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). ::: @@ -40,38 +40,24 @@ The code in this guide is pulled directly from the runnable [`walrus-harbor-quic ## 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 a role, and submit. - - `read_write` is required for any state change: create and delete buckets, upload, rename, and delete files, and finalize private buckets. - - `read_only` covers listing, status, and download only. Every write endpoint returns `403` with code `read_only_api_key` for these keys. Use this role when you hand a key to a downstream consumer that should not change your data. -3. On the reveal screen, copy the `hbr_…` key. Console shows it once and cannot recover it afterward. Store it like a cloud secret access key. +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. -Pick `read_write` if you intend to follow the encrypted flow below end to end. + 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. Creation goes through a reserve, sign, and finalize handshake, with a one-time service-key setup beforehand and a local decrypt step on download: +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: ``` -service key setup → get space → reserve → sign → finalize → -encrypt → upload → poll → download → decrypt +get space → reserve → sign → finalize → encrypt → upload → poll → download → decrypt ``` -:::warning - -You end this section holding two secrets: an `hbr_…` API key, sent as `Authorization: Bearer …` on every request, and a `suiprivkey1…` service private key, kept locally and used to sign the finalize transaction and to authenticate decrypt sessions with Seal. Console shows both once. Store them like cloud access keys. - -::: - -### Step 1: Create an encrypted-capable API key - -In **Settings → API Keys → New API key**, pick role `read_write` and tick **Create**. The reveal screen now exposes two secrets: - -- `hbr_…`, the API key. -- `suiprivkey1…`, the service private key: an Ed25519 secret in Sui keytool format. It is bound to this API key, and Console stores only the derived public address. It does not need a token balance. - -Paste them into your `.env` as `HARBOR_SERVICE_PRIVKEY` and your bearer token, or into Postman. - -### Step 2: Get your space ID +### Step 1: Get your space ID ```http GET /api/v1/spaces @@ -79,7 +65,7 @@ GET /api/v1/spaces The response's `data[]` array holds your spaces. Copy the `id` of the Personal Space created at sign-up. -### Step 3: Reserve the bucket +### Step 2: Reserve the bucket ```http POST /api/v1/spaces/{spaceId}/buckets @@ -109,19 +95,19 @@ The signing, encryption, and decryption steps below come from the runnable [`wal -### Step 4: Sign the bytes with the service 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 5: Finalize +### Step 4: Finalize ```http POST /api/v1/buckets/{bucketId}/finalize Content-Type: application/json -{ "signature": "" } +{ "signature": "" } ``` Console combines your signature with the gas-sponsor signature and broadcasts the transaction. Response (`200`): @@ -132,7 +118,7 @@ Console combines your signature with the gas-sponsor signature and broadcasts th `seal_policy_id` is the onchain bucket-policy object ID that Seal uses for access checks. The bucket is now usable. -### Step 6: Encrypt the file with Seal +### 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 `: @@ -140,7 +126,7 @@ Encrypt locally against the `seal_policy_id` from finalize. `encryptBytes` (in ` It writes `.enc`, the byte stream you upload next. -### Step 7: Upload +### Step 6: Upload ```http POST /api/v1/buckets/{bucketId}/files @@ -151,7 +137,7 @@ 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 8: Poll status +### Step 7: Poll status ```http GET /api/v1/buckets/{bucketId}/files/{fileId}/status @@ -159,7 +145,7 @@ 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 9: Download and decrypt +### Step 8: Download and decrypt ```http GET /api/v1/buckets/{bucketId}/files/{fileId}/download @@ -173,5 +159,5 @@ Decryption is fully client-side and never touches Console's backend. ## Get help -- Open an issue at [github.com/MystenLabs/harbor/issues](https://github.com/MystenLabs/harbor/issues) with the `developer-docs` label. Include the endpoint and method, the HTTP status, and the `code` field from any error response. - 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.