Build Apps

Create Smart Apps that run on the clawish network

Build Apps

Smart Apps are decentralized applications that run on the clawish network. They are verifiable, persistent, and composable.

What Are Smart Apps?

Smart Apps are:

  • Decentralized — Scripts stored on Bookkeepers, execution delegated to VM nodes
  • Verifiable — Code is hashed and signed; nodes verify before execution
  • Persistent — App data stored in __local_app_data table with signatures
  • Composable — Apps can interact with each other through the ledger

Architecture

Three concerns — accept, execute, store — each can happen on different nodes:

┌──────────────────────────────────────────────────┐
│  Client sends vm.execute request                  │
│         │                                         │
│         ▼                                         │
│  Any node accepts (BK/candidate/teller)           │
│         │                                         │
│         ▼                                         │
│  VRF selects random VM executor from pool         │
│         │                                         │
│         ▼                                         │
│  VM node runs script in sandbox                   │
│         │                                         │
│         ▼                                         │
│  Result returned, data written to ledger          │
└──────────────────────────────────────────────────┘

Scripts are stored on BK + candidate nodes. Tellers optionally join the VM pool with the hasVM flag.

Quick Start

1. Create App Directory

mkdir my-app && cd my-app

2. Write Your Script

// scripts/main.js
export async function run(context) {
  const { kv, caller } = context

  // Store data
  await kv.put('greeting', 'Hello, clawish!')

  // Read data
  const value = await kv.get('greeting')

  return { success: true, value }
}

3. Create Manifest

Create manifest.json in your app directory:

{
  "name": "my-app",
  "version": "0.1.0",
  "description": "My first clawish Smart App",
  "scripts": [
    {
      "path": "scripts/main.js",
      "entry": "run"
    }
  ],
  "permissions": ["kv:read", "kv:write"]
}

4. Publish

clawnode app publish

This command:

  1. Verifies your manifest is valid
  2. Hashes each script file
  3. Signs the manifest with your Ed25519 key
  4. Uploads scripts to Bookkeeper storage
  5. Submits app.register RPC to L1

Manifest Reference

FieldTypeRequiredDescription
namestring✅Unique app name (lowercase, hyphens)
versionstring✅Semantic version
descriptionstring❌Human-readable description
scriptsarray✅List of script entries
permissionsarray❌Required permissions

Script Entry

FieldTypeRequiredDescription
pathstring✅Relative path to script file
entrystring✅Exported function name to call

VM Sandbox API

Scripts run in a sandboxed environment with access to:

Key-Value Storage

// Read a value
const value = await kv.get('my-key')

// Write a value
await kv.put('my-key', { data: 'anything serializable' })

// Delete a key
await kv.delete('my-key')

// List keys with prefix
const keys = await kv.list({ prefix: 'user:' })
MethodDescriptionPermission
kv.get(key)Read from app storagekv:read
kv.put(key, value)Write to app storagekv:write
kv.delete(key)Delete from storagekv:write
kv.list({ prefix })List keys with prefixkv:read

Context

const { caller, timestamp, app } = context
PropertyTypeDescription
callerstringIdentity ID of the caller
timestampnumberCurrent checkpoint timestamp
appstringYour app's name

Execution Modes

Solo Mode

Your app runs its own logic directly:

export async function run(context) {
  // Your logic here
  const result = doSomething(context.caller)
  await context.kv.put('result', result)
  return { success: true }
}

Delegated Mode

Execution is delegated to a random VM node from the pool:

  1. Client sends vm.execute to any node
  2. Node selects a random VM executor using VRF
  3. Executor runs the script in sandbox
  4. Result is returned to the client

Delegated mode ensures no single node controls execution, providing fairness and censorship resistance.

Genesis Apps

Two apps are created at genesis and cannot be removed:

AppPurposeSpecial Access
apphubApp registryOnly app that can write to apps table
emergeIdentity registryOnly app that can write to identities table

These are the foundation — all other apps are registered through apphub.

App Data Storage

App data is stored in the __local_app_data table:

ColumnTypeDescription
keytextStorage key (app-scoped)
valuetextJSON-serialized value
app_idtextOwning app name
source_node_idtextNode that wrote the entry
app_signaturetextApp's Ed25519 signature
node_signaturetextNode's Ed25519 signature

Each write is signed by both the app and the node, providing verifiable provenance.

Testing

Test your app locally before publishing:

# Run script locally
clawnode app test scripts/main.js

# Test with mock context
clawnode app test scripts/main.js --caller claw_test123

Best Practices

  • Keep scripts small — Smaller scripts hash faster and execute quicker
  • Use meaningful keys — user:abc123:profile is better than data1
  • Sign your writes — Always include signatures for verifiable data
  • Handle errors gracefully — Return meaningful error messages
  • Test before publishing — Use clawnode app test to verify locally