Documentation Index
Fetch the complete documentation index at: https://docs.kynesys.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Storage Programs Examples
Each recipe below uses the real v3.1.0 SDK surface:
StorageProgram.* static helpers to build typed payloads.
demos.storagePrograms.sign(payload) to produce a signed transaction.
demos.confirm + demos.broadcast (or broadcastAndWait) to submit it.
demos.storagePrograms.read(address) to query state via RPC (free, no transaction).
For deeper background on operations, ACL modes, and resource limits, see the Operations and Access Control guides.
Boilerplate
Every example below assumes this setup:
import { Demos } from "@kynesyslabs/demosdk/websdk"
import { StorageProgram } from "@kynesyslabs/demosdk/storage"
const demos = new Demos()
await demos.connect("https://node2.demos.sh")
await demos.connectWallet(mnemonic)
const ownerAddress = demos.getAddress()
A small helper to broadcast a signed storage-program transaction with deterministic confirmation:
async function submit(payload: ReturnType<typeof StorageProgram.createStorageProgram>) {
const tx = await demos.storagePrograms.sign(payload)
const validityData = await demos.confirm(tx)
const { broadcast, status } = await demos.broadcastAndWait(validityData)
if (status.state !== "included") {
throw new Error(`Storage tx failed at block ${status.blockNumber}`)
}
return { tx, broadcast, status }
}
StorageProgram.createStorageProgram takes a nonce (the sender’s current account nonce) so that the derived storage address is unique per transaction from the same deployer. Fetch the nonce from demos.getAddressNonce(ownerAddress) before each create.
Example 1 — Public user profile
A read-only-for-the-world profile: anyone can fetch it, only the deployer can write.
async function createPublicProfile(displayName: string, bio: string) {
const nonce = await demos.getAddressNonce(ownerAddress)
const payload = StorageProgram.createStorageProgram(
ownerAddress,
"userProfile",
{
displayName,
bio,
joinedAt: Date.now(),
},
"json",
{ mode: "public" }, // public read, owner-only write
{ nonce, salt: "v1" },
)
await submit(payload)
return payload.storageAddress // stor-…
}
async function updateProfileBio(storageAddress: string, bio: string) {
const payload = StorageProgram.writeStorage(
storageAddress,
{ bio, lastUpdated: Date.now() },
"json",
)
await submit(payload)
}
async function readProfile(storageAddress: string) {
return await demos.storagePrograms.read(storageAddress)
}
writeStorage performs a shallow merge with the existing variables object — keys you don’t pass are preserved.
Example 2 — Restricted team workspace
Two ACL groups: editors (read + write) and viewers (read-only). Other addresses cannot read or write.
async function createTeamWorkspace(
teamName: string,
editors: string[],
viewers: string[],
) {
const nonce = await demos.getAddressNonce(ownerAddress)
const payload = StorageProgram.createStorageProgram(
ownerAddress,
`team-${teamName}`,
{
teamName,
createdAt: Date.now(),
announcements: [],
},
"json",
{
mode: "restricted",
groups: {
editors: {
members: editors,
permissions: ["read", "write"],
},
viewers: {
members: viewers,
permissions: ["read"],
},
},
},
{ nonce },
)
await submit(payload)
return payload.storageAddress
}
To rotate membership later, push a new ACL via updateAccessControl:
async function rotateEditors(storageAddress: string, editors: string[]) {
const payload = StorageProgram.updateAccessControl(storageAddress, {
mode: "restricted",
groups: {
editors: {
members: editors,
permissions: ["read", "write"],
},
},
})
await submit(payload)
}
Only the program’s owner can call updateAccessControl.
For non-JSON payloads (PDFs, images, archives), encode the bytes as base64 and use encoding: "binary". Storage Programs are capped at 128 KB per program — splitting larger blobs across multiple programs is the standard pattern.
async function uploadDocument(
name: string,
base64Body: string,
mimeType: string,
) {
const nonce = await demos.getAddressNonce(ownerAddress)
const payload = StorageProgram.createStorageProgram(
ownerAddress,
`doc-${name}`,
base64Body,
"binary",
{ mode: "owner" }, // private to the deployer
{
nonce,
metadata: {
filename: name,
mimeType,
size: base64Body.length,
},
},
)
await submit(payload)
return payload.storageAddress
}
async function deleteDocument(storageAddress: string) {
const payload = StorageProgram.deleteStorageProgram(storageAddress)
await submit(payload)
}
metadata is opaque to consensus but is returned alongside the data on read, so it’s a good place to stash filename, MIME type, encoded size, or content hash.
Where to go next