mcpField
W
walrus-sites

walrus-sites

github.com/mystenlabsCloud & DevOps343 installsUpdated 1w ago
Repository

About walrus-sites

A skill for deploying and operating static websites on Walrus Sites, aimed at developers publishing HTML, CSS, and JavaScript frontends. It covers site-builder, portal configuration, routing, storage duration, and deployment troubleshooting.

Key features

  1. 01Guide static site deployment and updates
  2. 02Configure resources, routes, and redirects
  3. 03Troubleshoot portal errors and expired blobs
  4. 04Explain local testnet portal setup
  5. 05Provide GitHub Actions deployment guidance

Use cases

  1. 01Publish a static frontend to Walrus Sites
  2. 02Update a site while retaining its site object
  3. 03Configure SPA deep links and redirects
  4. 04Diagnose site 404s or expired resources

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/mystenlabs/walrus-skills --skill walrus-sites

After installing, type/walrus-sitesin Claude Code to confirm it loaded.

Bundled filesevals/evals.json

Details

From the project README

Walrus Sites — Decentralized Website Hosting

Source constraint: All information in this skill is sourced from the Walrus Sites documentation and the MystenLabs/walrus-sites repository. When extending this skill, only pull from these sources.

Walrus Sites lets you deploy a static website (HTML/CSS/JS) to Walrus, with an on-chain Sui object tracking the site's resources. A portal server resolves site subdomains, fetches resources from Walrus, and serves them over HTTP. The result is a fully decentralized frontend with no centralized server, CDN, or DNS required.

Common failures:

  1. Blobs expire. Sites published with too few epochs silently break when blobs expire. Always use a generous --epochs value or --epochs max (53 epochs maximum — about 53 days on testnet, ~2 years on mainnet). Note: --permanent only makes a blob non-deletable; it does not extend its storage duration beyond the --epochs value.
  2. Portal misconfiguration. The portal's original_package_id must match the Walrus Sites framework package (site::Site), not your app's Move package.
  3. Testnet sites cannot use wal.app. The public wal.app portal serves mainnet only. Testnet requires a self-hosted local portal.
  4. SPA routing. Single-page apps need ws-resources.json routing config for client-side routing. Without it, direct navigation to deep links returns 404.

Sub-skills

publishing — Building and deploying sites

Path: publishing/SKILL.md Load when: deploying a new site, updating an existing site, choosing epoch duration, configuring ws-resources.json, or troubleshooting site-builder errors. Covers: site-builder deploy, site-builder publish, site-builder update, --epochs flag, blob expiration, ws-resources.json configuration, SPA routing, site naming, site-builder sitemap for debugging, site-builder destroy.

portal — Running a local portal for testnet

Path: portal/SKILL.md Load when: the user needs to view a testnet site, set up the local portal, run their own portal as a decentralized fallback, fix portal 404s, or understand how portal resolution works. Covers: cloning the portal, installing dependencies (Bun), portal-config.yaml, the original_package_id gotcha, starting the server, URL format, port conflicts, mainnet vs testnet differences.


Routing guide

TaskLoad
Deploy a frontend to Walrus Sitespublishing/
Update an existing deployed sitepublishing/
Fix 404 or expired sitepublishing/ + portal/
View a testnet site locallyportal/
Set up the local portalportal/
Portal shows "Page not found"portal/ + publishing/ (check blob expiry)
Configure SPA routing or custom headerspublishing/
Set up custom domain (SuiNS)Skill Content below
Set up CI/CD with GitHub ActionsSkill Content below
Check what resources a site haspublishing/ (site-builder sitemap)
Delete a deployed sitepublishing/
Choose between Walrus Sites and traditional hostingSkill Content below

Skill Content

Key concepts
  • Site object. A Sui object of type site::Site from the Walrus Sites framework package. It holds metadata and dynamic fields mapping resource paths to Walrus blob references. Created by site-builder deploy, owned by the publisher's address.

  • Resources. Each file in the deployed directory becomes a resource stored on Walrus. Small files might be batched into a quilt. Each resource has a path (for example, /index.html, /assets/main.js) and optional HTTP headers.

  • Portal. A server that maps <base36-site-id>.localhost:3000 or <name>.localhost:3000 (self-hosted/testnet) or <name>.wal.app (mainnet with SuiNS) URLs to on-chain site objects. It reads the site's dynamic fields to find the requested resource path, fetches the blob from a Walrus aggregator, and returns it as an HTTP response.

  • ws-resources.json. A configuration file in the site's root directory controlling resource headers, routing rules, redirect configuration, metadata, and file ignoring. Auto-generated on first deploy with the site object ID. On subsequent deploys, the site-builder reads it to determine whether to create a new site or update the existing one.

  • Blob expiration. Walrus blobs have a finite storage duration (maximum 53 epochs). When blobs expire, the portal returns a distinct 404 whose body reads "This content is no longer available / It may have expired" and names the Blob ID — this is different from a regular page-not-found 404. Use site-builder sitemap <object-id> to check expiration dates. Re-deploy with a higher --epochs value to fix.

  • site-builder deploy. The recommended unified command that creates a new site or updates an existing one (replacing the legacy publish and update commands).

Site-builder CLI commands
CommandPurpose
site-builder deploy <DIR>Create or update a site (recommended)
site-builder sitemap <OBJECT_ID>List resources with expiry dates
site-builder list-directory <DIR>Preview what would be uploaded
site-builder update-resourcesAdd or update one or more resources
site-builder destroy <OBJECT_ID>Remove a site permanently
site-builder convert <HEX>Convert hex object ID to Base36

deploy updates when it finds an object ID, from --object-id <OBJECT_ID> or the object_id field in ws-resources.json, and publishes a new site otherwise. --epochs is required (max for the longest duration); --list-directory generates an index page for raw files. Run site-builder deploy --help to confirm flag spellings for your installed version; the docs' tutorial shows --object-ids while the reference page shows --object-id.

HTTP redirects

The redirects field in ws-resources.json defines server-side HTTP redirects. Rules are stored as dynamic fields on the Site object, and the portal evaluates them before fetching any resource:

{
  "redirects": {
    "/old-page": { "location": "/new-page", "status_code": 301 },
    "/walrus-docs": { "location": "https://docs.wal.app", "status_code": 301 },
    "/legacy/**/*": { "location": "/index.html", "status_code": 302 }
  }
}

location is a path on the site or a full URL; status_code is typically 301, 302, or 308. Patterns are glob-style: * matches one path segment, **/* any number. This is distinct from the routes section, which is client-side routing only. The public portal caps redirect chains at a depth of 3.

Production checklist

From the docs' production guide:

  • Portal: wal.app serves only Mainnet sites that have a SuiNS name; nested subnames such as app.myname.wal.app do not resolve, so use one SuiNS name per site. Testnet and previews need a local or self-hosted portal.
  • SPA routing: "routes": { "/*": "/index.html" }; more specific patterns win over the wildcard.
  • Caching: portals and CDNs cache aggressively. Use content-hashed filenames, keep the old site object when you need a rollback target, and verify a fresh deploy from a private window before concluding it failed.
  • CI: there is no watch mode; run site-builder deploy from CI on each push. Commit ws-resources.json (it carries the site object ID) or every CI run creates a new site instead of updating yours.
  • Next.js: build with output: "export" and images: { unoptimized: true }, deploy out/, and put ws-resources.json in public/ so redeploys update the same site. API routes, server actions, and on-demand rendering do not run.
  • Toolchain: suiup installs and pins sui, walrus, and site-builder together.
Custom domains### Custom domains

SuiNS names: Associate a SuiNS name with your site object using SuiNS tooling (a separate on-chain step). The portal resolves <name>.wal.app to your site via the SuiNS name record's walrusSiteId field. Note: the site_name field in ws-resources.json only sets the Site object's on-chain display name — it does not configure SuiNS.

Custom DNS: Point your own domain to a Walrus Sites portal using DNS CNAME records. See the DNS configuration guide for details.

CI/CD with GitHub Actions

Use the official Deploy Walrus Site GitHub Action to automate deployments:

- uses: MystenLabs/walrus-sites-github-actions/deploy@v3
  with:
    DIST: ./dist
    SUI_NETWORK: testnet
    EPOCHS: 30
    SUI_ADDRESS: ${{ secrets.SUI_ADDRESS }}
    SUI_KEYSTORE: ${{ secrets.SUI_KEYSTORE }}

Authentication methods (choose one):

  1. SUI_ADDRESS + SUI_KEYSTORE — address and keystore file contents
  2. SUI_BECH32_PRIVATE_KEY — a bech32-encoded private key
  3. SUI_SECRET_PHRASE — a 12/24-word recovery phrase

The action handles building, publishing, and updating ws-resources.json. Use GITHUB_TOKEN to have the action create a PR with updated ws-resources.json after first deploy.

Known restrictions
  • Static sites only. No server-side rendering, request handlers, or databases. HTTP redirects are supported through the redirects field in ws-resources.json; the routes section is client-side only, and framework redirect plugins that run server-side at request time do not work.
  • No secrets. All content is stored on Walrus (public). Do not include API keys, credentials, or private data.
  • Maximum 3 redirect hops on the public portal; self-hosted portals can configure the limit.
  • Route wildcards only at the end of a pattern (/path/*, not /path/*/to), route targets must exist among deployed files, and custom headers are per exact resource path, not global.
  • Service-worker portal limitations on iOS and certain browsers.
  • PWAs not fully supported.
Rules
  1. Choose an appropriate --epochs value. Maximum is 53 epochs (~53 days on testnet where 1 epoch = 1 day, ~2 years on mainnet where 1 epoch = 14 days). Use --epochs max for the longest possible storage duration. Note: --permanent only prevents deletion — it does not extend storage duration.
  2. Build before deploying. Run npm run build (or equivalent) to produce a static output directory. The site-builder deploys whatever directory you point it at.
  3. Do not change original_package_id in the portal config unless you know the Walrus Sites framework package has been upgraded.
  4. wal.app serves Mainnet sites with a SuiNS name only. No Testnet sites, no nested subnames. For testnet, self-host the portal from the MystenLabs/walrus-sites repo.
  5. Keep ws-resources.json in version control. It records the site object ID. Without it, future deploys create a new site instead of updating the existing one.
  6. Use site-builder deploy instead of publish/update. The deploy command is the recommended unified command.
Common mistakes
  • Publishing with --epochs 5 and wondering why the site disappears. Blobs expired. Re-deploy with a higher --epochs value or --epochs max.
  • Changing the portal's original_package_id to your app's package ID. The portal needs the Walrus Sites framework package ID (site::Site), not your Move contract's ID.
  • Trying to visit a testnet site on wal.app. It only serves mainnet. Use a local portal for testnet.
  • Forgetting to build the frontend before deploying. site-builder deploy dist/ deploys the dist/ directory contents. If you did not build first, you are deploying source files or stale output.
  • Deploying an SPA without fallback routing. React/Vue/Svelte SPAs use client-side routing. Direct navigation to /dashboard hits the portal looking for a /dashboard resource that does not exist. Configure a fallback route in ws-resources.json.
  • Not killing port 3000 before starting the portal. Other dev tools often use port 3000. The portal fails silently if the port is taken.
  • Not committing ws-resources.json after first deploy. Without it, the next deploy creates a brand new site instead of updating the existing one.
  • WASM files returning 503 errors. Large .wasm files can trigger upstream connect error or disconnect/reset before headers from the portal. This is caused by the aggregator response exceeding the portal's idle timeout, not blob size limits. The fix (commit a31ec78, #714) sizes idleTimeout from the aggregator retry budget. If you hit this, tune via AGGREGATOR_REQUEST_TIMEOUT_MS / PORTAL_IDLE_TIMEOUT_MAX_S environment variables.
  • Windows users using $(pwd) in Docker commands. PowerShell does not support $(pwd). Use the full absolute path instead.
  • SuiNS domain costs. Custom .sui names cost approximately $10/year through SuiNS. This is separate from Walrus storage costs.
  • Deploying the repo root instead of the build output. Point site-builder deploy at dist/, build/, or out/ — not the repository root. Deploying the root uploads node_modules/, source files, and config, dramatically increasing costs.
  • Redeploying to an existing site without ws-resources.json. If you lost the file, use --object-id <SITE_OBJECT_ID> to target the existing site object directly.

Comments

Markdown supported