diff --git a/docs/develop/dynamic-content.md b/docs/develop/dynamic-content.md index 197f2e839..a5b94676e 100644 --- a/docs/develop/dynamic-content.md +++ b/docs/develop/dynamic-content.md @@ -5,8 +5,6 @@ sidebar_label: Dynamic Content description: Learn how to use feeds to create updateable content on Swarm — with a complete example project that builds a simple blog. --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; Every upload to Swarm produces a unique content hash — change one byte and you get a different address. This is great for data integrity, but it means there is no built-in way to give someone a single, stable link that always shows the latest version of your content. Feeds solve this problem. A feed acts as a mutable pointer on top of Swarm's immutable storage, giving you a permanent address that always resolves to whatever content you last pointed it at. @@ -16,8 +14,7 @@ If you followed the [Host a Webpage](/docs/develop/host-your-website) guide, you * A running Bee node ([install guide](/docs/bee/installation/quick-start)) * A valid postage stamp batch ([how to get one](/docs/develop/tools-and-features/buy-a-stamp-batch)) -* Node.js 18+ and `@ethersphere/bee-js` installed (for `bee-js` examples) -* [`swarm-cli` installed](https://docs.ethswarm.org/docs/bee/working-with-bee/swarm-cli) (for `swarm-cli` examples) +* Node.js 18+ and `@ethersphere/bee-js` installed ## Example Scripts and Projects @@ -38,13 +35,14 @@ Clone the repo and set up the example scripts: git clone https://github.com/ethersphere/examples.git cd examples/dynamic-content npm install +cp .env.example .env ``` -Update the `BATCH_ID` in the `.env` file with a valid batch ID, and make sure `BEE_URL` points to your Bee node: +Fill in your `BATCH_ID` and verify `BEE_URL` in `.env`: ```bash BEE_URL=http://localhost:1633 -BATCH_ID=BATCH_ID +BATCH_ID= ``` You can then run any script with: @@ -62,9 +60,6 @@ The blog project has its own setup — see [Example Project — Simple Blog](#ex To see why feeds are necessary, try uploading the same content twice with a small change ([`script-01.js`](https://github.com/ethersphere/examples/blob/main/dynamic-content/script-01.js)): - - - ```js import { Bee } from "@ethersphere/bee-js"; @@ -78,21 +73,6 @@ const upload2 = await bee.uploadFile(batchId, "Hello Swarm - version 2", "note.t console.log("Version 2:", upload2.reference.toHex()); ``` - - - -```bash -echo "Hello Swarm - version 1" > note-v1.txt -swarm-cli upload note-v1.txt --stamp BATCH_ID -# Swarm hash: a1b2c3d4... - -echo "Hello Swarm - version 2" > note-v2.txt -swarm-cli upload note-v2.txt --stamp BATCH_ID -# Swarm hash: e5f6a7b8... (different!) -``` - - - Each upload returns a different hash. If you shared the first hash with someone, they would always see "version 1" — there is no way to redirect them to "version 2" using content addressing alone. Feeds provide the missing layer of indirection. @@ -114,9 +94,6 @@ When a [mutable batch](/docs/concepts/incentives/postage-stamps#mutable-batches) Before creating a feed, you need a dedicated private key that will sign feed updates. Anyone with this key can publish to your feed, so store it securely. - - - ```js import crypto from "crypto"; import { PrivateKey } from "@ethersphere/bee-js"; @@ -130,40 +107,12 @@ console.log("Address:", pk.publicKey().address().toHex()); Save the private key somewhere secure. You will use it for all future feed updates. - - - -```bash -swarm-cli identity create publisher -``` - -Example output: - -``` -Name: publisher -Type: V3 Wallet -Private key: 0x634fb5a872396d9693e5c9f9d7233cfa93f395c093371017ff44aa9ae6564cdd -Public key: 0x218c79f8dfb26d077b6379eb56aa9c6e71edf74d... -Address: 0x8d3766440f0d7b949a5e32995d09619a7f86e632 -``` - -Record this output in a secure location. If you need to view it later: - -```bash -swarm-cli identity export publisher -``` - - - ### Write and Read a Feed Now upload some content and write its reference to a feed, then read it back ([`script-02.js`](https://github.com/ethersphere/examples/blob/main/dynamic-content/script-02.js)): - - - ```js import { Bee, Topic, PrivateKey } from "@ethersphere/bee-js"; @@ -184,43 +133,22 @@ const writer = bee.makeFeedWriter(topic, pk); await writer.upload(batchId, upload.reference); console.log("Feed updated at index 0"); +// Brief pause to allow the node to index the feed chunk +await new Promise((r) => setTimeout(r, 1000)); -// Read the latest reference from the feed (retries until indexed) +// Read the latest reference from the feed const reader = bee.makeFeedReader(topic, owner); -const result = await retryFeedRead(() => reader.downloadReference()); +const result = await reader.downloadReference(); console.log("Latest reference:", result.reference.toHex()); console.log("Current index:", result.feedIndex.toBigInt()); ``` - - - -```bash -# Upload content and update feed in one step -swarm-cli feed upload note.txt \ - --identity publisher \ - --topic-string notes \ - --stamp BATCH_ID - -# Read the feed -swarm-cli feed print \ - --identity publisher \ - --topic-string notes -``` - -The `feed print` command displays the current feed state — topic hash, number of updates, and the feed manifest URL. - - - ### Update the Feed When you have new content, upload it and write the new reference to the feed. The writer automatically uses the next sequential index (this continues from the previous snippet — both are combined in [`script-02.js`](https://github.com/ethersphere/examples/blob/main/dynamic-content/script-02.js)): - - - ```js // Upload updated content const upload2 = await bee.uploadFile(batchId, "My updated note", "note.txt"); @@ -230,73 +158,26 @@ console.log("New content hash:", upload2.reference.toHex()); await writer.upload(batchId, upload2.reference); console.log("Feed updated at index 1"); +// Brief pause to allow the node to index the new entry +await new Promise((r) => setTimeout(r, 1000)); -// Pass minFeedIndex so the retry waits for the new entry, not just any entry. -const result2 = await retryFeedRead( - () => reader.downloadReference(), - result.feedIndex.toBigInt() + 1n -); +const result2 = await reader.downloadReference(); console.log("Latest reference:", result2.reference.toHex()); console.log("Current index:", result2.feedIndex.toBigInt()); // 1n ``` - - - -```bash -# Update the note -echo "My updated note" > note.txt - -# Upload to the same feed — reuse the same identity and topic -swarm-cli feed upload note.txt \ - --identity publisher \ - --topic-string notes \ - --stamp BATCH_ID -``` - -The feed now points to the new content. The feed manifest URL (printed in the output) remains the same. - - - ## Feed Manifests — Stable URLs So far, reading a feed requires knowing the owner address and topic. A **feed manifest** packages these two values into a single Swarm hash that acts as a permanent URL. When Bee resolves a feed manifest through the `/bzz/` endpoint, it automatically looks up the latest feed entry and serves whatever content it points to ([`script-03.js`](https://github.com/ethersphere/examples/blob/main/dynamic-content/script-03.js)). - - - ```js // Create a feed manifest (one-time operation) const manifest = await bee.createFeedManifest(batchId, topic, owner); console.log("Feed manifest:", manifest.toHex()); ``` - - - -The `feed upload` command creates the manifest automatically and prints the `Feed Manifest URL` in its output: - -```bash -swarm-cli feed upload note.txt \ - --identity publisher \ - --topic-string notes \ - --stamp BATCH_ID -``` - -Example output: - -``` -Swarm hash: 387dc3cf98419dcb20c68b284373bf7d9e8dcb27... -URL: http://localhost:1633/bzz/387dc3cf98419dcb.../ -Feed Manifest URL: http://localhost:1633/bzz/6c30ef2254ac1565.../ -``` - -The hash in the `Feed Manifest URL` (`6c30ef2254ac1565...`) is your permanent reference. - - - You can now access the content through a stable URL: @@ -304,7 +185,7 @@ You can now access the content through a stable URL: http://localhost:1633/bzz/FEED_MANIFEST_HASH/ ``` -Every time you update the feed, the same URL serves the new content — no URL change needed. This is also the hash you would register in ENS as your content hash (see [Host a Webpage - Connect to ENS](/docs/develop/host-your-website#connect-site-to-ens-domain)). +Every time you update the feed, the same URL serves the new content — no URL change needed. This is also the hash you would register in ENS as your content hash (see [Host a Webpage - Connect to ENS](/docs/develop/host-your-website#optional-connect-site-to-ens-domain)). :::tip A feed manifest only needs to be created once. After that, just update the feed and the manifest URL will always resolve to the latest content. @@ -336,9 +217,6 @@ This project follows the same architectural pattern used by [Etherjot](https://g ### Project Setup - - - Clone the [examples](https://github.com/ethersphere/examples) repo (if you haven't already) and navigate to the blog project: ```bash @@ -347,38 +225,21 @@ cd examples/simple-blog npm install ``` -Update the `BATCH_ID` in the `.env` file with a valid batch ID, and make sure `BEE_URL` points to your Bee node: +Copy `.env.example` to `.env` and fill in your `BATCH_ID`: ```bash -BEE_URL=http://localhost:1633 -BATCH_ID=YOUR_BATCH_ID +cp .env.example .env ``` - - - -Create a new directory: - -```bash -mkdir swarm-blog && cd swarm-blog -mkdir site -``` - -Make sure you have a `swarm-cli` identity ready: - ```bash -swarm-cli identity create blog-publisher +BEE_URL=http://localhost:1633 +BATCH_ID= ``` - - ### Project Structure - - - ``` simple-blog/ ├── .env # Bee URL and batch ID @@ -390,78 +251,18 @@ simple-blog/ └── posts.json # Generated by init.js — stores all blog posts ``` - - +### The HTML Generation Module -``` -swarm-blog/ -├── site/ -│ ├── index.html # Blog index page (listing of all posts) -│ └── posts/ -│ └── .html # Individual post pages -└── posts.json # Tracks posts locally -``` - - - - - -### Initialize the Blog - - - - -Create `init.js` — this generates a publisher key, creates an empty blog, uploads it, sets up the feed, and saves the configuration: +The blog regenerates its site from `posts.json` every time a post is created, edited, or deleted. To keep that logic in one place, `init.js` and `post.js` both import a single helper from `html.js`: ```js -import { Bee, Topic, PrivateKey } from "@ethersphere/bee-js"; -import crypto from "crypto"; -import { writeFileSync, mkdirSync } from "fs"; -import { config } from "dotenv"; -config(); - -const bee = new Bee(process.env.BEE_URL); -const batchId = process.env.BATCH_ID; - -// Generate publisher key -const hex = "0x" + crypto.randomBytes(32).toString("hex"); -const pk = new PrivateKey(hex); -const owner = pk.publicKey().address(); -const topic = Topic.fromString("blog"); - -// Create initial empty blog -const posts = []; -writeFileSync("posts.json", JSON.stringify(posts, null, 2)); - -// Generate site files and upload -mkdirSync("site/posts", { recursive: true }); -writeSiteFiles(posts); - -const upload = await bee.uploadFilesFromDirectory(batchId, "./site", { - indexDocument: "index.html", -}); - -// Set up feed and manifest -const writer = bee.makeFeedWriter(topic, pk); -await writer.upload(batchId, upload.reference); -const manifest = await bee.createFeedManifest(batchId, topic, owner); +// html.js +import { writeFileSync, mkdirSync, rmSync } from "fs"; -// Save config -const cfg = { - privateKey: pk.toHex(), - owner: owner.toHex(), - topic: "blog", - manifest: manifest.toHex(), -}; -writeFileSync("config.json", JSON.stringify(cfg, null, 2)); +export function writeSiteFiles(posts) { + rmSync("site", { recursive: true, force: true }); + mkdirSync("site/posts", { recursive: true }); -console.log("Blog initialized!"); -console.log("Feed manifest:", manifest.toHex()); -console.log("View your blog:", `${process.env.BEE_URL}/bzz/${manifest.toHex()}/`); - -// --- HTML generation --- - -function writeSiteFiles(posts) { writeFileSync("site/index.html", generateIndex(posts)); for (const post of posts) { writeFileSync(`site/posts/${post.slug}.html`, generatePost(post)); @@ -493,7 +294,7 @@ function generatePost(post) { return ` ${esc(post.title)} -

← Back

+

← Back

${esc(post.title)}

${post.date}
${esc(post.body)}
@@ -501,88 +302,99 @@ function generatePost(post) { } function esc(s) { - return s.replace(/&/g, "&").replace(//g, ">"); + return s + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); } ``` -Run it once: +`writeSiteFiles(posts)` wipes the local `site/` directory and rebuilds it from the array of posts. The Swarm-side logic — uploading the regenerated directory and pointing the feed at the new reference — lives in `init.js` and `post.js`. -```bash -node init.js -``` +### Initialize the Blog -Example output: +Create `init.js` — this generates a publisher key, creates an empty blog, uploads it, sets up the feed, and saves the configuration: -``` -Blog initialized! -Feed manifest: caa414d70028d14b0bdd9cbab18d1c1a0a3bab1b... -View your blog: http://localhost:1633/bzz/caa414d70028d14b.../ -``` +```js +import { Bee, Topic, PrivateKey } from "@ethersphere/bee-js"; +import crypto from "crypto"; +import { writeFileSync } from "fs"; +import { config } from "dotenv"; +import { writeSiteFiles } from "./html.js"; + +config(); -
- +const bee = new Bee(process.env.BEE_URL); +const batchId = process.env.BATCH_ID; -Create the initial site structure: +// 1. Generate publisher key +const hex = "0x" + crypto.randomBytes(32).toString("hex"); +const pk = new PrivateKey(hex); +const owner = pk.publicKey().address(); +const topic = Topic.fromString("blog"); -```bash -mkdir -p site/posts -``` +// 2. Create initial empty blog +const posts = []; +writeFileSync("posts.json", JSON.stringify(posts, null, 2)); +writeSiteFiles(posts); -Create `site/index.html`: +const upload = await bee.uploadFilesFromDirectory(batchId, "./site", { + indexDocument: "index.html", +}); -```html - -My Blog - -

My Blog

-

0 posts

-

No posts yet.

- +// 3. Set up feed and manifest +const writer = bee.makeFeedWriter(topic, pk); +await writer.upload(batchId, upload.reference); +const manifest = await bee.createFeedManifest(batchId, topic, owner); + +// 4. Save config +const cfg = { + privateKey: pk.toHex(), + owner: owner.toHex(), + topic: "blog", + manifest: manifest.toHex(), +}; +writeFileSync("config.json", JSON.stringify(cfg, null, 2)); + +console.log("Blog initialized!"); +console.log(`View your blog: ${process.env.BEE_URL}/bzz/${manifest.toHex()}/`); ``` -Upload it to a feed: +Run it once: ```bash -swarm-cli feed upload ./site \ - --identity blog-publisher \ - --topic-string blog \ - --stamp BATCH_ID \ - --index-document index.html +node init.js ``` Example output: ``` -Swarm hash: 387dc3cf98419dcb20c68b284373bf7d9e8dcb27... -URL: http://localhost:1633/bzz/387dc3cf.../ -Feed Manifest URL: http://localhost:1633/bzz/6c30ef22.../ +Blog initialized! +Feed manifest: caa414d70028d14b0bdd9cbab18d1c1a0a3bab1b... +View your blog: http://localhost:1633/bzz/caa414d70028d14b.../ ``` -Save the `Feed Manifest URL` hash — this is your blog's permanent address. - -
-
### Create, Edit, and Delete Posts - - - Create `post.js` — a single script that handles creating, editing, and deleting posts. Each operation modifies the local `posts.json`, regenerates the entire site, uploads it, and updates the feed: ```js import { Bee, Topic, PrivateKey } from "@ethersphere/bee-js"; -import { readFileSync, writeFileSync, mkdirSync, rmSync } from "fs"; +import { readFileSync, writeFileSync } from "fs"; import { config } from "dotenv"; +import { writeSiteFiles } from "./html.js"; + config(); const [action, ...args] = process.argv.slice(2); if (!action || !["create", "edit", "delete"].includes(action)) { console.log(`Usage: - node post.js create <body> - node post.js edit <slug> <title> <body> + node post.js create <slug> "<title>" "<body>" + node post.js edit <slug> "<title>" "<body>" node post.js delete <slug>`); process.exit(1); } @@ -592,7 +404,8 @@ const batchId = process.env.BATCH_ID; const cfg = JSON.parse(readFileSync("config.json", "utf-8")); const posts = JSON.parse(readFileSync("posts.json", "utf-8")); -// Apply the action +// --- Apply the action --- + if (action === "create") { const [slug, title, body] = args; if (!slug || !title || !body) { @@ -637,18 +450,11 @@ if (action === "delete") { console.log(`Deleted post: ${slug}`); } -// Save updated posts -writeFileSync("posts.json", JSON.stringify(posts, null, 2)); +// --- Save, regenerate, upload, update feed --- -// Regenerate entire site -rmSync("site", { recursive: true, force: true }); -mkdirSync("site/posts", { recursive: true }); -writeFileSync("site/index.html", generateIndex(posts)); -for (const post of posts) { - writeFileSync(`site/posts/${post.slug}.html`, generatePost(post)); -} +writeFileSync("posts.json", JSON.stringify(posts, null, 2)); +writeSiteFiles(posts); -// Upload and update feed const pk = new PrivateKey(cfg.privateKey); const topic = Topic.fromString(cfg.topic); const writer = bee.makeFeedWriter(topic, pk); @@ -660,44 +466,6 @@ await writer.upload(batchId, upload.reference); console.log(`Blog updated! (${posts.length} post${posts.length !== 1 ? "s" : ""})`); console.log(`View: ${process.env.BEE_URL}/bzz/${cfg.manifest}/`); - -// --- HTML generation --- - -function generateIndex(posts) { - const items = posts - .sort((a, b) => new Date(b.date) - new Date(a.date)) - .map( - (p) => ` - <article style="margin:16px 0; padding:16px; border:1px solid #ddd; border-radius:4px;"> - <h2 style="margin:0 0 4px 0;"><a href="posts/${p.slug}.html">${esc(p.title)}</a></h2> - <small style="color:#888;">${p.date}</small> - </article>` - ) - .join("\n"); - - return `<!DOCTYPE html> -<html><head><meta charset="utf-8"><title>My Blog - -

My Blog

-

${posts.length} post${posts.length !== 1 ? "s" : ""}

- ${items || "

No posts yet.

"} -`; -} - -function generatePost(post) { - return ` -${esc(post.title)} - -

← Back

-

${esc(post.title)}

- ${post.date} -
${esc(post.body)}
-`; -} - -function esc(s) { - return s.replace(/&/g, "&").replace(//g, ">"); -} ``` Usage: @@ -722,75 +490,11 @@ Each command regenerates the entire site and updates the feed. The same feed man Notice that editing and deleting work the same way as creating — modify the local data, regenerate the site, re-upload, update the feed. Swarm itself doesn't have "edit" or "delete" operations. The old versions of the site remain accessible via their direct Swarm hashes, but the feed manifest always resolves to the latest version. ::: -
- - -With `swarm-cli`, you manage the site files manually (or with a helper script), then re-upload: - -#### Create a post - -Add a new post file at `site/posts/hello-world.html`: - -```html - -Hello World - -

← Back

-

Hello World

- 2025-06-15 -
This is my first blog post on Swarm.
- -``` - -Update `site/index.html` to link to it: - -```html - -My Blog - -

My Blog

-

1 post

- - -``` - -Upload to the feed: - -```bash -swarm-cli feed upload ./site \ - --identity blog-publisher \ - --topic-string blog \ - --stamp BATCH_ID \ - --index-document index.html -``` - -#### Edit a post - -Edit the HTML file at `site/posts/hello-world.html` with the updated title and body, then re-upload with the same command above. - -#### Delete a post - -Remove the post file from `site/posts/` and update `site/index.html` to remove the link, then re-upload. - -In every case, the operation is the same on the Swarm side: modify local files, then re-upload the `site/` folder to the feed. The feed manifest URL remains unchanged. - -:::tip -You can automate the HTML generation with a simple shell or Node.js script that reads posts from a local JSON file and writes the HTML before uploading. The key point is that the Swarm side of things is always the same: regenerate content, upload, update feed. -::: - -
-
### Read the Feed This demonstrates how anyone can read the feed without the publisher's private key — only the owner address and topic (or the manifest hash) are needed: - - - Create `read.js`: ```js @@ -806,7 +510,7 @@ const topic = Topic.fromString(cfg.topic); const owner = new EthAddress(cfg.owner); const reader = bee.makeFeedReader(topic, owner); -const result = await retryFeedRead(() => reader.downloadReference()); +const result = await reader.downloadReference(); console.log("Latest content reference:", result.reference.toHex()); console.log("Feed index:", result.feedIndex.toBigInt()); console.log("View:", `${process.env.BEE_URL}/bzz/${cfg.manifest}/`); @@ -818,23 +522,6 @@ Run it: node read.js ``` - - - -```bash -swarm-cli feed print \ - --identity blog-publisher \ - --topic-string blog -``` - -Or simply open the feed manifest URL in a browser: - -```text -http://localhost:1633/bzz/FEED_MANIFEST_HASH/ -``` - - - ## Summary @@ -843,6 +530,8 @@ Feeds add a mutable pointer layer on top of Swarm's immutable storage. The core This is the same pattern that [Etherjot](https://github.com/ethersphere/etherjot) uses to power fully decentralized blogs — regenerate the site, re-upload, and update the feed. The difference is only in scale and features (markdown rendering, categories, media management), not in the underlying feed mechanics. +The `simple-blog` project you just built is a single-author blog. The next guide extends this into a multi-author system where each author controls their own feed and an admin maintains an index feed that links them all together. + Key takeaways: - Every upload to Swarm is immutable and produces a unique hash. @@ -851,3 +540,7 @@ Key takeaways: - A **feed manifest** wraps the feed identity into a single permanent hash that resolves through `/bzz/`. - Only the feed owner (holder of the private key) can publish updates, but anyone can read the feed. - "Editing" and "deleting" content on Swarm means regenerating your site without the removed or changed content, re-uploading, and updating the feed. Old versions remain on Swarm at their original hashes, but the feed always points to the latest. + +--- + +**Next:** [Multi-Author Blog](/docs/develop/multi-author-blog) — extend feeds into a multi-publisher system where each author controls their own feed and a shared index links them together. \ No newline at end of file diff --git a/docs/develop/files.md b/docs/develop/files.md index 7bbde0a27..c6fedb0bc 100644 --- a/docs/develop/files.md +++ b/docs/develop/files.md @@ -5,11 +5,11 @@ sidebar_label: Manage Files description: Guide for working with file operations including chunking and handling in Swarm applications. --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; # Manage Files +In the [Host a Webpage](/docs/develop/host-your-website) guide you uploaded a directory and got back a single Swarm reference that serves your site. That reference points to a **manifest** — a data structure that maps relative paths to content. This guide explores manifests directly: how to inspect them, add a file without re-uploading everything, and move a file by remapping a path. + Swarm does not have a traditional filesystem — there are no mutable directories, in-place updates, or a built-in directory structure that preserves relationships between files. Instead, these capabilities are provided through the use of [manifests](./tools-and-features/manifests.md), which map relative paths (such as `/images/cat.jpg`) to immutable Swarm content references. When you upload a directory, Bee creates a manifest automatically and returns its reference. Files can then be accessed using paths that are relative to that manifest reference, based on the original directory structure. This provides filesystem-like behavior for your data, and the directory structure can later be changed by publishing a new version of the manifest with the desired updates. ## Usage and Example Scripts @@ -41,14 +41,18 @@ cd examples/filesystem npm install ``` -Update the `` in the `.env` file with a valid batch ID, and make sure that `BEE_URL` is set to the RPC endpoint for your Bee node: +Copy `.env.example` to `.env` and fill in your values: + +```bash +cp .env.example .env +``` ```bash BEE_URL=http://localhost:1633 # or http://127.0.0.1:1633 -BATCH_ID= +BATCH_ID= UPLOAD_DIR=./folder -SCRIPT_02_MANIFEST= -SCRIPT_03_MANIFEST= +SCRIPT_02_MANIFEST= +SCRIPT_03_MANIFEST= ``` ## Script 1: Upload Folder and Inspect Manifest @@ -437,3 +441,6 @@ No data is duplicated, the `new.txt` file has not been modified, only the path m With these tools, you can treat Swarm directories much like a filesystem — while still preserving immutability and content addressing. +--- + +**Next:** [Website Routing](/docs/develop/routing) — put manifest path mapping to practical use by setting up clean URL routing for a Swarm-hosted site. diff --git a/docs/develop/gateway.md b/docs/develop/gateway.md index e0f1233d2..4b0313c27 100644 --- a/docs/develop/gateway.md +++ b/docs/develop/gateway.md @@ -4,7 +4,9 @@ id: gateway-proxy description: Explains Bee gateway functionality for accessing Swarm content through HTTP interfaces. --- -This guide explains how to use the [swarm-gateway](https://github.com/ethersphere/swarm-gateway) tool to set up your node in gateway mode. Running your node in gateway mode exposes it publicly, allowing access through any typical browser or http API. +At this point you can build and deploy complete Swarm-hosted websites with working routing. This guide is an **operational step**: it shows how to make those sites accessible to the public web through an HTTP gateway, so that anyone with a browser can reach them without running their own Bee node. + +This guide explains how to use the [swarm-gateway](https://github.com/ethersphere/swarm-gateway) tool to set up your node in gateway mode. Running your node in gateway mode exposes it publicly, allowing access through any typical browser or http API. It is divided into several parts: @@ -65,14 +67,12 @@ This part of the guide does not cover setting up TLS, so your gateway will be ac ### 1. Configure DNS for your domain -Create an A record in your DNS provider: +Create an A record in your DNS provider pointing your domain to your server's IP address: ```text your-domain.example -> ``` -ADD SCREENSHOT - After DNS propagation, verify that the domain resolves to your server (this may take some time, to verify more quickly, try pinging from a different machine or VPS): ```bash @@ -101,7 +101,7 @@ The output should list `bee-1` as an attached container. ### 3. Pull the gateway image ```bash -docker pull ethersphere/swarm-gateway:0.1.3 +docker pull ethersphere/swarm-gateway:0.1.6 ``` ### 4. Run the gateway @@ -116,7 +116,7 @@ docker run -d --restart unless-stopped \ -e HOSTNAME="your-domain.example" \ -e BEE_API_URL="http://bee-1:1633" \ -e DATABASE_CONFIG="{}" \ - ethersphere/swarm-gateway:0.1.3 + ethersphere/swarm-gateway:0.1.6 ``` In this configuration, database-backed features such as subdomain rewrites and moderation are not configured. @@ -135,24 +135,25 @@ Expected output: OK ``` -### 6. Test with uploaded content +### 6. Test with existing content -Upload a file using Bee: +To confirm the gateway is correctly serving content from Swarm, request a reference that is already on the network. The following hash points to a small JSON file: -```bash -echo "hello swarm" > test.txt -swarm-cli upload test.txt +```text +http://your-domain.example/bzz/f3f5e25c90824876c2468b9bdf0d842cd05dc5f0974681789b9729bc155c4f65/ ``` -This will print a Swarm reference. - -Open the file through the gateway: +Expected output: -```text -http://your-domain.example/bzz// +```json +{ + "octalmage.com": "bzz://45f0f1e13b70e2919e59fdc5bcf3a99bcbe19dc1be6ebdebe3f89794b77c19ab/", + "o8.is": "bzz://4cd43b1c0ebc257f79cc45ebd9774e1251e34f08026325c78ef2ca46972935cc/", + "dist.o8.is": "bzz://0890110b61109aee2b6f0d071cedce584868bb29dcb7e41b1c0388d6cf775ace/" +} ``` -The file contents should be returned. +If the JSON is returned, your gateway is correctly fetching and serving content from Swarm. To serve your own content, upload a file or website through your Bee node (see the [Upload and Download](/docs/develop/upload-and-download) and [Host a Webpage](/docs/develop/host-your-website) guides) and use the resulting reference in place of the one above. ### 7. Optional: restrict uploads using authentication @@ -228,7 +229,7 @@ docker run -d --restart unless-stopped \ -e HOSTNAME="your-domain.example" \ -e BEE_API_URL="http://bee-1:1633" \ -e DATABASE_CONFIG="{}" \ - ethersphere/swarm-gateway:0.1.3 + ethersphere/swarm-gateway:0.1.6 ``` The gateway is now only accessible from within the Docker network. @@ -300,3 +301,6 @@ You can also verify that HTTP is redirected to HTTPS: curl -I http://your-domain.example/health ``` +--- + +**Next:** [Dynamic Content](/docs/develop/dynamic-content) — return to app development and learn how feeds add a mutable pointer layer on top of Swarm's immutable storage. diff --git a/docs/develop/host-your-website.md b/docs/develop/host-your-website.md index 029874293..c51eace62 100644 --- a/docs/develop/host-your-website.md +++ b/docs/develop/host-your-website.md @@ -4,295 +4,24 @@ id: host-your-website description: Comprehensive guide for uploading and hosting websites on Swarm with content addressing. --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -There are two primary methods most developers should use for uploading a website to Swarm - `swarm-cli` and `bee-js`. Depending on the specific use case, it may make more sense to pick one or the other. +In the [Upload and Download](/docs/develop/upload-and-download) guide you uploaded individual files and got back Swarm reference hashes. A website is just a collection of files — an HTML page, a stylesheet, maybe an image. When you upload a directory, Bee automatically builds a [manifest](/docs/develop/tools-and-features/manifests) that maps each relative path to its content. Set an `indexDocument` and the root URL resolves to your homepage. -For a simple website such as a personal blog or company page, using `swarm-cli` is simplest and fastest way to get your site uploaded and accessible on Swarm. +This guide shows how to upload a static site and open it through `/bzz//`. -However for developers who need finer grained control over the process or who wish to build a more complex application which require programmatically spinning up new pages, `bee-js` is required. - -:::tip -The guides below assume you already have a registered ENS domain name. By using an ENS domain name, you can make your Swarm hosted website accessible through an easy to remember human-readable name rather than a Swarm hash. If you don't have an ENS domain name registered, you can get one using the official ENS application at [app.ens.domains](https://app.ens.domains/). Refer to their support section for a step-by-step guide to [register an ENS domain](https://support.ens.domains/en/articles/7882582-how-to-register-a-eth-name). +:::info Example project +The example website used in this guide is in [`examples/website`](https://github.com/ethersphere/examples/tree/main/website). Clone the repo, copy `.env.example` to `.env`, fill in your values, and run `npm install && npm run upload`. ::: -:::tip FIX FOR ENS NOT WORKING ON LOCALHOST -If the site doesn’t load from localhost, it’s may be an issue with the resolver RPC (the RPC endpoint for the Ethereum node used to resolve your ENS domain name). This is even more likely if you are using a free RPC since they generally don't come with the same guarantees as paid ones. - -Some endpoints, such as... - -``` -https://cloudflare-eth.com -``` - -...may not resolve properly on localhost. Even if they previously worked, it may be the case that the RPC has been [changed or removed](https://developers.cloudflare.com/web3/reference/migration-guide/?utm_source=chatgpt.com). - - -As of the writing of this guide, both of these free and public endpoints work reliably for localhost resolution: - -``` -https://mainnet.infura.io/v3/ -https://eth-mainnet.public.blastapi.io -``` - -Alternatively, you can run your own Ethereum node and use that as the RPC. -::: - -## Host a Site With **swarm-cli** - -This guide shows you how to get your website hosted on Swarm with just a few simple commands by using `swarm-cli` from your terminal. - -### Prerequisites - -* A running Bee node (either a [standard installation](./../bee/installation/quick-start.md) or [Swarm Desktop](./../desktop/install.md)) -* A valid postage batch -* [`swarm-cli` installed](https://docs.ethswarm.org/docs/bee/working-with-bee/swarm-cli) -* A valid postage stamp batch -* Your static website files (you can also use the example website files provided below) -* (Optional for part one - "Upload & Access by Hash") An ENS domain which you [previously registered](https://support.ens.domains/en/articles/7882582-how-to-register-a-eth-name) - -### Upload & Access by Hash - - -You can download the example website files from the [ethersphere/examples](https://github.com/ethersphere/examples/tree/main/website) repository. - - -#### Uploading the Website - -1. Go to the folder containing your website files. - -The example website files look like this: - -``` -my-website/ -├── index.html # main landing page -├── 404.html # custom error page -├── styles.css # basic styling -├── script.js # optional script -├── favicon.svg # site icon -└── robots.txt # default robots config -``` - -* `index.html` will be served by default when users visit the root URL. -* `404.html` will be served for non-existent paths. -* The other files are optional and can be customized. - - -2. Run: - - - - - -```powershell -swarm-cli upload . ` - --stamp ` - --index-document index.html ` - --error-document 404.html -```` - - - - - -```bash -swarm-cli upload . \ - --stamp \ - --index-document index.html \ - --error-document 404.html -``` - - - - - -* Replace `` with your postage batch ID. -* `--index-document` tells Bee which file to serve at the root. -* `--error-document` defines the fallback file for missing paths. - -3. The upload will return a Swarm reference hash, for example: - -``` -cf50756e6115445fd283691673fa4ad2204849558a6f3b3f4e632440f1c3ab7c -``` - -Copy this and save it. You’ll need it for both direct access and [ENS integration](#connect-site-to-ens-domain). - - -#### Accessing the Website - -Anyone with a Bee node can now access the site using the Swarm hash you just saved: - -``` -http://localhost:1633/bzz// -``` - - -### Use Feeds for Seamless Updates - swarm-cli - -*If you have not already connected your site to your ENS domain, [do that now](#connect-site-to-ens-domain) before returning here.* - -If you have an ENS domain and Swarm hosted website, you can make the site available through the domain by registering website's Swarm hash as a content hash through the [ENS domain management app](https://app.ens.domains/). However, if you ever edit and reupload your site to Swarm, you will need to re-register your new website hash to make it available at your ENS domain. - -Therefore, instead of directly using your website hash as the content hash for your ENS domain, upload your site as a feed update and use the feed manifest hash as the content hash. Then every time you update your site as a new feed update, the ENS domain will always resolve to the newest version of your site without the need to register a new hash each time. - -:::tip -The examples below refer to core feed concepts such as "publisher identity", and "topic". To learn more about these concepts refer to the [bee-js documentation](https://bee-js.ethswarm.org/docs/soc-and-feeds/#feeds). -::: - -In this section, you will: - -1. Create a publisher identity -2. Upload your site to a feed (this automatically creates the feed manifest) -3. Copy the feed manifest reference -4. Use that manifest reference as your ENS contenthash - - -#### Step 1: Create a dedicated publisher identity - -This key will sign feed updates. - -```bash -swarm-cli identity create website-publisher -``` - -Terminal output: - -```bash -Name: website-publisher -Type: V3 Wallet -Private key: 0x22e918ef68c9bc975112ceaaee0ee0f147baa79da257873659bddbfd84a646fe -Public key: 0x218c79f8dfb26d077b6379eb56aa9c6e71edf74dde8ecd27dac5016528aea80ee121b9e5050adf3948c8b0d8cffda763d7fb1f5608250b5009c5d50e158ab4a5 -Address: 0x2fb11d37a9913bd3258b9918c399f35fd842a232 -``` - -Record the output in a secure location as a backup — you will need this identity for future updates. - -If you need to view/export it later: - -```bash -swarm-cli identity export website-publisher -``` - -#### Step 2: Upload your website to a feed (creates the manifest automatically) - -Don't forget to replace `` with your own valid batch ID before running the commands below: - - - - -```bash -swarm-cli feed upload ./website \ - --identity website-publisher \ - --topic-string website \ - --stamp \ - --index-document index.html \ - --error-document 404.html -``` - - - - -```powershell -swarm-cli feed upload .\website ` - --identity website-publisher ` - --topic-string website ` - --stamp ` - --index-document index.html ` - --error-document 404.html -``` - - - -You will see output that includes your **feed manifest reference**, for example: - -```bash -Swarm hash: 387dc3cf98419dcb20c68b284373bf7d9e8dcb27daadb67e1e6b6e0f17017f1f -URL: http://localhost:1633/bzz/387dc3cf98419dcb20c68b284373bf7d9e8dcb27daadb67e1e6b6e0f17017f1f/ -Feed Manifest URL: http://localhost:1633/bzz/6c30ef2254ac15658959cb18dd123bcce7c16d06fa7d0d4550a1ee87b0a846a2/ -Stamp ID: 3d98a22f -Usage: 50% -Capacity (mutable): 20.445 KB remaining out of 40.890 KB -``` - -You can find the manifest hash at `Feed Manifest URL` in the URL right after `/bzz/`: `6c30ef2254ac15658959cb18dd123bcce7c16d06fa7d0d4550a1ee87b0a846a2` - -Save this hash, you will use it for the next step. - -This is your **permanent website reference**. It is a reference to a feed manifest which points to the latest feed entry so that you can use it as a static, unchanging reference for your website even as you make multiple updates to the site. Every time you update the website through the feed, this manifest will point to the hash for the newest version of the website. - - -#### Step 3: Use the feed reference as the ENS contenthash - -Follow the [official ENS guide](https://support.ens.domains/en/articles/12275979-how-to-add-a-decentralized-website-to-an-ens-name) for registering a content hash adding your content hash in the ENS UI (see [guide](#connect-site-to-ens-domain)). However, rather than registering your website's hash directly, register the feed manifest hash we saved from the previous step from our example above. - -Example: - -``` -bzz://6c30ef2254ac15658959cb18dd123bcce7c16d06fa7d0d4550a1ee87b0a846a2 -``` - -Now your ENS name will always point to a static reference which will always resolve to the latest version of your website. - -#### Updating your site in the future - -When you have a new version of your site, just run `feed upload` again using the same topic and identity: - - - - -```bash -swarm-cli feed upload ./website \ - --identity website-publisher \ - --topic-string website \ - --stamp \ - --index-document index.html \ - --error-document 404.html -``` - - - - -```powershell -swarm-cli feed upload .\website ` - --identity website-publisher ` - --topic-string website ` - --stamp ` - --index-document index.html ` - --error-document 404.html -``` - - - - -* The **feed manifest reference stays the same**. -* The feed now points to the newly uploaded site version. -* No ENS changes needed. - - -## Host a Website with **bee-js** - -This guide explains how to host a website on Swarm using the `bee-js` JavaScript SDK instead of the CLI. - -For developers building apps, tools, or automated deployments, `bee-js` offers programmatic control over uploading and updating content on Swarm. - -### Prerequisites +## Prerequisites * A running Bee node (either a [standard installation](./../bee/installation/quick-start.md) or [Swarm Desktop](./../desktop/install.md)) * A valid postage stamp batch * Node.js (18+) and `@ethersphere/bee-js` installed in your project -* Static website files (HTML, CSS, etc.) - feel free to use the [provided example site](https://github.com/ethersphere/examples/tree/main/website) -* (Optional for part one - "Upload & Access by Hash") An ENS domain which you [previously registered](https://support.ens.domains/en/articles/7882582-how-to-register-a-eth-name) - +* Static website files (HTML, CSS, etc.) — feel free to use the [provided example site](https://github.com/ethersphere/examples/tree/main/website) -### Upload and Access by Hash +## Upload and Access by Hash Install bee-js: @@ -328,13 +57,13 @@ http://localhost:1633/bzz// ``` -### Use Feeds for Seamless Updates - bee-js +## Advanced: Keep Your URL Stable Across Updates -*If you have not already connected your site to your ENS domain, [do that now](#connect-site-to-ens-domain) before returning here.* - -If you have an ENS domain and Swarm hosted website, you can make the site available through the domain by registering website's Swarm hash as a content hash through the [ENS domain management app](https://app.ens.domains/). However, if you ever edit and reupload your site to Swarm, you will need to re-register your new website hash to make it available at your ENS domain. +:::note Prerequisite +This section uses feeds — the concept of a mutable pointer on top of Swarm's immutable storage. Feeds are explained from scratch in the [Dynamic Content](/docs/develop/dynamic-content) guide. You can complete this guide without this section and come back after you have worked through Dynamic Content. +::: -Therefore, instead of directly using your website hash as the content hash for your ENS domain, upload your site as a feed update and use the feed manifest hash as the content hash. Then every time you update your site as a new feed update, the ENS domain will always resolve to the newest version of your site without the need to register a new hash each time. +Every time you re-upload a site to Swarm, you get a new reference hash. If you want a single stable URL that always points to the latest version of your site — useful for ENS integration or sharing a permanent link — publish each upload as a feed entry and share the feed manifest hash instead of the content hash. :::tip You will need a publisher key to use for setting up your website feed. @@ -414,7 +143,7 @@ Website Swarm Hash: 6c45eae389b3bffce21443316d0bd47c4101545092b7c72c313a33ee7d00 Feed Manifest: caa414d70028d14b0bdd9cbab18d1c1a0a3bab1b20a56cf06937a6b20c7e7377 ``` -Follow the [official ENS guide](https://support.ens.domains/en/articles/12275979-how-to-add-a-decentralized-website-to-an-ens-name) for registering a content hash adding your content hash in the ENS UI (see [guide](#connect-site-to-ens-domain)). However, rather than registering your website's hash directly, register the feed manifest hash we saved from the previous step from our example above. +Follow the [official ENS guide](https://support.ens.domains/en/articles/12275979-how-to-add-a-decentralized-website-to-an-ens-name) for registering a content hash adding your content hash in the ENS UI (see [guide](#optional-connect-site-to-ens-domain)). However, rather than registering your website's hash directly, register the feed manifest hash we saved from the previous step from our example above. ``` bzz:// @@ -430,7 +159,7 @@ Your ENS domain will always point to the latest upload via the feed manifest. You’ve now got a programmatic way to deploy and update your Swarm-hosted site with ENS support using `bee-js`! -## Connect Site to ENS Domain +## Optional: Connect Site to ENS Domain Once your site is uploaded to Swarm, you can make it accessible via an easy to remember ENS domain name rather than its Swarm hash: @@ -464,7 +193,11 @@ The guide covers: When you reach Step 2 in the ENS guide (“Add content hash record”), enter your Swarm reference in the following format: :::tip -For the content hash, you can use a Swarm hosted website's hash directly, or as is recommended in the [`swarm-cli`](#use-feeds-for-seamless-updates---swarm-cli) and [`bee-js`](#use-feeds-for-seamless-updates---bee-js) guides above, publish your site to a feed and use the feed manifest hash instead. By using a feed manifest as the content hash, you can avoid repeated ENS registry updates. +For the content hash, you can use a Swarm-hosted website's hash directly, or — as recommended in the [Advanced: Keep Your URL Stable Across Updates](#advanced-keep-your-url-stable-across-updates) section above — publish your site to a feed and use the feed manifest hash instead. By using a feed manifest as the content hash, you can avoid repeated ENS registry updates. +::: + +:::tip If ENS does not resolve on localhost +If the site doesn't load from `http://localhost:1633/bzz/yourname.eth/`, the issue is usually the ENS resolver RPC. Free public endpoints like `https://cloudflare-eth.com` [may not resolve reliably](https://developers.cloudflare.com/web3/reference/migration-guide/?utm_source=chatgpt.com). Reliable alternatives include `https://mainnet.infura.io/v3/` and `https://eth-mainnet.public.blastapi.io`, or run your own Ethereum node. ::: ``` @@ -485,3 +218,6 @@ This works across: You do not need to encode the hash or use any additional tools. `bzz://` is sufficient. +--- + +**Next:** [Manage Files](/docs/develop/files) — learn how manifests provide filesystem-like path mapping and how to add, move, or remove files without re-uploading everything. diff --git a/docs/develop/introduction.md b/docs/develop/introduction.md index b8be3b42d..f2f21e325 100644 --- a/docs/develop/introduction.md +++ b/docs/develop/introduction.md @@ -64,8 +64,8 @@ This is the go-to starting point for web3 developers who want to build with Swar
  • - -

    Working with the "Filesystem"

    +
    +

    Manage Files

    Learn about how manifests enable a virtual "filesystem" on Swarm, and how to manipulate the manifest to re-write virtual paths to add, remove, or move content.

    @@ -74,7 +74,7 @@ This is the go-to starting point for web3 developers who want to build with Swar
  • -

    Routing

    +

    Website Routing

    Learn about routing on Swarm and the various options at your disposal for approaching website routing.

    diff --git a/docs/develop/multi-author-blog.md b/docs/develop/multi-author-blog.md index 0781a242a..e5eaac39e 100644 --- a/docs/develop/multi-author-blog.md +++ b/docs/develop/multi-author-blog.md @@ -5,8 +5,6 @@ sidebar_label: Multi-Author Blog description: Build a decentralized multi-author blog on Swarm using linked feeds — each author has their own feed, and a master index feed ties them together. --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; This guide extends the [Dynamic Content](/docs/develop/dynamic-content) pattern into a multi-author system. Instead of a single publisher managing one feed, each author independently controls their own feed, and an admin maintains an index feed that links them all together. This demonstrates the core architectural pattern needed for decentralized networks: **feeds that reference other feeds**. @@ -16,8 +14,7 @@ The key insight is that a feed entry does not have to point to HTML content — * A running Bee node ([install guide](/docs/bee/installation/quick-start)) * A valid postage stamp batch ([how to get one](/docs/develop/tools-and-features/buy-a-stamp-batch)) -* Node.js 18+ and `@ethersphere/bee-js` installed (for `bee-js` examples) -* [`swarm-cli` installed](https://docs.ethswarm.org/docs/bee/working-with-bee/swarm-cli) (for `swarm-cli` examples) +* Node.js 18+ and `@ethersphere/bee-js` installed * Familiarity with the [Dynamic Content](/docs/develop/dynamic-content) guide and feeds ## Architecture @@ -71,13 +68,14 @@ Clone the repo and set up the project: git clone https://github.com/ethersphere/examples.git cd examples/multi-author-blog npm install +cp .env.example .env ``` -Update the `BATCH_ID` in the `.env` file with a valid batch ID, and make sure `BEE_URL` points to your Bee node: +Fill in your `BATCH_ID` in `.env`: ```bash BEE_URL=http://localhost:1633 -BATCH_ID=YOUR_BATCH_ID +BATCH_ID= ``` ## Example Project — Multi-Author Blog @@ -86,54 +84,18 @@ This section builds a complete runnable project: a blog where multiple authors p ### Project Setup - - - -Create a new directory and install dependencies: - -```bash -mkdir swarm-multiblog && cd swarm-multiblog -npm init -y -npm install @ethersphere/bee-js dotenv -``` - -Add `"type": "module"` to your `package.json` to use ES module imports. - -Create a `.env` file: +Use the cloned examples repo (see [Example Scripts](#example-scripts) above): ```bash -BEE_URL=http://localhost:1633 -BATCH_ID= -``` - - - - -Create the project directory and author folders: - -```bash -mkdir swarm-multiblog && cd swarm-multiblog -mkdir -p blog/alice blog/bob blog/home -``` - -Set up three identities — one for each author and one for the admin: - -```bash -swarm-cli identity create blog-admin -swarm-cli identity create alice -swarm-cli identity create bob +cd examples/multi-author-blog +npm install +cp .env.example .env +# Fill in BEE_URL and BATCH_ID in .env ``` -Keep the output of each command — you'll need the owner addresses and any other identity information for later steps. - - - ### Project Structure - - - ``` swarm-multiblog/ ├── .env @@ -147,33 +109,11 @@ swarm-multiblog/ └── read.js # Read the feeds without private keys ``` - - - -``` -swarm-multiblog/ -├── blog/ -│ ├── alice/ -│ │ └── index.html # Alice's latest blog page -│ ├── bob/ -│ │ └── index.html # Bob's latest blog page -│ └── home/ -│ └── index.html # Homepage with aggregated posts -├── authors.json # Directory of authors (manifest hashes, topics, owners) -├── alice-posts.json # Local tracking of Alice's posts -└── bob-posts.json # Local tracking of Bob's posts -``` - - - ### Initialize the Blog This step generates keys for all authors and the admin, creates feeds for each author and for the homepage, builds the index feed with an `authors.json` manifest, and saves everything to `config.json`. - - - Create `init.js`: ```js @@ -350,6 +290,7 @@ Run it once to initialize the blog: ```bash node init.js +# or: npm run init ``` Example output: @@ -365,130 +306,11 @@ Alice's feed: http://localhost:1633/bzz/3fa19c.../ Bob's feed: http://localhost:1633/bzz/7c244b.../ ``` - - - -Create initial HTML files for each author. Start with Alice: - -```bash -cat > blog/alice/index.html << 'EOF' - - -Alice's Blog - -

    Alice's Blog

    -

    No posts yet.

    - - -EOF -``` - -Do the same for Bob: - -```bash -cat > blog/bob/index.html << 'EOF' - - -Bob's Blog - -

    Bob's Blog

    -

    No posts yet.

    - - -EOF -``` - -Upload Alice's initial page to her feed and save the manifest hash: - -```bash -swarm-cli feed upload ./blog/alice \ - --identity alice \ - --topic-string alice-posts \ - --stamp \ - --index-document index.html -``` - -This outputs a `Feed Manifest URL`. Extract the hash and save it as `ALICE_MANIFEST`. Do the same for Bob and save as `BOB_MANIFEST`. - -Now create `authors.json` with your manifest hashes and owner addresses (from your `swarm-cli identity` output earlier): - -```json -[ - { - "name": "Alice", - "topic": "alice-posts", - "owner": "", - "feedManifest": "" - }, - { - "name": "Bob", - "topic": "bob-posts", - "owner": "", - "feedManifest": "" - } -] -``` - -Upload `authors.json` to the index feed: - -```bash -swarm-cli feed upload authors.json \ - --identity blog-admin \ - --topic-string blog-index \ - --stamp -``` - -Save the `Feed Manifest URL` hash as `INDEX_MANIFEST`. - -Finally, create the homepage and upload it to the home feed: - -```bash -cat > blog/home/index.html << 'EOF' - - -Multi-Author Blog - -

    Multi-Author Blog

    -

    2 authors

    -
    -

    Alice

    -

    No posts yet.

    -
    -
    -

    Bob

    -

    No posts yet.

    -
    - - -EOF -``` - -Upload it: - -```bash -swarm-cli feed upload ./blog/home \ - --identity blog-admin \ - --topic-string blog-home \ - --stamp \ - --index-document index.html -``` - -Save the `Feed Manifest URL` hash as `HOME_MANIFEST`. This is your permanent blog URL. - -:::info -In the swarm-cli workflow, `authors.json` and all state tracking is maintained manually or by local shell scripts. The swarm-cli commands handle uploads and feed updates; local file organization is left to you. For automation, you could write a shell script that regenerates `blog/home/index.html` and re-runs the feed upload. -::: - - - ### Add a Post Authors publish independently. Each author regenerates their blog page with the new post, uploads it, and updates their feed. The admin can then aggregate the latest posts into the homepage. - - - Create `add-post.js`: ```js @@ -576,83 +398,18 @@ Run it: ```bash node add-post.js alice "Hello Swarm" "My first post on a decentralized blog." node add-post.js bob "Why Swarm?" "Censorship resistance matters." +# or: npm run add-post -- alice "Hello Swarm" "My first post on a decentralized blog." ``` :::tip Authors are fully independent. Bob can publish a post without Alice's involvement, without any coordination, and without running any admin script. Each author controls only their own private key and topic. The admin (homepage aggregator) runs separately and at their own discretion. ::: - - - -Edit `blog/alice/index.html` to include the new post: - -```bash -cat > blog/alice/index.html << 'EOF' - - -Alice's Blog - -

    Alice's Blog

    -

    1 post

    -
    -

    Hello Swarm

    - 2025-06-15 -

    My first post on a decentralized blog.

    -
    - - -EOF -``` - -Re-upload to Alice's feed: - -```bash -swarm-cli feed upload ./blog/alice \ - --identity alice \ - --topic-string alice-posts \ - --stamp \ - --index-document index.html -``` - -The Feed Manifest URL stays the same. Alice's page now shows the new post. Do the same for Bob: - -```bash -cat > blog/bob/index.html << 'EOF' - - -Bob's Blog - -

    Bob's Blog

    -

    1 post

    -
    -

    Why Swarm?

    - 2025-06-15 -

    Censorship resistance matters.

    -
    - - -EOF - -swarm-cli feed upload ./blog/bob \ - --identity bob \ - --topic-string bob-posts \ - --stamp \ - --index-document index.html -``` - -Each author's feed manifest URL always serves their latest posts. - -
    -
    ### Update the Homepage The admin aggregates all author feeds and publishes an updated homepage with previews of their latest posts. This is the key demonstration of feeds referencing feeds: the aggregator reads the index feed to discover authors, then reads each author's feed to fetch their latest content. - - - Create `update-index.js`: ```js @@ -735,6 +492,7 @@ Run it after authors publish: ```bash node update-index.js +# or: npm run update-index ``` The homepage now displays previews of the latest posts from all authors. The homepage feed manifest URL (`cfg.manifests.home`) always serves the aggregated view. @@ -743,60 +501,11 @@ The homepage now displays previews of the latest posts from all authors. The hom The `update-index.js` script reads local JSON sidecars (`alice-posts.json`, `bob-posts.json`) to populate post previews. In a production system, each post would be a separate Swarm upload, and the author's feed would store a JSON post-list reference (topic + manifest hash) instead of raw HTML. See [Etherjot](https://github.com/ethersphere/etherjot) for a full-featured example of this approach. ::: - - - -After authors have published posts, update the homepage HTML to include previews and links: - -```bash -cat > blog/home/index.html << 'EOF' - - -Multi-Author Blog - -

    Multi-Author Blog

    -

    2 authors

    -
    -

    Alice

    -

    Hello Swarm — 2025-06-15

    -

    My first post on a decentralized blog.

    -
    -
    -

    Bob

    -

    Why Swarm? — 2025-06-15

    -

    Censorship resistance matters.

    -
    - - -EOF -``` - -Re-upload the homepage to its feed: - -```bash -swarm-cli feed upload ./blog/home \ - --identity blog-admin \ - --topic-string blog-home \ - --stamp \ - --index-document index.html -``` - -The homepage feed manifest URL (your permanent blog URL) now displays aggregated posts. The `` links point to each author's feed manifest hash — stable, permanent URLs that always resolve to the latest version of each author's page. - -:::tip -Notice: you did not edit the links to Alice or Bob's feeds. They are permanent feed manifest hashes. When Alice publishes a new post, her feed manifest URL automatically serves the new content. No link updates needed. -::: - -
    -
    ### Read the Blog Any third party can read the blog and discover all authors using only the index feed manifest hash. No private keys are needed. - - - Create `read.js`: ```js @@ -827,7 +536,7 @@ for (const author of authors) { const owner = new EthAddress(author.owner); const reader = bee.makeFeedReader(topic, owner); try { - const result = await reader.downloadReference(); + const result = await reader.download(); console.log(`${author.name}`); console.log(` Feed index: ${result.feedIndex.toBigInt()}`); console.log(` URL: ${process.env.BEE_URL}/bzz/${author.feedManifest}/`); @@ -849,58 +558,19 @@ Run it: ```bash node read.js +# or: npm run read ``` - - - -Print the state of all feeds: - -```bash -# Read the index feed -swarm-cli feed print \ - --identity blog-admin \ - --topic-string blog-index - -# Read each author's feed -swarm-cli feed print \ - --identity alice \ - --topic-string alice-posts - -swarm-cli feed print \ - --identity bob \ - --topic-string bob-posts - -# Read the homepage feed -swarm-cli feed print \ - --identity blog-admin \ - --topic-string blog-home -``` - -:::info -The `swarm-cli feed print` command requires the identity (key file) to be available locally. To read another author's feed without their key, use Bee's HTTP API directly: - -```bash -curl http://localhost:1633/bzz// -``` - -This fetches the latest content published to that feed manifest, without needing the author's private key. -::: - - - ### Adding a New Author Extending the system with a new author is straightforward. The new author gets their own key and topic. Their entry is appended to `authors.json`. Readers automatically discover them. - - - The [`add-author.js`](https://github.com/ethersphere/examples/blob/main/multi-author-blog/add-author.js) script from the examples repo handles everything in one step. From the project directory (after running `init.js`): ```bash node add-author.js charlie +# or: npm run add-author -- charlie ``` This will: @@ -912,51 +582,6 @@ This will: Then run `update-index.js` to refresh the homepage with the new author. - - - -Create the new author's identity and directory: - -```bash -swarm-cli identity create charlie -mkdir blog/charlie -``` - -Upload their initial page: - -```bash -# Create blog/charlie/index.html (empty page template) - -swarm-cli feed upload ./blog/charlie \ - --identity charlie \ - --topic-string charlie-posts \ - --stamp \ - --index-document index.html -``` - -Save the Feed Manifest URL hash as `CHARLIE_MANIFEST`. Update `authors.json`: - -```json -[ - { "name": "Alice", "topic": "alice-posts", "owner": "", "feedManifest": "" }, - { "name": "Bob", "topic": "bob-posts", "owner": "", "feedManifest": "" }, - { "name": "Charlie", "topic": "charlie-posts", "owner": "", "feedManifest": "" } -] -``` - -Re-upload `authors.json` to the index feed: - -```bash -swarm-cli feed upload authors.json \ - --identity blog-admin \ - --topic-string blog-index \ - --stamp -``` - -The index feed now points to the updated manifest. - - - :::info Because the index feed always points to the *latest* `authors.json`, any reader who polls the index feed automatically discovers newly added authors. You don't need to notify readers through a separate channel — the feed is the notification channel. diff --git a/docs/develop/routing.md b/docs/develop/routing.md index a4f678c82..58f6e72b1 100644 --- a/docs/develop/routing.md +++ b/docs/develop/routing.md @@ -4,10 +4,10 @@ id: routing description: Explains message routing protocols and peer discovery mechanisms in the Swarm network. --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -# Routing on Swarm +# Routing on Swarm + +In the [Manage Files](/docs/develop/files) guide you saw that a directory upload creates a manifest — a data structure mapping paths to content. Routing on Swarm is a direct consequence: every URL your site serves must correspond to an entry in that manifest. This guide covers the two main strategies for giving your site clean, navigable URLs. Swarm does not behave like a traditional web server — there is no server-side routing, and every route must correspond to a real file inside the site [manifest](./tools-and-features/manifests.md). @@ -230,49 +230,19 @@ Everything inside `dist/` will be uploaded to your Swarm feed. #### 7. Deploy Site -Now with routing handled its time to deploy the site. Refer to the [Host Your Website](./host-your-website.md#host-a-site-with-swarm-cli) guide for instructions on how to deploy your site using `bee-js` or `swarm-cli` - -If you already have `swarm-cli` installed, you can do this easily with the following command: - -:::warning -You will need to replace `` with a valid batch id from your Bee node, and may need to replace the directory specified from this line... - -```bash -swarm-cli feed upload ./dist -``` - -If you have a different build directory. If you followed all the steps above though, it should be in `./dist` already. -::: - - - +The project includes an `upload.js` script that uploads `./dist` to Swarm and publishes the result to a feed (so your URL stays stable across re-uploads). Set up your `.env` and run it: ```bash -swarm-cli feed upload ./dist \ - --identity website-publisher \ - --topic-string website \ - --stamp \ - --index-document index.html \ - --error-document 404.html -``` - - - - -```powershell -swarm-cli feed upload .\dist ` - --identity website-publisher ` - --topic-string website ` - --stamp ` - --index-document index.html ` - --error-document 404.html +cp .env.example .env +# Fill in BATCH_ID and PUBLISHER_KEY in .env +npm run upload ``` - - +You should see the website Swarm hash and a feed manifest hash. Open the feed manifest URL — `http://localhost:1633/bzz//` — in your browser. +For details on feeds and stable URLs, see the [Host a Webpage](./host-your-website.md#advanced-keep-your-url-stable-across-updates) guide. -Now the Home and About pages will be properly resolved by the routes we specified (`./#/`) and (`./#/about`), and non existent URLs will be handled by the `NotFound` component for hash URLs and our `404.html` error document for all others. +The Home and About pages will be properly resolved by the routes we specified (`./#/`) and (`./#/about`), and non-existent URLs will be handled by the `NotFound` component for hash URLs and our `404.html` error document for all others. ![](/img/hash-routing.jpg) @@ -478,5 +448,9 @@ Once you understand manifest-based routing, you can dynamically: * Create custom routes * Remove unwanted paths +--- + +**Next:** [Run a Gateway](/docs/develop/gateway-proxy) — expose your Swarm-hosted site to the public web through an HTTP gateway. + diff --git a/docs/develop/upload-and-download.md b/docs/develop/upload-and-download.md index e7dbfe4e4..7a726d8ab 100644 --- a/docs/develop/upload-and-download.md +++ b/docs/develop/upload-and-download.md @@ -6,6 +6,10 @@ description: Comprehensive guide for uploading and downloading files with the Be Uploading to Swarm has two steps: (1) **buy storage** as a **postage stamp batch** with a unique **batch ID**—and (2) **upload using the batch ID**. The upload returns a **Swarm reference hash**, anyone with that reference can download the content. +:::info Example project +The runnable Node.js scripts for this guide are in [`examples/upload-and-download`](https://github.com/ethersphere/examples/tree/main/upload-and-download). Clone the repo, copy `.env.example` to `.env`, fill in your `BEE_URL` and `BATCH_ID`, run `npm install`, then `npm run script:01` or `npm run script:02`. +::: + **Before you begin:** - You need a running Bee node connected to Gnosis Chain and funded with **xBZZ** and **xDAI**. @@ -279,84 +283,9 @@ console.log(stylesheet.name); // "example.txt" console.log(stylesheet.data.toUtf8()); // prints file content ``` -## Upload & Download with Swarm CLI - -The `swarm-cli` tool offers a convenient command-line interface for Bee node interaction. It's a convenient tool for node management or one-off uploads and downloads. - -Refer to [the official README](https://github.com/ethersphere/swarm-cli/blob/master/README.md) for a more complete usage guide. - -Buy storage via an interactive prompt (capacity + TTL), then upload: - -```bash -swarm-cli stamp create -``` - -Follow the interactive prompts: - -```bash -For swarm cli, use "stamp create", which looks like this: - -PS C:\Users\noahm> swarm-cli stamp create -Please provide the total capacity of the postage stamp batch -This represents the total size of data that can be uploaded -Example: 1GB - -Please provide the time-to-live (TTL) of the postage stamps -Defines the duration after which the stamp will expire -Example: 1d, 1w, 1month - -You have provided the following parameters: -Capacity: 1.074 GB -TTL: 7 days - -Cost: 0.6088166475825152 xBZZ -Available: 10000.0000000000000000 xBZZ -Type: Immutable -? Confirm the purchase Yes -... Creating postage batch. This may take up to 5 minutes. -``` - -Once you have a valid stamp batch, you can find it using `swarm-cli stamp list` - -```bash -swarm-cli stamp list -``` - -```bash -Stamp ID: 6dd0c4bbb6d62ba6c5fae3b000301c961ee584dd32846291821d789d7582ae36 -Usage: 0% -Capacity (immutable): 2.380 GB remaining out of 2.380 GB -TTL: A few seconds (2025-09-21) ------------------------------------------------------------------------------------------------------------------------- -Stamp ID: d13210952ec60b01a3c0027602743921736d6b277e9e70dd00d0d95fd878acbc -Usage: 0% -Capacity (immutable): 2.380 GB remaining out of 2.380 GB -TTL: A few seconds (2025-09-21) -``` - -Use `swarm-cli upload` along with a valid batch ID to upload a file: - -```bash -swarm-cli upload test.txt --stamp -``` - -You can also simply use: - -```bash -swarm-cli upload -``` - -And an interactive prompt will walk your through stamp selection and the rest of the upload. - -Upon upload, a Swarm reference hash will be returned which can then be used to download content: - -```bash -swarm-cli download ./output/ -``` - ## Upload & Download with the Bee API (advanced) -The **Bee HTTP API** offers the **lowest-level access** to a Bee node. However, it is **more complex and difficult to use** than **bee-js** or **swarm-cli** because you must manage headers, content types, and postage parameters yourself. **Unless you specifically require raw HTTP control**, we **do not recommend** using the Bee API directly. Instead use **bee-js** for application development and **swarm-cli** for command-line interaction. +The **Bee HTTP API** offers the **lowest-level access** to a Bee node. It is **more complex and harder to use** than **bee-js** because you must manage headers, content types, and postage parameters yourself. **Unless you specifically require raw HTTP control**, we **do not recommend** using the Bee API directly — use **bee-js** instead for application development. Refer to the [Bee API reference specification](https://docs.ethswarm.org/api/) for detailed usage information. @@ -368,7 +297,7 @@ The Bee API exposes three HTTP endpoints: #### Upload with **/bzz** -While both `swarm-cli` and `bee-js` allow for postage stamp batches to be purchased by specifying the storage duration and data size, the actual call to the Bee API requires an `amount` and `depth` parameters. The relationship between these parameters and the storage size and duration of the batch is complex. Therefore `bee-js` and `swarm-cli` (which allow batches to be purchased by data size/duration which are then converted to `depth`/`amount`) are strongly encouraged for newcomers to development on Swarm. [Learn more](./tools-and-features/buy-a-stamp-batch.md). +While `bee-js` allows postage stamp batches to be purchased by specifying storage duration and data size, the raw Bee API requires `amount` and `depth` parameters directly. The relationship between these parameters and the storage size and duration of the batch is complex, so `bee-js` is strongly encouraged for newcomers. [Learn more](./tools-and-features/buy-a-stamp-batch.md). 1. Buy a postage batch: @@ -399,3 +328,7 @@ Response: ```bash curl http://localhost:1633/bzz/ -o output.txt ``` + +--- + +**Next:** [Host a Webpage](/docs/develop/host-your-website) — upload a static website and serve it through `/bzz//`.