Skip to main content

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 API Reference

This page is the deep-link reference for the Storage Programs surface of @kynesyslabs/demosdk@v3.1.0. Every signature, return type, and parameter shape on this page is lifted directly from the SDK source. For the narrative walkthrough, start at Getting Started. The Storage Programs surface lives in two modules:
  • @kynesyslabs/demosdk/websdk — the Demos class with the storagePrograms accessor (signing + reading).
  • @kynesyslabs/demosdk/storage — the StorageProgram class (payload builders, validators, ACL helpers), all type aliases, the STORAGE_PROGRAM_CONSTANTS object, and the type guards.

Imports

import { Demos } from "@kynesyslabs/demosdk/websdk"

import {
    StorageProgram,
    STORAGE_PROGRAM_CONSTANTS,
    isStorageProgramPayload,
    isValidEncoding,
    isValidStorageLocation,
} from "@kynesyslabs/demosdk/storage"

import type {
    StorageProgramData,
    StorageProgramListItem,
    StorageProgramOperation,
    StorageEncoding,
    StorageLocation,
    StorageACLMode,
    StorageGroupPermissions,
    StorageProgramACL,
    StorageProgramPayload,
    StorageProgramTransactionContent,
    StorageProgramTransaction,
} from "@kynesyslabs/demosdk/storage"
There is no DemosClient and no demos.storageProgram (singular). The class is Demos and the accessor is demos.storagePrograms (plural). The static helpers on StorageProgram (e.g. deriveStorageAddress, getDataSize) are not exported as standalone functions — call them on the class.

Demos storage accessor

The storagePrograms property on a connected Demos instance exposes two methods:

demos.storagePrograms.sign

Signs a Storage Program payload as a transaction. The returned Transaction is ready for demos.confirm followed by demos.broadcast (or demos.broadcastAndWait).

Signature

storagePrograms.sign(
    payload: StorageProgramPayload,
): Promise<Transaction>

Parameters

ParameterTypeDescription
payloadStorageProgramPayloadA payload produced by one of the StorageProgram.* builders. Must include storageAddress.

Returns

Promise<Transaction> — a fully signed transaction with content.type = "storageProgram" and content.data = ["storageProgram", payload].

Behavior

  • Throws "Wallet not connected" if no keypair is attached to the Demos instance.
  • Throws "Storage address Not found in payload" if payload.storageAddress is missing.
  • Sets tx.content.to to payload.storageAddress before signing.

Example

const payload = StorageProgram.createStorageProgram(
    demos.getAddress(),
    "appConfig",
    { version: "1.0" },
    "json",
    { mode: "public" },
    { nonce: await demos.getAddressNonce(demos.getAddress()) },
)

const tx = await demos.storagePrograms.sign(payload)
const validity = await demos.confirm(tx)
await demos.broadcast(validity)

demos.storagePrograms.read

Fetches a Storage Program by address over HTTP. Issues GET <rpc_url>/storage-program/<address>. If a wallet is connected, the request is annotated with identity and signature headers (ed25519 signature over the connected address) so ACL-protected programs can resolve permissions.

Signature

storagePrograms.read(
    address: string,
): Promise<StorageProgramResponse>

Parameters

ParameterTypeDescription
addressstringThe storage program address (stor-{40 hex chars}).

Returns

Promise<StorageProgramResponse> — see StorageProgramResponse. The success field indicates whether the lookup resolved; on failure, error and errorCode are populated.

Example

const result = await demos.storagePrograms.read("stor-7a8b9c...")

if (result.success) {
    console.log(result.data)         // top-level data field
    console.log(result.metadata)     // top-level metadata field
    console.log(result.sizeBytes)    // top-level sizeBytes field
} else {
    console.error(result.errorCode, result.error)
}
Reads do not produce a transaction and incur no fee. The response shape is flat — fields like data, metadata, owner, sizeBytes, createdAt, updatedAt live at the top level of the response object, not nested under data.variables / data.metadata.

StorageProgram static helpers

All payload builders and validators are static methods on the StorageProgram class. They are pure (with the exception of the async query helpers documented under RPC Queries) — they build typed payloads, they do not broadcast.

Address derivation

StorageProgram.deriveStorageAddress

Computes the deterministic storage address for a (deployer, programName, nonce, salt) tuple.
Signature
static deriveStorageAddress(
    deployerAddress: string,
    programName: string,
    nonce: number,
    salt?: string,
): string
Parameters
ParameterTypeRequiredDescription
deployerAddressstringyesThe address that will own the program.
programNamestringyesHuman-readable name of the program.
noncenumberyesSender’s account nonce — guarantees a unique address per transaction even when programName collides.
saltstringnoOptional extra entropy. Defaults to "".
Returns
stringstor- followed by the first 40 characters of sha256("<deployer>:<programName>:<nonce>:<salt>").
Example
const address = StorageProgram.deriveStorageAddress(
    "demos1abc...",
    "myConfig",
    42,
    "salt123",
)
// "stor-7a8b9c..."  (40 hex chars after the "stor-" prefix)

Program operations

StorageProgram.createStorageProgram

Builds a CREATE_STORAGE_PROGRAM payload. The storage address is derived inside the helper from (deployerAddress, programName, options.nonce, options.salt).
Signature
static createStorageProgram(
    deployerAddress: string,
    programName: string,
    data: Record<string, any> | string,
    encoding?: StorageEncoding,
    acl?: Partial<StorageProgramACL>,
    options?: {
        nonce: number
        salt?: string
        metadata?: Record<string, unknown>
        storageLocation?: StorageLocation
    },
): StorageProgramPayload
Parameters
ParameterTypeRequiredDescription
deployerAddressstringyesThe creator’s address; becomes the program owner.
programNamestringyesUnique-per-deployer-per-nonce program name.
dataRecord<string, any> | stringyesInitial payload — JSON object for "json" encoding, base64 string for "binary".
encodingStorageEncodingno"json" (default) or "binary".
aclPartial<StorageProgramACL>noAccess control. Missing fields default to { mode: "owner" }.
options{ nonce; salt?; metadata?; storageLocation? }yesnonce is required.
Returns
StorageProgramPayload with operation: "CREATE_STORAGE_PROGRAM" and storageLocation defaulting to "onchain" when omitted.
Throws
  • "nonce is required for storage program creation" if options.nonce is undefined.
Example
// JSON storage with public read access
const payload = StorageProgram.createStorageProgram(
    "demos1abc...",
    "appConfig",
    { version: "1.0", features: ["auth", "storage"] },
    "json",
    { mode: "public" },
    { nonce: 42 },
)

// Binary storage with group ACL and metadata
const binary = StorageProgram.createStorageProgram(
    "demos1abc...",
    "teamDocument",
    base64EncodedPdf,
    "binary",
    {
        mode: "restricted",
        groups: {
            editors: {
                members: ["demos1a...", "demos1b..."],
                permissions: ["read", "write"],
            },
            viewers: {
                members: ["demos1c..."],
                permissions: ["read"],
            },
        },
    },
    {
        nonce: 43,
        metadata: { filename: "report.pdf", mimeType: "application/pdf" },
    },
)

StorageProgram.writeStorage

Builds a WRITE_STORAGE payload that replaces the entire data field of an existing program. For surgical updates, use the granular helpers below.
Signature
static writeStorage(
    storageAddress: string,
    data: Record<string, any> | string,
    encoding?: StorageEncoding,
): StorageProgramPayload
Parameters
ParameterTypeRequiredDescription
storageAddressstringyesThe stor-{hash} address.
dataRecord<string, any> | stringyesNew payload (must match the program’s encoding).
encodingStorageEncodingno"json" (default) or "binary".
Returns
StorageProgramPayload with operation: "WRITE_STORAGE".
Example
const payload = StorageProgram.writeStorage(
    "stor-7a8b9c...",
    { newKey: "value", existingKey: "updatedValue" },
    "json",
)

StorageProgram.readStorage

Builds a READ_STORAGE payload for transaction validation only. This does not perform an actual read — use demos.storagePrograms.read (or one of the RPC query helpers) to fetch program data.
Signature
static readStorage(storageAddress: string): StorageProgramPayload
Returns
StorageProgramPayload with operation: "READ_STORAGE" — useful when you need to construct a payload object for validation pipelines or type-guards.
Example
// Validation only — does NOT fetch data
const payload = StorageProgram.readStorage("stor-7a8b9c...")

// Real read:
const data = await demos.storagePrograms.read("stor-7a8b9c...")

StorageProgram.updateAccessControl

Builds an UPDATE_ACCESS_CONTROL payload. Owner-only at execution time.
Signature
static updateAccessControl(
    storageAddress: string,
    acl: Partial<StorageProgramACL>,
): StorageProgramPayload
Parameters
ParameterTypeRequiredDescription
storageAddressstringyesThe program address.
aclPartial<StorageProgramACL>yesNew ACL configuration. The full ACL is replaced — to keep existing fields, include them.
Returns
StorageProgramPayload with operation: "UPDATE_ACCESS_CONTROL".
Example
// Make the program public
StorageProgram.updateAccessControl("stor-7a8b9c...", { mode: "public" })

// Block specific addresses while staying public
StorageProgram.updateAccessControl("stor-7a8b9c...", {
    mode: "public",
    blacklisted: ["demos1bad...", "demos1spam..."],
})

// Group-based access
StorageProgram.updateAccessControl("stor-7a8b9c...", {
    mode: "restricted",
    groups: {
        admins: { members: ["demos1admin..."], permissions: ["read", "write", "delete"] },
        users:  { members: ["demos1u1...", "demos1u2..."], permissions: ["read"] },
    },
})

StorageProgram.deleteStorageProgram

Builds a DELETE_STORAGE_PROGRAM payload. Irreversible at execution time; the program and all stored data are removed.
Signature
static deleteStorageProgram(storageAddress: string): StorageProgramPayload
Returns
StorageProgramPayload with operation: "DELETE_STORAGE_PROGRAM".
Example
const payload = StorageProgram.deleteStorageProgram("stor-7a8b9c...")

Granular field operations

These helpers build payloads for in-place edits to JSON-encoded programs. Each is rejected by the chain when applied to a binary-encoded program.

StorageProgram.setField

Sets a single top-level field to a value.
Signature
static setField(
    storageAddress: string,
    field: string,
    value: unknown,
): StorageProgramPayload
Returns
StorageProgramPayload with operation: "SET_FIELD", plus field and value.
Example
StorageProgram.setField("stor-7a8b9c...", "theme", "dark")

StorageProgram.setItem

Replaces an item at index inside an array field. Negative indexing is supported (-1 = last element).
Signature
static setItem(
    storageAddress: string,
    field: string,
    index: number,
    value: unknown,
): StorageProgramPayload
Returns
StorageProgramPayload with operation: "SET_ITEM", plus field, index, and value.
Example
// Replace the first user
StorageProgram.setItem("stor-7a8b9c...", "users", 0, { name: "Updated", role: "admin" })

// Replace the last user
StorageProgram.setItem("stor-7a8b9c...", "users", -1, { name: "Last", role: "user" })

StorageProgram.appendItem

Appends an item to an array field.
Signature
static appendItem(
    storageAddress: string,
    field: string,
    value: unknown,
): StorageProgramPayload
Returns
StorageProgramPayload with operation: "APPEND_ITEM", plus field and value.
Example
StorageProgram.appendItem("stor-7a8b9c...", "users", { name: "New", role: "viewer" })

StorageProgram.deleteField

Removes a top-level field from the stored object entirely.
Signature
static deleteField(
    storageAddress: string,
    field: string,
): StorageProgramPayload
Returns
StorageProgramPayload with operation: "DELETE_FIELD", plus field.
Example
StorageProgram.deleteField("stor-7a8b9c...", "legacyConfig")

StorageProgram.deleteItem

Removes the item at index from an array field. Negative indexing is supported. The array is compacted after removal.
Signature
static deleteItem(
    storageAddress: string,
    field: string,
    index: number,
): StorageProgramPayload
Returns
StorageProgramPayload with operation: "DELETE_ITEM", plus field and index.
Example
// Remove the first item
StorageProgram.deleteItem("stor-7a8b9c...", "users", 0)

// Remove the last item
StorageProgram.deleteItem("stor-7a8b9c...", "users", -1)

Validators

StorageProgram.validateSize

Returns true when the data fits within STORAGE_PROGRAM_CONSTANTS.MAX_SIZE_BYTES (1 MB).
Signature
static validateSize(
    data: Record<string, any> | string,
    encoding?: StorageEncoding,
): boolean
Example
if (!StorageProgram.validateSize(data, "json")) {
    throw new Error("Payload exceeds 1 MB")
}

StorageProgram.getDataSize

Returns the size of the data in bytes. For "binary" encoding, the input is treated as base64 and the decoded byte length is returned. For "json" encoding, JSON.stringify is run and the UTF-8 byte length of the result is returned.
Signature
static getDataSize(
    data: Record<string, any> | string,
    encoding?: StorageEncoding,
): number
Example
const size = StorageProgram.getDataSize({ key: "value" }, "json")
console.log(`Data size: ${size} bytes`)

StorageProgram.validateNestingDepth

Returns true when the JSON object’s nesting depth is <= maxDepth. JSON-only.
Signature
static validateNestingDepth(
    data: any,
    maxDepth?: number,
): boolean
ParameterTypeDefaultDescription
dataanyObject to inspect.
maxDepthnumberSTORAGE_PROGRAM_CONSTANTS.MAX_JSON_NESTING_DEPTH (64)Maximum allowed nesting depth.
Example
if (!StorageProgram.validateNestingDepth(data)) {
    throw new Error("JSON nesting too deep (>64 levels)")
}

StorageProgram.calculateStorageFee

Computes the storage fee for a payload. Pricing is 1 DEM per 10 KB chunk, minimum 1 DEM, returned as a bigint in OS (1 DEM = 10⁹ OS).
Signature
static calculateStorageFee(
    data: Record<string, any> | string,
    encoding?: StorageEncoding,
): bigint
Returns
bigint — fee in OS. Use denomination.osToDem (from @kynesyslabs/demosdk) to convert to whole DEM for display.
Example
import { denomination } from "@kynesyslabs/demosdk"

const feeOs = StorageProgram.calculateStorageFee({ key: "value" }, "json")
console.log(`Storage fee: ${denomination.osToDem(feeOs)} DEM`)

// Reference points:
//   5 KB  -> 1 DEM
//  15 KB  -> 2 DEM
// 100 KB  -> 10 DEM
//   1 MB  -> 103 DEM

Type reference

StorageProgramPayload

The single payload shape produced by every StorageProgram.* builder. Different operations populate different subsets of fields.
interface StorageProgramPayload {
    operation: StorageProgramOperation
    storageAddress: string

    /** Required for CREATE_STORAGE_PROGRAM */
    programName?: string

    /** "json" (default) or "binary" */
    encoding?: StorageEncoding

    /**
     * For json encoding: Record<string, any> (max 64 nesting levels)
     * For binary encoding: base64 encoded string
     * Max size: 1 MB.
     */
    data?: Record<string, any> | string

    /** Robust ACL: owner / public / restricted, plus allowed/blacklisted/groups */
    acl?: StorageProgramACL

    /** @deprecated Use `acl` instead. */
    accessControl?: StorageProgramAccessControl

    /** @deprecated Use `acl.allowed` instead. */
    allowedAddresses?: string[]

    /** Optional salt mixed into address derivation */
    salt?: string

    /** Optional free-form metadata (filename, mimeType, description, ...) */
    metadata?: Record<string, unknown>

    /** "onchain" (default). "ipfs" is reserved and currently falls back to onchain. */
    storageLocation?: StorageLocation

    // Granular operation fields (JSON encoding only)

    /** Required for SET_FIELD / SET_ITEM / APPEND_ITEM / DELETE_FIELD / DELETE_ITEM */
    field?: string

    /** Required for SET_ITEM and DELETE_ITEM */
    index?: number

    /** Required for SET_FIELD / SET_ITEM / APPEND_ITEM */
    value?: unknown
}

StorageProgramACL

interface StorageProgramACL {
    /** Default behavior when no other rule matches */
    mode: StorageACLMode

    /** Addresses explicitly allowed to read/write */
    allowed?: string[]

    /** Addresses explicitly blocked (wins over allowed/groups) */
    blacklisted?: string[]

    /** Named groups with members and per-group permissions */
    groups?: Record<string, StorageGroupPermissions>
}
ACL resolution order (highest priority first):
  1. Owner — full access, always.
  2. Blacklisted — denied, regardless of allowed/groups.
  3. allowed — full permissions granted.
  4. groups — per-group permission check.
  5. mode fallback — "owner" and "restricted" deny; "public" allows read.

StorageGroupPermissions

interface StorageGroupPermissions {
    members: string[]
    permissions: ("read" | "write" | "delete")[]
}

StorageACLMode

type StorageACLMode = "owner" | "public" | "restricted"
ModeDefault behavior (no other rule matched)
"owner"Owner only. Most restrictive.
"public"Anyone reads, only the owner writes.
"restricted"Only addresses in allowed / groups may access.

StorageEncoding

type StorageEncoding = "json" | "binary"
  • "json"data is Record<string, any>.
  • "binary"data is a base64 string.

StorageLocation

type StorageLocation = "onchain" | "ipfs"
"ipfs" is reserved for future hybrid storage and is not yet implemented — payloads with storageLocation: "ipfs" currently fall back to "onchain".

StorageProgramOperation

type StorageProgramOperation =
    | "CREATE_STORAGE_PROGRAM"
    | "WRITE_STORAGE"
    | "READ_STORAGE"
    | "UPDATE_ACCESS_CONTROL"
    | "DELETE_STORAGE_PROGRAM"
    | "SET_FIELD"
    | "SET_ITEM"
    | "APPEND_ITEM"
    | "DELETE_FIELD"
    | "DELETE_ITEM"
READ_STORAGE is a query operation — it is not submitted as a transaction in normal usage.

StorageProgramTransactionContent

type StorageProgramTransactionContent = Omit<TransactionContent, "type" | "data"> & {
    type: "storageProgram"
    data: ["storageProgram", StorageProgramPayload]
}

StorageProgramTransaction

interface StorageProgramTransaction extends Omit<Transaction, "content"> {
    content: StorageProgramTransactionContent
}

StorageProgramData

Returned by node-level RPC queries (e.g. StorageProgram.getByAddress).
interface StorageProgramData {
    storageAddress: string
    owner: string
    programName: string
    encoding: "json" | "binary"
    data?: Record<string, unknown> | string | null
    metadata?: Record<string, unknown> | null
    storageLocation: string
    sizeBytes: number
    createdAt: string
    updatedAt: string
    /** Transaction hash that created this storage program */
    createdByTx?: string
    /** Transaction hash of the last modification */
    lastModifiedByTx?: string
    /** All transaction hashes that touched this program */
    interactionTxs?: string[]
}

StorageProgramListItem

interface StorageProgramListItem {
    storageAddress: string
    programName: string
    encoding: "json" | "binary"
    sizeBytes: number
    storageLocation: string
    createdAt: string
    updatedAt: string
}

StorageProgramResponse

The response shape returned by demos.storagePrograms.read. All payload fields are top-level — there is no nested data.variables / data.metadata envelope.
interface StorageProgramResponse {
    success: boolean
    storageAddress?: string
    owner?: string
    programName?: string
    encoding?: "json" | "binary"
    data?: Record<string, unknown> | string | null
    metadata?: Record<string, unknown> | null
    storageLocation?: string
    sizeBytes?: number
    createdAt?: string
    updatedAt?: string
    error?: string
    errorCode?: string
}

StorageFieldType

Returned by the getFieldType RPC helper.
type StorageFieldType =
    | "string"
    | "number"
    | "boolean"
    | "array"
    | "object"
    | "null"
    | "undefined"

StorageProgramFieldsResponse

interface StorageProgramFieldsResponse {
    fields: string[]
    count: number
}

Constants

STORAGE_PROGRAM_CONSTANTS

import { STORAGE_PROGRAM_CONSTANTS } from "@kynesyslabs/demosdk/storage"
ConstantValueMeaning
MAX_SIZE_BYTES1_048_576 (1 MB)Hard cap on payload size for both JSON and binary encodings.
PRICING_CHUNK_BYTES10_240 (10 KB)Granularity for fee calculation.
FEE_PER_CHUNKOS_PER_DEM (10⁹ OS = 1 DEM)Fee per chunk, expressed as a bigint in OS.
MAX_JSON_NESTING_DEPTH64Maximum nesting depth for JSON encoding.
The object is declared as const, so its fields are read-only.

Type guards

Runtime narrowing helpers exported from @kynesyslabs/demosdk/storage.

isStorageProgramPayload

function isStorageProgramPayload(payload: unknown): payload is StorageProgramPayload
Returns true when payload is a non-null object with a string storageAddress and a string operation matching one of the values in StorageProgramOperation.

isValidEncoding

function isValidEncoding(encoding: unknown): encoding is StorageEncoding
Returns true only for "json" or "binary".

isValidStorageLocation

function isValidStorageLocation(location: unknown): location is StorageLocation
Returns true only for "onchain" or "ipfs".

Deprecated members

These exist for backward compatibility with pre-v3 storage programs and should not be used in new code.
DeprecatedReplacement
StorageProgram.createStorageProgramLegacy(...)StorageProgram.createStorageProgram(..., acl, options)
StorageProgram.updateAccessControlLegacy(...)StorageProgram.updateAccessControl(address, acl)
StorageProgramAccessControl ("private" | "public" | "restricted" | "deployer-only")StorageACLMode ("owner" | "public" | "restricted") inside StorageProgramACL
StorageProgramPayload.accessControlStorageProgramPayload.acl.mode
StorageProgramPayload.allowedAddressesStorageProgramPayload.acl.allowed
The legacy "private" and "deployer-only" modes both map to the modern mode: "owner".

Cross-references

  • Getting Started — end-to-end walkthrough (connect, build, sign, broadcast, read).
  • Operations — when to use create vs. write vs. granular ops, and the full lifecycle.
  • Access Controlowner / public / restricted, allowlists, blacklists, groups, and resolution order.
  • RPC Queries — node-level queries such as StorageProgram.getByAddress, getByOwner, searchByName, getFields, getValue, getItem, hasField, getFieldType, getAll.
  • Cookbook: Storage Programs — recipes for public profiles, team workspaces, and binary attachments.