diff --git a/docs/documentation/act.md b/docs/documentation/act.md
index c40010d8..387c400b 100644
--- a/docs/documentation/act.md
+++ b/docs/documentation/act.md
@@ -16,3 +16,248 @@ This section is still being worked on. Check back soon for updates!
* Show example of creating grantee list
* Show example of secure upload
* Show example of secure download
+
+
+## Create Grantees List
+First we create a grantees list with the public keys of anyone we want to grant access to:
+
+```js
+import { Bee, PublicKey, BatchId } from '@ethersphere/bee-js';
+
+// Initialize Bee instance
+const bee = new Bee('http://localhost:1643');
+
+// Grantee's public key (replace with the actual key)
+const grantees = [
+ new PublicKey('027d0c4759f689ea3dd3eb79222870671c492cb99f3fade275bcbf0ea39cd0ef6e'),
+];
+
+// Your postage batch ID (replace with a valid one)
+const postageBatchId = new BatchId('f2949db4cfa4f5140ed3ef29f651d189175a8cb9534c992d3c3212b17f0b67f7');
+
+// Function to create grantees list
+async function createGranteeList() {
+ try {
+ // Create the grantee list using `createGrantees`
+ const response = await bee.createGrantees(postageBatchId, grantees);
+
+ // Log the response (ref and history ref)
+ console.log('Grantee List Created Successfully:');
+ console.log('Reference:', response.ref.toHex());
+ console.log('History Reference:', response.historyref.toHex());
+ } catch (error) {
+ console.error('Error creating grantee list:', error);
+ }
+}
+
+// Call the function to create the grantee list
+createGranteeList();
+```
+
+Example output:
+
+```bash
+Grantee List Created Successfully:
+Reference: 98bc4076efe5736aa682649c1928cbc9e0ac11d395f7ed0e67126ff660f5410a238e14f8b1d74f7df6121b8450d79ca789a257eeb84c5ca7dda3ed08a6859934
+History Reference: 06ee838e1f9b26c6d44077a2e63ad5ba9ecc143f278f9301f477eb99391f5796
+```
+
+The first reference `Reference` is used on its own for reviewing the list contents and updating the list. It is encrypted so only the list creator can view the list contents.
+
+The seconde reference `History Reference` is used along with the first `Reference` for creating a new grantee list based on the list referred to by the `Reference`.
+
+## Update Grantees
+
+In order to update grantees we pass an object in this format to the `bee.patchGrantees` method containing the public keys we wish to add or remove from the list along with the `History Reference` and the grantee list `Reference` and a valid postage stamp:
+
+```js
+bee.patchGrantees(postageBatchId, granteeListRef, granteeHistoryRef, {
+ add: [grantee1, grantee2], // Add the new grantee
+ revoke: [],
+ });
+```
+
+Calling this method will create an entirely new grantees list based on our first list and will return the `Reference` and `History Reference` for the new list.
+
+This ***WILL NOT*** update which users had access to content uploaded using the first grantees list, it is simply creating a new list with a different set of grantees which can be used for uploading content accessible to the new set.
+
+Full example script:
+
+```js
+import { Bee, PublicKey, BatchId, Reference } from '@ethersphere/bee-js';
+
+// Initialize Bee instance
+const bee = new Bee('http://localhost:1643'); // Correct port is 1643
+
+// Grantee's public key to be added (replace with the actual key)
+const grantee = new PublicKey('03636056d1e08f100c5acaf14d10070102de9444c97b2e8215305ab3e97254ede6');
+
+// Grantee list reference and history reference (replace with actual references from `createGrantees`)
+const granteeListRef = new Reference('98bc4076efe5736aa682649c1928cbc9e0ac11d395f7ed0e67126ff660f5410a238e14f8b1d74f7df6121b8450d79ca789a257eeb84c5ca7dda3ed08a6859934')
+const granteeHistoryRef = new Reference('06ee838e1f9b26c6d44077a2e63ad5ba9ecc143f278f9301f477eb99391f5796')
+
+// Your postage batch ID (replace with a valid one)
+const postageBatchId = new BatchId('f2949db4cfa4f5140ed3ef29f651d189175a8cb9534c992d3c3212b17f0b67f7');
+
+// Function to update the grantee list by adding the new public key
+async function updateGranteeList() {
+ try {
+ // Call the patchGrantees function to add the new public key
+ const response = await bee.patchGrantees(postageBatchId, granteeListRef, granteeHistoryRef, {
+ add: [grantee], // Add the new grantee
+ revoke: [],
+ });
+
+ // Log the updated grantee list references
+ console.log('Grantee List Updated Successfully:');
+ console.log('Updated Reference:', response.ref.toHex());
+ console.log('Updated History Reference:', response.historyref.toHex());
+ } catch (error) {
+ console.error('Error updating grantee list:', error.message);
+ if (error.response) {
+ // If there's a response object, log the full response for more details
+ console.error('Response Status:', error.response.status);
+ console.error('Response Body:', JSON.stringify(error.response.body, null, 2));
+ }
+ }
+}
+
+// Call the function to update the grantee list
+updateGranteeList();
+```
+
+Example output:
+
+```bash
+Grantee List Updated Successfully:
+Updated Reference: 363430f8c500e7ea7d23eff1f14674cf6d46ce1640684edad7cc2e5631c37bbaf9dc5b0f5ea42f919191c77187a7f1f40adfd1ab60bc84f1ae4f2d7bf42b98bd
+Updated History Reference: c2a43bea8abaae8ef31141ef8ec953097c76f48c2a14c1a6119bb110675e5378
+```
+
+## Get Grantees List
+
+In order to view the members of our grantees list we need to use the `Reference` returned when we create or update a list. We will view both our original list and the updated list based on the original list using the respective `Reference` from each list:
+
+```js
+import { Bee, Reference } from '@ethersphere/bee-js';
+
+// Initialize Bee instance with the correct port (1643)
+const bee = new Bee('http://localhost:1643');
+
+
+// Grantee list references (the reference returned from the `createGrantees` function)
+const granteeListRef_01 = new Reference('98bc4076efe5736aa682649c1928cbc9e0ac11d395f7ed0e67126ff660f5410a238e14f8b1d74f7df6121b8450d79ca789a257eeb84c5ca7dda3ed08a6859934');
+const granteeListRef_02 = new Reference('363430f8c500e7ea7d23eff1f14674cf6d46ce1640684edad7cc2e5631c37bbaf9dc5b0f5ea42f919191c77187a7f1f40adfd1ab60bc84f1ae4f2d7bf42b98bd');
+
+// Function to get the grantee list
+async function getGranteeList(granteeListRef) {
+ try {
+ // Call the getGrantees function with the reference
+ const result = await bee.getGrantees(granteeListRef);
+
+ // Log the full response
+ console.log('Grantee List Retrieved:');
+ console.log('Status:', result.status);
+ console.log('Status Text:', result.statusText);
+
+ // Log the as an array of their string representations (compressed hex values)
+ console.log('Grantees:', result.grantees.map(grantee => grantee.toCompressedHex()));
+
+ } catch (error) {
+ console.error('Error retrieving grantee list:', error);
+ }
+}
+
+// Call the function to fetch the grantee list
+getGranteeList(granteeListRef_01);
+getGranteeList(granteeListRef_02);
+```
+
+Example output:
+
+```bash
+Grantee List Retrieved:
+Status: 200
+Status Text: OK
+Grantees: [
+ '027d0c4759f689ea3dd3eb79222870671c492cb99f3fade275bcbf0ea39cd0ef6e'
+]
+Grantee List Retrieved:
+Status: 200
+Status Text: OK
+Grantees: [
+ '027d0c4759f689ea3dd3eb79222870671c492cb99f3fade275bcbf0ea39cd0ef6e',
+ '03636056d1e08f100c5acaf14d10070102de9444c97b2e8215305ab3e97254ede6'
+]
+```
+
+The first list of grantees contains the first public key we gave access to when we created the list, while the second one contains both the first and the second one we added when we created our second list based on the first one.
+
+## Upload With ACT
+
+We can upload our content with either of the two lists we created depending on which set of users we wish to give access too. In the example below, we use both lists.
+
+```js
+import { Bee, BatchId, Reference } from '@ethersphere/bee-js';
+
+// Initialize Bee instance with the correct port (1643)
+const bee = new Bee('http://localhost:1643');
+
+// Your postage batch ID (replace with a valid one)
+const postageBatchId = new BatchId('f2949db4cfa4f5140ed3ef29f651d189175a8cb9534c992d3c3212b17f0b67f7');
+
+// Grantee history reference (the reference returned from the `createGrantees` function)
+const granteeHistoryRef1 = new Reference('06ee838e1f9b26c6d44077a2e63ad5ba9ecc143f278f9301f477eb99391f5796');
+const granteeHistoryRef2 = new Reference('c2a43bea8abaae8ef31141ef8ec953097c76f48c2a14c1a6119bb110675e5378');
+
+// Sample data to upload
+const fileData = 'This is a sample string that will be uploaded securely using ACT. ABCDE.';
+
+// Function to upload the data with ACT
+async function uploadWithACT(historyRef) {
+ try {
+ // Upload the string with ACT enabled
+ const result = await bee.uploadFile(postageBatchId, fileData, 'samplefile.txt', {
+ act: true, // Enable ACT for the uploaded data
+ actHistoryAddress: historyRef, // Provide the grantee list reference for ACT
+ contentType: 'text/plain',
+ });
+
+ console.log('File uploaded successfully with ACT:');
+ console.log('Reference:', result.reference.toHex());
+ console.log("History reference")
+ console.log(result.historyAddress.value.toHex())
+ } catch (error) {
+ console.error('Error uploading file with ACT:', error);
+ }
+}
+
+// Call the function to upload the file
+uploadWithACT(granteeHistoryRef1);
+uploadWithACT(granteeHistoryRef2);
+```
+
+Example output:
+
+```bash
+File uploaded successfully with ACT:
+Reference: d3d4485efcc22acdf4d20a31d79edc3220655151bd15cec0df9111e0c0f89e86
+History reference
+06ee838e1f9b26c6d44077a2e63ad5ba9ecc143f278f9301f477eb99391f5796
+File uploaded successfully with ACT:
+Reference: d3d4485efcc22acdf4d20a31d79edc3220655151bd15cec0df9111e0c0f89e86
+History reference
+c2a43bea8abaae8ef31141ef8ec953097c76f48c2a14c1a6119bb110675e5378
+```
+
+The reference hash is the same for each upload since the content is the same. The reference hash along with a `History Reference` and the uploader's public key are required in order to access the content uploaded with ACT.
+
+You can choose which `History Reference` to share depending on which set of public keys you wish to authorize to download the content.
+
+## Download With ACT
+
+The example below uses the first `History Reference`, and so can only gives access to the single public key in the grantees list it refers to. If we wish to give both public keys access then we could share the other key.
+
+```bash
+curl -X GET "http://localhost:1633/bzz/d3d4485efcc22acdf4d20a31d79edc3220655151bd15cec0df9111e0c0f89e86/" -H "swarm-act-publisher: 0295562f9c1013d1db29f7aaa0c997c4bb3f1fc053bd0ed49a3d98584490cc8f96" -H "swarm-act-history-address: 06ee838e1f9b26c6d44077a2e63ad5ba9ecc143f278f9301f477eb99391f5796" --output downloaded_file.txt
+```
\ No newline at end of file
diff --git a/docs/documentation/gsoc.md b/docs/documentation/gsoc.md
index 8ac87ae2..c6f15690 100644
--- a/docs/documentation/gsoc.md
+++ b/docs/documentation/gsoc.md
@@ -15,4 +15,146 @@ This section is still being worked on. Check back soon for updates!
* Show mining the writer private key for the targeted overlay address [Gist](https://gist.github.com/Cafe137/e76ef081263aaec7a715139d700f3433)
* Show a simple listener, and a simple send invocation [Gist1](https://gist.github.com/Cafe137/7f02fb54ad5a79833f3b718b94df0d41) [Gist2](https://gist.github.com/Cafe137/6277f1d112b3b78ba36f717551357c3b)
-* Show Identifier class usage [Gist](https://gist.github.com/Cafe137/25a244d85758480aa1e15c80ff147b72)
\ No newline at end of file
+* Show Identifier class usage [Gist](https://gist.github.com/Cafe137/25a244d85758480aa1e15c80ff147b72)
+
+
+The GSOC (Graffiti Several Owner Chunk) feature allows a **full node** to receive messages from many other nodes using a shared Single Owner Chunk (SOC). This enables real-time communication and notification over Swarm.
+
+## Overview
+
+GSOC messages are updates to a pre-mined SOC that lands in the **neighborhood** of the recipient node. Only full nodes receive these updates because light nodes do not sync neighborhood chunks. However, **any node** (light or full) can send GSOC messages.
+
+### GSOC vs PSS
+
+GSOC shares some similarities with PSS - both features allow for full nodes to receive messages from other nodes.
+
+However, there are several key differences:
+
+- Unlike PSS, **GSOC only needs to mine the target chunk once**βmultiple messages reuse it, making it **faster, cheaper**, and **more efficient** for recurring updates.
+- Unlike PSS, **No encryption** is used by default, making it unsuitable for handling sensitive data.
+
+
+## Requirements
+
+To use the example scripts below, you need:
+
+- A Bee full node with a fully synced reserve for receiving GSOC messages.
+- A light node for sending GSOC messages.
+- The batch ID of a usable postage batch. If you don't have one already, you will need to [buy a batch](/docs/storage/#purchasing-storage) to upload data. If you do have one, you will need to [get and save](/docs/storage/#selecting-a-batch) its batch ID.
+
+## Create an Identifier (Receiver and Sender)
+
+Identifiers in GSOC are similar to topics in PSS β they define the stream of messages a receiver node is subscribed to. The sender must use the same identifier so that their messages are received.
+
+Each identifier is a 64-digit hex string (32 bytes). It can be initialized with an a hex string of your choice or any arbitrary string using the `Identifier` utility class. You can also use the zero-initialized `NULL_IDENTIFIER` as a default identifier for cases where you don't need a unique identifier:
+
+
+```js
+import { Identifier, NULL_IDENTIFIER } from '@ethersphere/bee-js'
+
+// Use default (all zeros):
+const identifier = NULL_IDENTIFIER
+
+// From a hex string:
+const identifier = new Identifier('6527217e549e84f98e51b1d8b1ead62ff5cad59acd9713825754555d6975f103')
+
+// From a human-readable string:
+const identifier = Identifier.fromString('chat:v1')
+```
+
+- Use `NULL_IDENTIFIER` is a 64 digit hex string of all zeros - use it for quick testing or when uniqueness doesn't matter.
+- Use any hex string to initialize a new `Identifier` object .
+- Use `Identifier.fromString()` to generate an identifier derived from your string of choice (allows for easy to remember human readable identifiers `"notifications"`, `"chat:user1"`).
+
+## Get Target Overlay (Receiver Node)
+
+This step **is performed by the receiving full node** to retrieve its overlay address. This overlay address is then shared with the sender node to use as a target overlay for its GSOC messages:
+
+```js
+import { Bee } from '@ethersphere/bee-js'
+
+const bee = new Bee('http://localhost:1633')
+
+async function checkAddresses() {
+ const addresses = await bee.getNodeAddresses()
+ console.log('Node Addresses:', addresses)
+}
+
+checkAddresses()
+```
+
+Example output:
+
+```bash
+Node Addresses:
+Overlay: 1e2054bec3e681aeb0b365a1f9a574a03782176bd3ec0bcf810ebcaf551e4070
+Ethereum: 9a73f283cd9211b96b5ec63f7a81a0ddc847cd93
+Public Key: 7d0c4759f689ea3dd3eb79222870671c492cb99f3fade275bcbf0ea39cd0ef6e25edd43c99985983e49aa528f3f2b6711085354a31acb4e7b03559b02ec868f0
+PSS Public Key: 5ade58d20be7e04ee8f875eabeebf9c53375a8fc73917683155c7c0b572f47ef790daa3328f48482663954d12f6e4739f748572c1e86bfa89af99f17e7dd4d33
+Underlay: [
+ '/ip4/127.0.0.1/tcp/1634/p2p/QmcpSJPHuuQYRgDkNfwziihVcpuVteoNxePvfzaJyp9z7j',
+ '/ip4/172.17.0.2/tcp/1634/p2p/QmcpSJPHuuQYRgDkNfwziihVcpuVteoNxePvfzaJyp9z7j',
+ '/ip6/::1/tcp/1634/p2p/QmcpSJPHuuQYRgDkNfwziihVcpuVteoNxePvfzaJyp9z7j'
+]
+```
+
+The `Overlay` should be saved and shared with sender nodes.
+
+## Set Up a Listener (Receiver Node)
+
+This must be run on a full node. It mines a key that lands within its own neighborhood and starts listening.
+
+```js
+import { Bee, Identifier, NULL_IDENTIFIER } from '@ethersphere/bee-js'
+
+const bee = new Bee('http://localhost:1633')
+const identifier = Identifier.fromString(NULL_IDENTIFIER)
+
+async function listen() {
+ const { overlay } = await bee.getNodeAddresses()
+
+ // The signer is initialized using the receiving node's own overlay and chosen identifier
+ const signer = bee.gsocMine(overlay, identifier)
+
+ // A GSOC subscription is established using the blockchain address derived from the signer and the identifier
+ bee.gsocSubscribe(signer.publicKey().address(), identifier, {
+ // A callback function is used to handle incoming updates - you can include your application logic here
+ onMessage: message => console.log('Received:', message.toUtf8()),
+ onError: error => console.error('Subscription error:', error),
+ })
+
+ console.log('Listening for GSOC updates...')
+}
+
+listen()
+```
+
+## Send a Message (Sender Node)
+
+The sending node must have a ***usable postage batch id*** and also know the ***target overlay address*** and ***identifier*** in order to send a message:
+
+```js
+import { Bee, Identifier, NULL_IDENTIFIER } from '@ethersphere/bee-js'
+
+const bee = new Bee('http://localhost:1643')
+
+// The identifier is initialized using the same data as the receiving node
+const identifier = Identifier.fromString(NULL_IDENTIFIER)
+const batchId = '6c84b6d3f5273b969c3df875cde7ccd9920f5580122929aedaf440bfe4484405'
+
+const recipientOverlay = '1e2054bec3e681aeb0b365a1f9a574a03782176bd3ec0bcf810ebcaf551e4070'
+
+async function sendMessage() {
+ // The signer is initialized using the overlay address and identifier shared by the receiving node
+ const signer = bee.gsocMine(recipientOverlay, identifier)
+
+ // bee.gsocSend is called with the batch id, initialized signer, identifier, and message payload in order to send a GSOC message
+ await bee.gsocSend(batchId, signer, identifier, 'Hello via GSOC!')
+ console.log('Message sent')
+}
+
+sendMessage()
+```
+
+For more information about GSOC, refer to the [Bee documentation](https://docs.ethswarm.org/docs/develop/tools-and-features/gsoc).
+
diff --git a/docs/documentation/pinning.md b/docs/documentation/pinning.md
index e415e208..d65d1662 100644
--- a/docs/documentation/pinning.md
+++ b/docs/documentation/pinning.md
@@ -5,14 +5,141 @@ slug: /pinning
sidebar_label: Pinning
---
-## π§ Under Construction π§
-:::caution π§ This page is under construction
+Pinning allows you to guarantee that your uploaded content will always be available by storing it **locally on your own Bee node**. However, pinning **guarantee availability across the Swarm network**. Therefore you must use pinning along with the **stewardship utilities** included in `bee-js` to monitor the availability of your pinned content and reupload it if needed.
-This section is still being worked on. Check back soon for updates!
+In this section, you'll learn how to:
-:::
+- Pin content
+- Check whether pinned content is still retrievable from the network
+- Reupload missing content
+- View all currently pinned references
+- Remove pins that are no longer required
+
+
+
+## Pinning and Unpinning Content
+
+To pin a reference (so it remains stored on your node):
+
+```js
+await bee.pin(reference)
+console.log('Reference pinned locally.')
+```
+
+To stop tracking and remove it from local pin storage:
+
+```js
+await bee.unpin(reference)
+console.log('Reference unpinned and no longer tracked.')
+```
+
+
+## Checking if a Reference is Retrievable
+
+Use `isReferenceRetrievable(reference)` to verify if the content for a given Swarm reference is currently accessible on the network:
+
+```js
+const isAvailable = await bee.isReferenceRetrievable(reference)
+
+if (isAvailable) {
+ console.log('Data is retrievable from the network.')
+} else {
+ console.log('Data is missing from the network.')
+}
+```
+
+## Reuploading Pinned Data
+
+If content is missing but was previously pinned, you can reupload it using `reuploadPinnedData(postageBatchId, reference)`:
+
+```js
+await bee.reuploadPinnedData(postageBatchId, reference)
+console.log('Data has been reuploaded to the network.')
+```
+
+## Listing All Pinned References
+
+You can get all currently pinned references with:
+
+```js
+const pins = await bee.getAllPins()
+console.log('Pinned references:', pins.map(ref => ref.toHex()))
+```
+
+To check if a specific reference is pinned:
+
+```js
+const pinStatus = await bee.getPin(reference)
+console.log('Pin info:', pinStatus)
+```
+
+## Deleting a Tag After Use
+
+Once youβre done tracking an upload, you can delete the tag to keep your node clean:
+
+```js
+await bee.deleteTag(tag.uid)
+console.log("Deleted tag:", tag.uid)
+```
+
+
+## Example Script
+
+The following script automates the process of checking all locally pinned references, verifying their retrievability from the network, and reuploading any that are missing. This ensures that your pinned data remains available even if it has dropped out of the Swarm network.
+
+```js
+import { Bee } from "@ethersphere/bee-js"
+
+const bee = new Bee('http://localhost:1633')
+const postageBatchId = "129903062bedc4eca6fc1c232ed385e93dda72f711caa1ead6018334dd801cee"
+
+async function reuploadMissingPins() {
+ try {
+ // Get all currently pinned references
+ const pinnedRefs = await bee.getAllPins()
+
+ if (!pinnedRefs.length) {
+ console.log("No pinned references found.")
+ return
+ }
+
+ console.log(`Found ${pinnedRefs.length} pinned references.`)
+
+ let repaired = 0
+
+ // Loop through all references and check retrievability
+ for (const ref of pinnedRefs) {
+ const reference = ref.toHex()
+ const isAvailable = await bee.isReferenceRetrievable(reference)
+
+ if (isAvailable) {
+ console.log(`β
${reference} is retrievable.`)
+ } else {
+ console.log(`β οΈ ${reference} is missing β reuploading...`)
+ await bee.reuploadPinnedData(postageBatchId, reference)
+ console.log(`π Reuploaded ${reference}`)
+ repaired++
+ }
+ }
+
+ console.log(`\nDone. ${repaired} reference(s) were reuploaded.`)
+ } catch (error) {
+ console.error("Error:", error.message)
+ }
+}
+
+reuploadMissingPins()
+```
+
+Example terminal output:
+
+```bash
+Found 2 pinned references.
+β οΈ 1880ff0bbd23997dfa46921ba2ab0098824d967fe60c6ca1ae2e8fd722f4db78 is missing β reuploading...
+π Reuploaded 1880ff0bbd23997dfa46921ba2ab0098824d967fe60c6ca1ae2e8fd722f4db78
+β
fd79d5e0ebd8407e422f53ce1d7c4c41ebf403be55143900f8d1490560294780 is retrievable.
+
+Done. 1 reference(s) were reuploaded.
+```
-* Show an example of pinning a reference
-* Show an example of listing references
-* Show an example of re-uploading a reference
diff --git a/docs/documentation/pss.md b/docs/documentation/pss.md
index 36c26c8e..7509cd43 100644
--- a/docs/documentation/pss.md
+++ b/docs/documentation/pss.md
@@ -2,203 +2,155 @@
title: Postal Service over Swarm
id: pss
slug: /pss
-sidebar_label: Postal Service over Swarm
+sidebar_label: PSS
---
-## π§ Under Construction π§
-:::caution π§ This page is under construction
+Swarm supports sending encrypted messages over the network using a system called **Postal Service over Swarm** (PSS). These messages are embedded in regular Swarm traffic and routed to specific nodes based on their overlay address.
-This section is still being worked on. Check back soon for updates!
+## What Is PSS?
-:::
+PSS provides a pub/sub-like functionality for secure messaging. Full nodes can listen for messages on a specific **topic**, and other nodes (light or full) can send them payloads using the recipient node's **overlay address** and optionally encrypted using the recipient's **PSS public key**.
+Messages can be received via **subscription** or by a **one-off listener**.
-* Remove the unrelated intro section
-* Show a listener
-* Show a one time receive
-* Show a send invocation
-* Move it towards the end of the chapters, it is not very important
+:::caution
+Only full nodes can receive messages since PSS messages are sent as a part of the chunk syncing process which only full nodes take part in.
+:::
+## Requirements
-import Tabs from '@theme/Tabs'
-import TabItem from '@theme/TabItem'
+To use the example scripts below, you need:
-Swarm provides the ability to send messages that appear to be normal Swarm traffic, but are in fact messages that may be received and decrypted to reveal their content only to specific nodes that were intended to receive them.
+- A Bee full node with a fully synced reserve for receiving PSS messages.
+- A light node for sending PSS messages.
+- The batch ID of a usable postage batch. If you don't have one already, you will need to [buy a batch](/docs/storage/#purchasing-storage) to upload data. If you do have one, you will need to [get and save](/docs/storage/#selecting-a-batch) its batch ID.
-PSS provides a pub-sub facility that can be used for a variety of tasks. Nodes are able to listen to messages received for a specific topic in their nearest neighbourhood and create messages destined for another neighbourhood which are sent over the network using Swarm's usual data dissemination protocols.
-The intended use of PSS is to communicate privately with a publicly known identity (to for example initiate further communication directly). Due to the cost of mining the trojan chunks, it is not recommended to use as an instant messaging system.
+## Get Recipient Info (Full Node Only)
-:::caution Light nodes are unreachable
-Be aware! You can not send message to Light nodes! This is because light nodes does not fully participate
-in the data exchange in Swarm network and hence the message won't arrive to them.
-:::
+This step **must be performed by the receiving full node**. It retrieves the nodeβs **overlay address** and **PSS public key**, which must then be shared with the sending node:
-## Getting the relevant data
-When you start `bee`, you may find all the necessary information in the log:
-```sh
-INFO using existing swarm network address: 9e2ebf266369090091620db013aab164afb1574aedb3fcc08ce8dc6e6f28ef54
-INFO swarm public key 03e0cee7e979fa99350fc2e2f8c81d857b525b710380f238742af269bb794dfd3c
-INFO pss public key 02fa24cac43531176d21678900b37bd800c93da3b02c5e11572fb6a96ec49527fa
-INFO using ethereum address 5f5505033e3b985b88e20616d95201596b463c9a
-```
-Let's break it down:
-- **Ethereum address** is the public address of your node wallet. Together with the corresponding private key, it is used for things such as making Ethereum transactions (receiving and sending ETH and BZZ); receiving, claiming and singing cheques and the Swarm network address is also derived from it.
-- The **Swarm network address** defines your location in the kademlia and within the context of PSS is used for addressing the trojan chunks to you. In other words, others may use it to send you a message.
-- **PSS public key** can be used by others to encrypt their messages for you.
+- The **overlay address** is **required** as the routing target.
+- The **PSS public key** is **optional** and only needed for encryption.
-
+```js
+import { Bee } from '@ethersphere/bee-js'
-## Sending message
+const bee = new Bee('http://localhost:1633')
-To send data simply define a topic, prefix of the recipient's swarm network address (we recommend 4-6 character prefix length) and the data to be send.
-:::caution Your communication privacy may be at risk
-When sending PSS messages without encryption key, any Bee node through which the trojan chunk passes would be able to read the message.
-:::
+async function checkAddresses() {
+ const addresses = await bee.getNodeAddresses()
-
-
-
-```ts
-/**
- * @param {string} topic
- * @param {string} targetPrefix
- * @param {string|Uint8Array} data
- * @param {string} encryptionKey
- */
-bee.pssSend('topic', '9e2e', 'Hello!')
-```
-
-
-
+ console.log('Node Addresses:', addresses)
+}
-```js
-/**
- * @param {string} topic
- * @param {string} targetPrefix
- * @param {string|Uint8Array} data
- * @param {string} encryptionKey
- */
-bee.pssSend('topic', '9e2e', 'Hello!')
+checkAddresses()
```
-
-
-
-If you want to encrypt the message, you may provide the recipient's PSS public key.
-
-
-
-
-```ts
-bee.pssSend(
- 'topic',
- '9e2e',
- 'Encrypted Hello!',
- '02fa24cac43531176d21678900b37bd800c93da3b02c5e11572fb6a96ec49527fa',
-)
+Example output:
+
+```bash
+Node Addresses:
+Overlay: 1e2054bec3e681aeb0b365a1f9a574a03782176bd3ec0bcf810ebcaf551e4070
+Ethereum: 9a73f283cd9211b96b5ec63f7a81a0ddc847cd93
+Public Key: 7d0c4759f689ea3dd3eb79222870671c492cb99f3fade275bcbf0ea39cd0ef6e25edd43c99985983e49aa528f3f2b6711085354a31acb4e7b03559b02ec868f0
+PSS Public Key: 5ade58d20be7e04ee8f875eabeebf9c53375a8fc73917683155c7c0b572f47ef790daa3328f48482663954d12f6e4739f748572c1e86bfa89af99f17e7dd4d33
+Underlay: [
+ '/ip4/127.0.0.1/tcp/1634/p2p/QmcpSJPHuuQYRgDkNfwziihVcpuVteoNxePvfzaJyp9z7j',
+ '/ip4/172.17.0.2/tcp/1634/p2p/QmcpSJPHuuQYRgDkNfwziihVcpuVteoNxePvfzaJyp9z7j',
+ '/ip6/::1/tcp/1634/p2p/QmcpSJPHuuQYRgDkNfwziihVcpuVteoNxePvfzaJyp9z7j'
+]
```
+The `Overlay` and `PSS Public Key` values should be shared with the sending node.
-
-
+The sender (which can be a light node or a full node) needs the **overlay address** to generate the message target, and can optionally use the **PSS public key** to encrypt the message.
-```js
-bee.pssSend(
- 'topic',
- '9e2e',
- 'Encrypted Hello!',
- '02fa24cac43531176d21678900b37bd800c93da3b02c5e11572fb6a96ec49527fa',
-)
-```
+## Listen for Messages (Full Node)
-
-
+You can listen on a topic using both **continuous subscription** and **one-time receive**:
-## Retrieving message
-As a recipient, you have two ways how to receive the message. If you are expecting one off message (which is the intended PSS use case to exchange credentials for further direct communication), you can use the `pssReceive` method.
-
-
+- `bee.pssSubscribe` is used to set up a continuous subscription.
+- `bee.pssReceive` is used to set up a listener on a timeout which closes after receiving a message.
-```ts
-const message = await bee.pssReceive('topic')
+```js
+import { Bee, Topic } from '@ethersphere/bee-js'
+
+const bee = new Bee('http://localhost:1633')
+
+// Generate a topic from a unique string
+const topic = Topic.fromString('pss-demo')
+
+console.log('Subscribing to topic:', topic.toHex())
+
+// Continuous subscription
+bee.pssSubscribe(topic, {
+ onMessage: msg => console.log('Received via subscription:', msg.toUtf8()),
+ onError: err => console.error('Subscription error:', err.message),
+})
+
+// One-time receive (3 hour timeout)
+async function receiveOnce() {
+ try {
+ console.log('Waiting for one-time message...')
+ const message = await bee.pssReceive(topic, 1000 * 60 * 60 * 3)
+ console.log('One-time received:', message.toUtf8())
+ } catch (err) {
+ console.error('pssReceive error or timeout:', err.message)
+ }
+}
-console.log(message.text()) // prints the received message
+receiveOnce()
```
-
-
+In this script we generate a `topic` from our chosen string with the `Topic.fromString` method. Then we subscribe to listen for incoming pss messages for that topic with the `bee.pssSubscribe` method, and we also set up a listener for receiving a single message with the `bee.pssReceive` method. When a chunk with a PSS message for that topic is synced into our node's neighborhood, it will be received and handled by our node with the `onMessage` callback function when using the `bee.pssSubscribe` or through the return value of the `bee.pssReceive` method in our `receiveOnce` function.
+
+## Send Message (Light or Full Node)
+
+The sender must provide:
+
+- A valid **postage batch ID**
+- The recipientβs **overlay address** (used to generate the routing target)
+- Optionally the **PSS public key** for encryption
```js
-const message = await bee.pssReceive('topic')
+import { Bee, Topic, Utils } from '@ethersphere/bee-js'
-console.log(message.text()) // prints the received message
-```
+const bee = new Bee('http://localhost:1643')
+const BATCH_ID = '6d8118c693423eef41796d58edbbffb76881806a0f44da728bf80f0c1aafa783'
-
-
+// The overlay address of the receiving node
+const recipientOverlay = '1e2054bec3e681aeb0b365a1f9a574a03782176bd3ec0bcf810ebcaf551e4070'
-If you want to subscribe to multiple messagees, use the `pssSubscribe` method.
+// Generate a topic using the same string shared by the receiving node
+const topic = Topic.fromString('pss-demo')
+// Set the number of leading prefix bits to mine for the chunk bearing the PSS message
+const target = Utils.makeMaxTarget(recipientOverlay)
-
-
+// The PSS message payload
+const message = 'Hello from the light node!'
-```ts
-const handler = {
- onMessage: (message: Data) => {console.log(message.text())},
- onError: (error: BeeError) => {console.log(error)}
+async function send() {
+ try {
+ await bee.pssSend(BATCH_ID, topic, target, message)
+ console.log('Message sent via PSS.')
+ } catch (err) {
+ console.error('Failed to send message:', err.message)
+ }
}
-// Subscribe
-const subscription = bee.pssSubscribe('topic', handler)
-
-// Terminate the subscription
-subscription.cancel()
+send()
```
-
-
+## Encrypt with PSS Public Key
-```js
-const handler = {
- onMessage: (message) => {console.log(message.text())},
- onError: (error) => {console.log(error)}
-}
+To encrypt the message specifically for the recipient, include their **PSS public key** in the send call:
-// Subscribe
-const subscription = bee.pssSubscribe('topic', handler)
+```js
+const recipientPssPublicKey = '5ade58d20be7e04ee8f875eabeebf9c53375a8fc73917683155c7c0b572f47ef790daa3328f48482663954d12f6e4739f748572c1e86bfa89af99f17e7dd4d33'
-// Terminate the subscription
-subscription.cancel()
+await bee.pssSend(BATCH_ID, topic, target, message, recipientPssPublicKey)
```
-
-
+The message will then be encrypted using the PSS public key of the recipient node before sending and will only be decryptable by the recipient node (although the message bearing the PSS chunk will be received by all nodes in the same neighborhood as the recipient, it will only be decryptable by the recipient node).
diff --git a/docs/documentation/soc-and-feeds.md b/docs/documentation/soc-and-feeds.md
index d3259d15..1830c78e 100644
--- a/docs/documentation/soc-and-feeds.md
+++ b/docs/documentation/soc-and-feeds.md
@@ -16,7 +16,12 @@ This section is still being worked on. Check back soon for updates!
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
-Swarm provides the ability to store content in content-addressed chunks or Single Owner Chunks (SOC). With single owner chunks, a user can assign arbitrary data to an address and attest chunk integrity with their digital signature.
+:::info Note on Ethereum addresses
+This article mentions Ethereum address in multiple sections. Please not that this refers to an Ethereum format account only, and does not indicate the Ethereum blockchain itself. Swarm is built on the Gnosis Blockchain which is a fork of ethereum.
+:::
+
+
+Swarm provides the ability to store content in content-addressed chunks (CAC) whose addresses are derived from the chunk data, or Single Owner Chunks (SOC) whose addresses are derived from the uploader's own public key and chosen identifier. With single owner chunks, a user can assign arbitrary data to an address and attest chunk integrity with their digital signature.
Feeds are a unique feature of Swarm. They constitute the primary use case for single owner chunks. Developers can use Feeds to version revisions of a mutable resource, indexing sequential updates to a topic, publish the parts to streams, or post consecutive messages in a communication channel. Feeds implement persisted pull-messaging and can also be interpreted as a pub-sub system.
@@ -24,82 +29,141 @@ Because Feeds are built on top of SOCs, their interfaces are somewhat similar an
## Single Owner Chunks
-Bee-js calculates a SOC address as the hash of an `identifier` and `owner`. The `identifier` is a 32 bytes long arbitrary data, usually expected as a hex string or a `Uint8Array`. The `owner` is an Ethereum address that consists of 20 bytes in a format of a hex string or `Uint8Array`.
+Bee-js calculates a SOC Swarm reference hash as the keccak256 hash of the concatenation of the `identifier` and `owner` Ethereum address. The `identifier` is a 32 byte long arbitrary value (by default a hex string or a `Uint8Array`). The `owner` is an Ethereum address that consists of 20 bytes in a format of a hex string or `Uint8Array`.
:::warning SOCs are immutable!
-You might be tempted to modify a SOC's content to "update" the chunk. Reuploading of SOC is forbidden in Swarm as it might create uncertain behavior. Bee node will reject the API call if it finds already existing SOC for the given address. Either use a different `identifier`, or you might be looking for Feeds as your use case.
+You might be tempted to modify a SOC's content to "update" the chunk. Reuploading of SOC is forbidden in Swarm as it might create uncertain behavior. A Bee node will reject the API call if it finds an already existing SOC for the given address. Either use a different `identifier`, or you might be looking for Feeds as your use case.
:::
-### Reading SOCs
+### Uploading SOCs
-To read data from a SOC, we need to make a reader object bound to a specific `owner`. Then we can download the data with the provided chunk's `identifier`.
+To write a Single Owner Chunk (SOC), use the `makeSOCWriter()` method from the Bee client. This method requires a signer, which can be an instance of `PrivateKey`, a raw Ethereum private key as a hex string (with or without the `0x` prefix), or a `Uint8Array` representing the private key.
-```js
-const owner = '0x8d3766440f0d7b949a5e32995d09619a7f86e632'
-const socReader = bee.makeSOCReader(owner)
-const identifier = '0000000000000000000000000000000000000000000000000000000000000000'
-const soc = await socReader.download(identifier)
-const data = soc.payload()
-```
+The signer is used to cryptographically sign the chunk, using the same format Ethereum uses for signing transactions.
-### Writing SOCs
+Once the `SOCWriter` is created, you can upload an SOC by providing a `postageBatchId`, a 32-byte `identifier`, and the `data` payload.
-When writing a SOC, first, we need to make a writer object. Because we need to sign the chunk, we need to pass in a `signer` object. The `signer` object can be either an Ethereum private key (as a hex string or `Uint8Array`) or an instance of the `Signer` interface. The `Signer` interface can be used for integration with 3rd party Ethereum wallet applications because Swarm uses the same format for signing chunks that Ethereum uses for signing transactions.
:::info Default `signer`
+When you are instantiating `Bee` class you can pass an Ethereum private key as the default signer that will be used if you won't specify it directly for the `makeSOCWriter`.
+:::
-When you are instantiating `Bee` class you can pass it a default signer that will be used if you won't specify it
-directly for the `makeSOCWriter`. See `Bee` constructor for more info.
-
+:::warning Your assets and/or privacy may be at risk
+We suggest using ephemeral private keys (e.g. randomly generated) when writing to SOC or Feeds. Never use your real Ethereum private keys here (or in any web applications) directly because it will allow others to sign messages with your kay which may compromise your privacy or lead to the loss of funds stored by that account.
:::
-:::tip Ethereum Wallet signers
+```js
+import { Bee, PrivateKey, NULL_IDENTIFIER, Bytes } from "@ethersphere/bee-js"
+
+// Define your Ethereum private key (never use your real private keys in production code)
+const privateKey = new PrivateKey('0x634fb5a872396d9693e5c9f9d7233cfa93f395c093371017ff44aa9ae6564cdd')
-If you want to use your browser Ethereum Wallet like Metamask you can use utility called `makeEthereumWalletSigner` that we ship with bee-js
-which creates a `Signer` object out of given EIP-1193 compatible provider.
+// Print the identifier and address which can be used to retrieve the SOC
+// SOC identifier
+console.log("SOC identifier")
+console.log(new Bytes(NULL_IDENTIFIER).toHex())
-See it used in our example [here](https://github.com/ethersphere/examples-js/tree/master/eth-wallet-signing).
+// Ethereum address
+console.log("Ethereum address")
+console.log(privateKey.publicKey().address().toHex())
-```js
-import { Utils } from '@ethersphere/bee-js'
+// Initialize Bee client with default signer and Swarm node URL
+const bee = new Bee('http://localhost:1643', { signer: privateKey })
-const signer = Utils.makeEthereumWalletSigner(window.ethereum)
-...
-```
-:::
+// Paste your own postage ID here
+const postageBatchId = 'f2949db4cfa4f5140ed3ef29f651d189175a8cb9534c992d3c3212b17f0b67f7'
+
+// Create the SOC writer using the default signer
+const socWriter = bee.makeSOCWriter()
+// The data you want to store in the SOC
+const data = 'this is my sample data'
-```ts
-type SyncSigner = (digest: Data) => Signature | string
-type AsyncSigner = (digest: Data) => Promise
+async function uploadSOC() {
+ try {
+ // Upload the data to the SOC using the postage batch and identifier
+ const response = await socWriter.upload(postageBatchId, NULL_IDENTIFIER, data)
-/**
- * Interface for implementing Ethereum compatible signing.
- *
- * @property sign The sign function that can be sync or async
- * @property address The Ethereum address of the signer
- */
-export type Signer = {
- sign: SyncSigner | AsyncSigner
- address: EthAddress
+ // Log the human-readable reference (hex string)
+ console.log("SOC reference:")
+ console.log("Reference (Hex):", response.reference.toHex())
+ } catch (error) {
+ // Handle any errors during the upload
+ console.error("Error uploading SOC:", error)
+ }
}
+
+// Call the function to write the SOC
+uploadSOC()
+```
+Example output:
+
+```bash
+SOC identifier
+0000000000000000000000000000000000000000000000000000000000000000
+Ethereum address
+8d3766440f0d7b949a5e32995d09619a7f86e632
+SOC reference:
+Reference (Hex): 9d453ebb73b2fedaaf44ceddcf7a0aa37f3e3d6453fea5841c31f0ea6d61dc85
```
-:::warning Your communication privacy may be at risk
-We suggest using either ephemeral private keys (e.g. randomly generated) or the `Signer` interface when writing to SOC or Feeds. Never use your real Ethereum private keys here (or in any web applications really) directly because you may lose your funds stored on it.
-:::
-Using the writer interface is similar to using the reader:
+In this example:
+- `privateKey` defines the identity used to sign the SOC.
+- `NULL_IDENTIFIER` is the 32-byte value used as the identifier (can be replaced with any user-defined value).
+- `socWriter.upload()` signs and uploads the data, returning a `reference` as confirmation.
+
+The `identifier` and Ethereum address together determine the SOC address and must match exactly when retrieving the chunk later. The returned `reference` is included as part of the upload response, but unlike non-SOC uploads, the returned reference is not used to retrieve the chunk, rather the `identifier` and Ethereum address are used (see next section for example usage).
+
+## Reading SOCs
+
+To retrieve a previously uploaded SOC, you must know the Ethereum address of the owner (the signer used to upload the SOC) and the exact 32-byte `identifier` used during upload. These two values uniquely determine the SOC address in Swarm.
+
+To download a SOC in Bee-JS, use the `makeSOCReader()` method. This method takes the owner's Ethereum address (as a `EthAddress` instance, a hex string, or a `Uint8Array`) and returns a `SOCReader` object. You can then call `.download(identifier)` on the reader to retrieve the chunk's data.
+
+:::info SOC address is derived from the identifier and owner
+Unlike uploads using content addressed chunks which are retrieved by their Swarm reference hash, SOCs are retrieved using the combination of `identifier` and `owner`, not their Swarm reference hash.
+:::
```js
-const postageBatchId = await bee.createPostageBatch("100", 17)
-const signer = '0x634fb5a872396d9693e5c9f9d7233cfa93f395c093371017ff44aa9ae6564cdd'
-const socWriter = bee.makeSOCWriter(signer)
-const identifier = '0000000000000000000000000000000000000000000000000000000000000000'
-const data = new Uint8Array([1, 2, 3])
-const response = await socWriter.upload(postageBatchId, identifier, data)
+import { Bee, Size, NULL_IDENTIFIER } from "@ethersphere/bee-js"
+
+// Initialize Bee client pointing to the Swarm node
+const bee = new Bee('http://localhost:1633')
+
+// The owner's Ethereum address (20 bytes)
+const owner = '8d3766440f0d7b949a5e32995d09619a7f86e632'
+
+// Create a SOC reader object bound to the owner
+const socReader = bee.makeSOCReader(owner)
+
+async function readSOC() {
+ try {
+ // Download the SOC using the identifier
+ const response = await socReader.download(NULL_IDENTIFIER)
+
+ // Log the data
+ console.log("SOC Data:", response.payload.toUtf8())
+
+ // Optionally, you can use the data in other ways (e.g., process, display, etc.)
+ } catch (error) {
+ // Handle any errors during download
+ console.error("Error downloading SOC:", error)
+ }
+}
+
+// Call the function to read the SOC
+readSOC()
```
+In this example:
+- The `owner` is the Ethereum address used to sign the SOC.
+- `NULL_IDENTIFIER` is the same default identifier used in the earlier upload example.
+- The returned payload is a `Bytes` object, and `.toUtf8()` converts it to a human-readable string.
+
+Make sure the `owner` and `identifier` values match exactly what was used during upload β any mismatch will result in the chunk not being found.
+
+
## Feeds
Feeds are an abstraction built on top of SOCs to provide mutable resources on the otherwise immutable data types that Swarm supports.
@@ -116,7 +180,7 @@ Feeds enable Swarm users to represent a sequence of content updates. The content
### Topic
-In Swarm, `topic` is a 32-byte long arbitrary byte array. It's possible to choose an arbitrary topic for each application, and then knowing someone's (or something's) address, it's possible to find their feeds. Also, this can be the hash of a human-readable string, specifying the topic and optionally the subtopic of the feed. There is a helper function provided for that:
+In Swarm, `topic` is any arbitrary 32-byte long array. This allows for the selection of a unique topic for each application, which along with someone's (or something's) address, allow for the feed to be discovered. Also, this can be the hash of a human-readable string, specifying the topic and optionally the subtopic of the feed. There is a helper function provided for that:
```js
const topic = bee.makeFeedTopic('my-dapp.eth/outbox')
diff --git a/docs/documentation/staking.md b/docs/documentation/staking.md
index b7ea495a..5687499b 100644
--- a/docs/documentation/staking.md
+++ b/docs/documentation/staking.md
@@ -5,14 +5,104 @@ slug: /staking
sidebar_label: Staking
---
-## π§ Under Construction π§
-:::caution π§ This page is under construction
-This section is still being worked on. Check back soon for updates!
+Operating a Bee full node and staking BZZ makes you eligible to participate in the redistribution game β a mechanism for earning additional BZZ through by sharing disk space with the Swarm network. This guide shows how to use `bee-js` to deposit stake and check your node's staking status.
+
+:::danger
+β οΈ **Important:** Staked BZZ is **non-refundable** β once deposited, it **cannot be withdrawn**.
+:::
+
+
+:::info
+Currently, `bee-js` supports depositing stake and checking staking status, but does **not yet support** advanced features like [partial stake withdrawals](https://docs.ethswarm.org/docs/bee/working-with-bee/staking#partial-stake-withdrawals) or [reserve doubling](https://docs.ethswarm.org/docs/bee/working-with-bee/staking#reserve-doubling).
+
+
+For a complete guide to the requirements and configuration for staking, refer to the [Bee documentation](https://docs.ethswarm.org/docs/bee/working-with-bee/staking).
:::
-* Get staked xBZZ
-* Stake xBZZ
-* Get redistribution state
+
+
+
+## Stake BZZ
+
+To stake, use the `depositStake` method provided by `bee-js`. It accepts a value in PLUR, the smallest unit of BZZ (like wei in Ethereum). The `BZZ` utility class simplifies conversion from decimal string to PLUR.
+
+```js
+import { Bee, BZZ } from '@ethersphere/bee-js'
+
+const bee = new Bee('http://localhost:1633')
+
+async function main() {
+
+ // Convert 10 BZZ to PLUR
+ const amount = BZZ.fromDecimalString('10')
+
+ const txHash = await bee.depositStake(amount)
+ console.log('Stake deposited. Transaction hash:', txHash.toHex())
+}
+
+main().catch(console.error)
+```
+
+Example output:
+
+```bash
+Stake deposited. Transaction hash: e1b86eebc54b465d84ab278da94a387e9786076557ab8f3fe04ba1b52dc065c8
+```
+A successful staking transaction will return the transaction hash which you can look up on a blockchain explorer like [Gnosisscan](https://gnosisscan.io/tx/0xe1b86eebc54b465d84ab278da94a387e9786076557ab8f3fe04ba1b52dc065c8).
+
+## Check Staking Status
+
+After staking, you can confirm the deposited amount and monitor your nodeβs participation in the redistribution game:
+
+```js
+import { Bee } from '@ethersphere/bee-js'
+
+const bee = new Bee('http://localhost:1633')
+
+async function main() {
+ const stake = await bee.getStake()
+ const redistributionState = await bee.getRedistributionState()
+
+ console.log('Current staked amount:', stake.toDecimalString(), 'BZZ')
+ console.log('\nRedistribution State:')
+ console.log(JSON.stringify(redistributionState, null, 2))
+}
+
+main().catch(console.error)
+```
+
+Example output:
+
+```bash
+Current staked amount: 10.0000000000000001 BZZ
+
+Redistribution State:
+{
+ "minimumGasFunds": {
+ "state": "274506772500000"
+ },
+ "hasSufficientFunds": true,
+ "isFrozen": false,
+ "isFullySynced": true,
+ "phase": "claim",
+ "round": 261311,
+ "lastWonRound": 0,
+ "lastPlayedRound": 0,
+ "lastFrozenRound": 0,
+ "lastSelectedRound": 0,
+ "lastSampleDurationSeconds": 0,
+ "block": 39719372,
+ "reward": {
+ "state": "0"
+ },
+ "fees": {
+ "state": "0"
+ },
+ "isHealthy": true
+}
+```
+
+For details on interpreting these values, refer to the [staking status section](https://docs.ethswarm.org/docs/bee/working-with-bee/staking#check-status) of the Bee documentation.
\ No newline at end of file
diff --git a/docs/documentation/tracking-uploads.md b/docs/documentation/tracking-uploads.md
new file mode 100644
index 00000000..fca8adb3
--- /dev/null
+++ b/docs/documentation/tracking-uploads.md
@@ -0,0 +1,135 @@
+---
+title: Tracking Uploads
+id: tracking-uploads
+slug: /tracking-uploads
+sidebar_label: Tracking Uploads
+---
+
+You can track the progress of your uploads using "tags". Each tag tracks how many chunks were **split**, **stored**, **seen**, and **synced** by the network. By creating a tag before uploading and passing it to the upload function, you make the upload *trackable, allowing you to confirm whether your uploaded data has been fully synced.
+
+## How It Works
+
+### 1. Create a Tag
+
+Before uploading, create a new tag using `bee.createTag()`. This returns a unique tag UID that will be used to monitor the upload.
+
+```js
+const tag = await bee.createTag()
+console.log("Created new tag with UID:", tag.uid)
+```
+
+Alternatively, you can use an existing tag from `bee.getAllTags()` (useful for testing or reuse):
+
+```js
+const allTags = await bee.getAllTags()
+if (allTags.length > 0) {
+ tag = allTags[0]
+ console.log("Using existing tag with UID:", tag.uid)
+}
+```
+
+### 2. Upload a File with the Tag
+
+To enable tracking, pass the tag UID into the upload options under the `tag` key:
+
+```js
+const result = await bee.uploadFile(postageBatchId, fileData, 'nodes.json', {
+ tag: tag.uid,
+ contentType: 'application/json'
+})
+```
+
+This links the upload to your tag so you can monitor its progress.
+
+### 3. Track Tag Progress
+
+Use `bee.retrieveTag(tagUid)` to check how many chunks have been split and how many are synced. Poll repeatedly until `synced === split` to know when the upload has fully propagated.
+
+```js
+const tag = await bee.retrieveTag(tagUid)
+console.log(` - Total split: ${tag.split}`)
+console.log(` - Synced: ${tag.synced}`)
+```
+
+## Example Script
+
+```js
+import { Bee } from "@ethersphere/bee-js"
+import fs from "fs/promises"
+
+const bee = new Bee('http://localhost:1633')
+const postageBatchId = "129903062bedc4eca6fc1c232ed385e93dda72f711caa1ead6018334dd801cee"
+
+async function waitForTagSync(tagUid, interval = 800) {
+ while (true) {
+ const tag = await bee.retrieveTag(tagUid)
+
+ console.log(`Progress (Tag ${tagUid}):`)
+ console.log(` - Total split: ${tag.split}`)
+ console.log(` - Stored: ${tag.stored}`)
+ console.log(` - Seen: ${tag.seen}`)
+ console.log(` - Synced: ${tag.synced}`)
+
+ if (tag.split > 0 && tag.synced >= tag.split) {
+ console.log("Upload fully synced!")
+ break
+ }
+
+ await new Promise(resolve => setTimeout(resolve, interval))
+ }
+}
+
+async function uploadNodesJsonWithTag() {
+ try {
+ const fileData = await fs.readFile('./nodes.json')
+
+ const tag = await bee.createTag()
+ console.log("Created new tag with UID:", tag.uid)
+
+ const result = await bee.uploadFile(postageBatchId, fileData, 'nodes.json', {
+ tag: tag.uid,
+ contentType: 'application/json'
+ })
+
+ console.log("Uploaded reference:", result.reference.toHex())
+
+ await waitForTagSync(tag.uid)
+ } catch (error) {
+ console.error("Error uploading nodes.json:", error.message)
+ }
+}
+
+uploadNodesJsonWithTag()
+```
+
+Example terminal output:
+
+```bash
+Created new tag with UID: 85
+Uploaded reference: 78e5247e97b1a3362b6c3f054924dce734e0ffd7df0cb5ed9b636cb6a4a14d93
+Progress (Tag 85):
+ - Total split: 1078
+ - Stored: 0
+ - Seen: 0
+ - Synced: 546
+Progress (Tag 85):
+ - Total split: 1078
+ - Stored: 0
+ - Seen: 0
+ - Synced: 1078
+Upload fully synced!
+```
+
+## Deleting Tags
+
+You can delete tags you no longer need using their uid:
+
+```js
+await bee.deleteTag(tag.uid)
+console.log("Deleted tag:", tag.uid)
+```
+
+## References
+
+- [Bee docs β Syncing / Tags](https://docs.ethswarm.org/docs/develop/access-the-swarm/syncing)
+- [Bee API Reference β `/tags`](https://docs.ethswarm.org/api/#tag/Tag)
\ No newline at end of file
diff --git a/docs/documentation/upload-download.md b/docs/documentation/upload-download.md
index e003f7a8..7a65e962 100644
--- a/docs/documentation/upload-download.md
+++ b/docs/documentation/upload-download.md
@@ -5,143 +5,324 @@ slug: /upload-download
sidebar_label: Upload and Download
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
-Uploading your data to Swarm is easy with `bee-js`. Based on your needs you can either upload directly unstructured data, single file or even complex directories. Let's walk through the options one by one.
+Uploading and downloading with Swarm is easy with `bee-js`. Based on your needs you can either upload unstructured data directly, single files, lists of files, or entire directories. Each upload will return a Swarm reference hash, which is a unique identifier for the upload that can be used to download the uploaded content.
+
## Requirements
To use the example scripts below, you need:
- An instance of `bee-js`'s `Bee` [initialized](/docs/getting-started/) as `bee` using the API endpoint of a currently operating Bee node.
-- (Uploads only) The batch ID of a previously purchased usable postage batch with enough `remainingSize` left to upload the desired data. If you don't have one already, you will need to [buy a batch](/docs/storage/#purchasing-storage) to upload data. If you do have one, you will need to [get and save](/docs/storage/#selecting-a-batch) its batch ID.
-
-## Uploading
+- The batch ID of a previously purchased usable postage batch with enough `remainingSize` left to upload the desired data. If you don't have one already, you will need to [buy a batch](/docs/storage/#purchasing-storage) to upload data. If you do have one, you will need to [get and save](/docs/storage/#selecting-a-batch) its batch ID.
-The examples below assume you already have an instance of the `Bee` class [initialized](/docs/getting-started/) as `bee`, and the [batch ID](/docs/storage/#purchasing-storage) of a valid postage stamp batch saved as a string in `postageBatchId`.
-### Upload Data
+## Arbitrary Data
You can upload and retrieve any `string` or `Uint8Array` data with the `uploadData` and `downloadData` functions.
-When you download data the return type is the `Data` interface which extends `Uint8Array` with convenience functions like:
+When you download data the return type is `Bytes`. The `Bytes` class includes various convenience functions like:
- `toUtf8()` that converts the bytes into UTF-8 encoded string
- - `hex()` that converts the bytes into **non-prefixed** hex string
- - `json()` that converts the bytes into JSON object
+ - `toHex()` that converts the bytes to a hex string
+ - `toJSON()` that converts the bytes into JSON object
+
+:::info
+The `Bytes` class is a core data type in `bee-js`. It includes a variety of useful utility methods which you can learn more about on the [Utility Classes](/docs/utilities/) page.
+:::
```js
import { Bee } from "@ethersphere/bee-js"
const bee = new Bee('http://localhost:1633')
-const postageBatchId = "177da0994ed3000d241b183d33758aec42495bf9008fab059f0e3f208f3a1ade"
+const postageBatchId = "129903062bedc4eca6fc1c232ed385e93dda72f711caa1ead6018334dd801cee"
+
+const jsonData = {
+ message: "Bee is awesome!",
+ features: ["decentralized", "reliable", "scalable"],
+ version: 1.0
+};
-const result = await bee.uploadData(postageBatchId, "Bee is awesome!")
+const jsonString = JSON.stringify(jsonData);
-console.log(result.reference.toHex())
+const result = await bee.uploadData(postageBatchId, jsonString)
+// Prints the 64 character long hex string Swarm reference - make sure to save the reference in order to access the content later
+console.log(result.reference.toHex())
+
+// Use the Swarm reference hash to download the data
const retrievedData = await bee.downloadData(result.reference)
-console.log(retrievedData.toUtf8()) // prints 'Bee is awesome!'
+
+console.log(retrievedData) // Prints the raw data
+console.log(retrievedData.toUtf8()) // Prints the data as UTF-8 text
+console.log(retrievedData.toJSON()) // Prints the data as JSON
```
:::info Tip
A Swarm reference or hash is a 64 character long hex string which is the address of the uploaded data, file, or directory. It must saved so it can be used later to retrieve the uploaded content.
:::
-### Upload Single file
+Example terminal output:
+
+```bash
+e2d9d04da9f8a000ddcc50e1b86fbff00c6202b406a9dd7aa55d283747858c33
+Bytes {
+ bytes: ,
+ length: 92
+}
+{"message":"Bee is awesome!","features":["decentralized","reliable","scalable"],"version":1}
+{
+ message: 'Bee is awesome!',
+ features: [ 'decentralized', 'reliable', 'scalable' ],
+ version: 1
+}
+```
+
-You can also upload files by specifying a filename. When you download the file, `bee-js` will return additional information like the `contentType` or `name` of the file.
+## Single Files
+
+The `uploadFile` function accepts a `string`, `Uint8Array`, Node.js `Readable` stream, or browser `File` object as input data, along with an optional filename and upload options.
+
+:::info
+When working with browsers you can use the [`File` interface](https://developer.mozilla.org/en-US/docs/Web/API/File). The filename is taken from the `File` object itself, but can be overwritten through the second argument of the `uploadFile` function.
+:::
```js
import { Bee } from "@ethersphere/bee-js"
+import { readFileSync } from "fs"
const bee = new Bee('http://localhost:1633')
-const postageBatchId = "177da0994ed3000d241b183d33758aec42495bf9008fab059f0e3f208f3a1ade"
+const postageBatchId = "ec4d7e3acbd626471b33135164335dfcb0bed889dd4a951c09da8ea7b59c1fc9"
+
+async function uploadFromDisk() {
+ try {
+ // Read the file from the local file system
+ const filePath = "./textfile.txt"
+ const fileData = readFileSync(filePath)
+
+ // Upload the file data
+ const result = await bee.uploadFile(postageBatchId, fileData, "textfile.txt", {
+ contentType: "text/plain"
+ })
+
+ // Print the reference hash used to retrieve the content
+ console.log(result.reference.toHex())
+
+
+ // Download the file
+ const retrievedFile = await bee.downloadFile(result.reference.toHex())
+
+ console.log(retrievedFile.name) // Prints 'textfile.txt'
+ console.log(retrievedFile.contentType) // Prints 'text/plain'
+ console.log(retrievedFile.data.toUtf8()) // Prints file content
-const result = await bee.uploadFile(postageBatchId, "Bee is awesome!", "textfile.txt")
-const retrievedFile = await bee.downloadFile(result.reference.toHex())
+ return result.reference.toHex()
+ } catch (error) {
+ console.error("Error:", error.message)
+ }
+}
-console.log(retrievedFile.name) // prints 'textfile.txt'
-console.log(retrievedFile.contentType) // prints 'application/x-www-form-urlencoded'
-console.log(retrievedFile.data.toUtf8()) // prints 'Bee is awesome!'
+uploadFromDisk()
```
-You can directly upload using the [`File` interface](https://developer.mozilla.org/en-US/docs/Web/API/File). The filename is taken from the `File` object itself, but can be overwritten through the second argument of the `uploadFile` function.
+Example terminal output:
+
+```bash
+textfile.txt
+application/x-www-form-urlencoded
+this is a sample file
+0dca369c5a1a1ef5be1f5e293089c695920323805568382d9e97e3cd17678a3a
+```
+
+## Multiple Files
+
+For uploading multiple files at once in a browser environment you can use the `uploadFiles` function.
+
+*Note that it preserves the relative paths of all uploaded files, so the full paths (e.g. "folder/nested.txt") must be used when downloading them.*
+
+:::caution Browser Only
+
+
+The `uploadFiles` function is only supported for **browser environments**, as it requires input in the form of a [`FileList`](https://developer.mozilla.org/en-US/docs/Web/API/FileList) or array of [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) objects.
+
+While `File` is available in Node v20+, it is still not fully supported and may not always work as expected.
+:::
```js
import { Bee } from "@ethersphere/bee-js"
-import fs from 'fs'
const bee = new Bee('http://localhost:1633')
-const postageBatchId = "177da0994ed3000d241b183d33758aec42495bf9008fab059f0e3f208f3a1ade"
+const postageBatchId = "ec4d7e3acbd626471b33135164335dfcb0bed889dd4a951c09da8ea7b59c1fc9"
+
+async function uploadFiles() {
+ try {
+ // Create 3 File objects from strings with nested paths
+ const rootFile = new File(["Root file content"], "root.txt", { type: "text/plain" })
+ const nestedFile = new File(["Nested file content"], "folder/nested.txt", { type: "text/plain" })
+ const deepFile = new File(["Deeply nested file content"], "folder/subfolder/deep.txt", { type: "text/plain" })
+
+ // Upload all files
+ const result = await bee.uploadFiles(postageBatchId, [rootFile, nestedFile, deepFile])
+ console.log("Files uploaded with reference:", result.reference.toHex())
+
+ // Download each file by full path
+ const downloadedRoot = await bee.downloadFile(result.reference, 'root.txt')
+ const downloadedNested = await bee.downloadFile(result.reference, 'folder/nested.txt')
+ const downloadedDeep = await bee.downloadFile(result.reference, 'folder/subfolder/deep.txt')
+
+ // Display contents
+ console.log("Root file:", downloadedRoot.data.toUtf8())
+ console.log("Nested file:", downloadedNested.data.toUtf8())
+ console.log("Deep file:", downloadedDeep.data.toUtf8())
+
+ return result.reference.toHex()
+ } catch (error) {
+ console.error("Error:", error.message)
+ }
+}
+
+uploadFiles()
+```
-// Read the file content
-const fileContent = fs.readFileSync("./textFile.txt", "utf8")
+## Directories
-// Upload the file content with a name
-const result = await bee.uploadFile(postageBatchId, fileContent, "textfile.txt")
+:::info
+`uploadFilesFromDirectory` is not available in the browser as it relies on [`fs` from NodeJS](https://nodejs.org/api/fs.html).
+:::
-// Download the file
-const retrievedFile = await bee.downloadFile(result.reference)
-console.log(retrievedFile.name) // prints 'textfile.txt'
-console.log(retrievedFile.contentType) // should print 'application/x-www-form-urlencoded
-console.log(retrievedFile.data.toUtf8()) // prints the file content
-```
+The `uploadFilesFromDirectory` function takes a directory path as input and recursively uploads all files within it, including those in all nested subdirectories, while preserving their relative paths.
-### Files and Directories
+*When downloading files later, you must use the full relative paths exactly as they appeared during upload.*
-In browsers, you can easily upload an array of `File` objects coming from your form directly with [`FileList`](https://developer.mozilla.org/en-US/docs/Web/API/FileList). If the files uploaded through `uploadFiles` have a relative path, they are added relative to this filepath. Otherwise, the whole structure is flattened into single directory.
+Let's assume we have the following file structure:
+
+```bash
+.
+βββ folder
+βΒ Β βββ nested.txt
+βΒ Β βββ subfolder
+βΒ Β βββ deep.txt
+βββ root.txt
+```
```js
import { Bee } from "@ethersphere/bee-js"
-import fs from 'fs'
const bee = new Bee('http://localhost:1633')
-const postageBatchId = "177da0994ed3000d241b183d33758aec42495bf9008fab059f0e3f208f3a1ade"
-const foo = new File(["foo"], "foo.txt", { type: "text/plain" })
-const bar = new File(["bar"], "bar.txt", { type: "text/plain" })
+const postageBatchId = "ec4d7e3acbd626471b33135164335dfcb0bed889dd4a951c09da8ea7b59c1fc9"
+
+async function uploadDirectory() {
+ try {
+ // Upload the current directory (where the script is run)
+ const result = await bee.uploadFilesFromDirectory(postageBatchId, process.cwd())
+
+ console.log("Directory uploaded successfully!")
+ console.log("Swarm reference:", result.reference.toHex())
+
+ // Download each file using its relative path
+ const root = await bee.downloadFile(result.reference, 'root.txt')
+ const nested = await bee.downloadFile(result.reference, 'folder/nested.txt')
+ const deep = await bee.downloadFile(result.reference, 'folder/subfolder/deep.txt')
+
+ // Print out file contents
+ console.log("root.txt:", root.data.toUtf8())
+ console.log("folder/nested.txt:", nested.data.toUtf8())
+ console.log("folder/subfolder/deep.txt:", deep.data.toUtf8())
+ } catch (error) {
+ console.error("Error during upload or download:", error.message)
+ }
+}
+
+uploadDirectory()
+```
-const result = await bee.uploadFiles(postageBatchId, [ foo, bar ]) // upload
+Example terminal output:
+
+```bash
+Directory uploaded successfully!
+Swarm reference: 3e42f7cfbeec140129211fa24b9b57b2bff5932416dc8ff30b44c8446b259e92
+root.txt: Root level content
+folder/nested.txt: Nested content
+folder/subfolder/deep.txt: Deeply nested content
+```
+
+
+## Upload Options
+
+The `uploadData`, `uploadFile`, `uploadFiles`, and other similar methods accept an **options object** as their third argument. This object lets you modify how the upload is handled β for example, enabling encryption, pinning data locally, or attaching a tag to track the upload.
-const Foo = await bee.downloadFile(result.reference, './foo.txt') // download foo
-const Bar = await bee.downloadFile(result.reference, './bar.txt') // download bar
-console.log(Foo.data.toUtf8()) // prints 'foo'
-console.log(Bar.data.toUtf8()) // prints 'bar'
+### Pinning
+
+If you set `pin: true`, the uploaded data will be **stored locally** on your Bee node, even if it becomes unavailable on the wider Swarm network. This lets your node re-upload the content if needed:
+
+```js
+await bee.uploadData(postageBatchId, 'my content', { pin: true })
```
-You may also utilize the `uploadFilesFromDirectory` function, which takes the directory path as input and uploads all files in that directory. Let's assume we have the following file structure:
+:::info
+Pinning is local only. It doesn't make data permanent across the network.
+:::
-```sh
-.
-+-- foo.txt
-+-- dir
-| +-- bar.txt
+More info:
+- [Bee docs β Pinning](https://docs.ethswarm.org/docs/develop/access-the-swarm/pinning)
+
+
+### Encryption
+
+You can enable **client-side encryption** by setting `encrypt: true`. This encrypts the content before uploading and returns a longer Swarm reference that includes the decryption key.
+
+**Example:**
+
+```js
+await bee.uploadData(postageBatchId, 'sensitive content', { encrypt: true })
```
+When you later download the content, `bee-js` will decrypt it automatically if the reference contains the embedded key.
+
+More info:
+- [Store with Encryption](https://docs.ethswarm.org/docs/develop/access-the-swarm/store-with-encryption)
+
+
+### Tags
+
+Tags let you **track upload progress** through the Bee API. You can create a new tag using `bee.createTag()`, then pass its ID when uploading.
+
+**Example:**
+
```js
-import { Bee } from "@ethersphere/bee-js"
-import fs from 'fs'
+const tag = await bee.createTag()
+await bee.uploadData(postageBatchId, 'track me', { tag: tag.uid })
+```
-const bee = new Bee('http://localhost:1633')
-const postageBatchId = "177da0994ed3000d241b183d33758aec42495bf9008fab059f0e3f208f3a1ade"
-const result = await bee.uploadFilesFromDirectory(postageBatchId, './') // upload recursively current folder
+You can use the tag ID to monitor syncing status: how many chunks were split, stored, seen, and synced.
-const Foo = await bee.downloadFile(result.reference, './foo.txt') // download foo
-const Bar = await bee.downloadFile(result.reference, './dir/bar.txt') // download bar
+See [here](/docs/upload-download/#using-tags-to-monitor-upload-progress) for more info on creating, monitoring, and managing tags.
-console.log(Foo.data.toUtf8()) // prints 'foo'
-console.log(Bar.data.toUtf8()) // prints 'bar'
+
+### Deferred Uploads
+
+By default, uploads are **deferred**, meaning the client sends the data to the Bee node, generates and returns the Swarm reference hash, and the node starts syncing it to the network **in the background**. The function returns as soon as the chunks are processed and a Swarm reference is generated β **even before the data is actually available on the network**.
+
+This can be risky if you assume the data is already retrievable using the returned reference.
+
+**To ensure reliability, it is recommended to set `deferred: false`** unless you're explicitly tracking the upload using tags.
+
+**Recommended usage:**
+
+```js
+await bee.uploadData(postageBatchId, 'data', { deferred: false })
```
+:::tip
+If you do use `deferred: true`, make sure to use a [tag](/docs/upload-download/#using-tags-to-monitor-upload-progress) to track upload progress and confirm the success or failure of the upload.
+:::
diff --git a/sidebars.js b/sidebars.js
index 1cb529e4..050efd54 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -11,13 +11,17 @@ module.exports = {
'documentation/chequebook',
'documentation/storage',
'documentation/upload-download',
- // 'documentation/pinning',
- // 'documentation/staking',
- // 'documentation/manifests',
- // 'documentation/soc-and-feeds',
- // 'documentation/pss',
- // 'documentation/gsoc',
+ 'documentation/tracking-uploads',
+ 'documentation/pinning',
+ 'documentation/staking',
+ 'documentation/pss',
+ 'documentation/gsoc',
+ 'documentation/soc-and-feeds',
// 'documentation/act',
+ // 'documentation/manifests',
+
+
+
'documentation/utilities',
],
collapsed: false