# LumeScope Actions API reference
Source: https://docs.lumera.io/api/actions
Query Cascade and Sense actions on the Lumera network with decoded metadata, lifecycle details, and aggregated statistics.
The LumeScope Actions API gives you a decoded view of every Cascade and Sense action registered on the Lumera network. Rather than parsing raw chain transactions yourself, you query a pre-indexed store. The store tracks each action through its full lifecycle, from the initial `register` transaction through `finalize` and `approve`. Three endpoints cover the most common needs. You can list actions with filters, fetch the full detail for a single action, and retrieve aggregate counts across the network.
All Actions API endpoints are read-only and require no authentication. The base URL for mainnet is `https://lumescope.lumera.io`. Substitute `https://lumescope.testnet.lumera.io` for testnet.
***
## GET /v1/actions
Returns a paginated list of Cascade and Sense actions with fully decoded metadata. Use the query parameters to filter by type, creator, state, and block height range.
### Query parameters
Filter by action type. Accepted values are `cascade` and `sense`. Omit to return both types.
Filter by the Bech32 address of the account that created the action.
Filter by action state. Common values are `registered`, `finalized`, and `approved`.
Filter actions associated with a specific SuperNode address.
Return only actions created at or after this block height.
Return only actions created at or before this block height.
Maximum number of actions to return per page.
Opaque pagination cursor returned by the previous response. Pass this value to fetch the next page.
When `true`, each action in the list includes its full transaction lifecycle (register, finalize, approve tx hashes and heights).
### Example request
```shell Cascade actions theme={null}
curl https://lumescope.lumera.io/v1/actions?type=cascade&limit=10
```
```shell With transactions theme={null}
curl "https://lumescope.lumera.io/v1/actions?type=sense&limit=5&include_transactions=true"
```
```shell Height range theme={null}
curl "https://lumescope.lumera.io/v1/actions?fromHeight=100000&toHeight=200000&limit=20"
```
### Example response
```json theme={null}
{
"actions": [
{
"id": "abc123",
"type": "cascade",
"status": "approved",
"data_hash": "sha256:deadbeef...",
"created_at": "2024-01-15T10:30:00Z",
"finalized_at": "2024-01-15T10:45:00Z"
}
],
"total": 1234,
"page": 1
}
```
### Response fields
Array of action objects matching the query.
Unique action identifier on the Lumera chain.
The action type, either `cascade` or `sense`.
The current lifecycle state, one of `registered`, `finalized`, or `approved`.
SHA-256 content hash of the stored data.
RFC 3339 timestamp of the registration transaction.
RFC 3339 timestamp of the finalization transaction. Null if not yet finalized.
Total number of actions matching the query filters, across all pages.
Current page number (1-indexed).
***
## GET /v1/actions/\{id}
Returns the full details for a single action, including its complete transaction lifecycle. Use this endpoint when you need register, finalize, and approve transaction hashes and block heights for a specific action.
### Path parameters
The unique action ID. This is the same `id` value returned by `GET /v1/actions`.
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/actions/abc123
```
### Example response
```json theme={null}
{
"id": "abc123",
"type": "cascade",
"status": "approved",
"data_hash": "sha256:deadbeef...",
"creator": "lumera1qg5ega6dykkxc307y25pecuufrjkxkaggkkxh",
"created_at": "2024-01-15T10:30:00Z",
"finalized_at": "2024-01-15T10:45:00Z",
"approved_at": "2024-01-15T10:50:00Z",
"transactions": {
"register": {
"tx_hash": "A1B2C3D4...",
"height": 123456
},
"finalize": {
"tx_hash": "E5F6G7H8...",
"height": 123789
},
"approve": {
"tx_hash": "I9J0K1L2...",
"height": 123901
}
}
}
```
### Response fields
Unique action identifier.
The action type, either `cascade` or `sense`.
Current lifecycle state.
SHA-256 content hash of the stored data.
Bech32 address of the account that registered the action.
RFC 3339 timestamp of the registration transaction.
RFC 3339 timestamp of the finalization transaction.
RFC 3339 timestamp of the approval transaction.
Transaction lifecycle for this action.
Transaction hash of the register message.
Block height of the register transaction.
Transaction hash of the finalize message.
Block height of the finalize transaction.
Transaction hash of the approve message.
Block height of the approve transaction.
***
## GET /v1/actions/stats
Returns aggregated action statistics across the entire network. Use this endpoint to build dashboards or monitor overall Cascade and Sense activity without paginating through individual records.
### Query parameters
Scope statistics to a single action type, either `cascade` or `sense`. Omit to aggregate across both types.
Start of the time window as an RFC 3339 timestamp (e.g. `2024-01-01T00:00:00Z`).
End of the time window as an RFC 3339 timestamp.
### Example request
```shell All actions theme={null}
curl https://lumescope.lumera.io/v1/actions/stats
```
```shell Cascade only theme={null}
curl "https://lumescope.lumera.io/v1/actions/stats?type=cascade"
```
```shell Time-bounded theme={null}
curl "https://lumescope.lumera.io/v1/actions/stats?from=2024-01-01T00:00:00Z&to=2024-12-31T23:59:59Z"
```
### Example response
```json theme={null}
{
"total_actions": 5000,
"cascade_count": 3200,
"sense_count": 1800,
"approved_count": 4800
}
```
### Response fields
Total number of actions indexed by LumeScope, across all types and states.
Number of Cascade actions.
Number of Sense actions.
Number of actions that have reached the `approved` state.
***
Combine `GET /v1/actions/stats` with time-window filters to plot approval rates over time and detect anomalies in SuperNode throughput.
# CometBFT RPC reference
Source: https://docs.lumera.io/api/cometbft-rpc
Query blocks, transactions, and validator sets through the consensus RPC that every Lumera node serves on port 26657.
Every `lumerad` node serves the CometBFT RPC on port 26657. CometBFT is the consensus engine under Lumera Protocol, and this API exposes what it sees. Raw blocks, transactions, the validator set, peer connections, and node health. Cosmos wallets, client libraries, and the Lumera SDKs use it to broadcast transactions and follow chain state.
This page covers the endpoints developers reach for most. The [official CometBFT RPC documentation](https://docs.cometbft.com/v0.38/rpc/) lists the full catalog. Lumera runs the v0.38 line.
## Base URLs
| Network | Endpoint |
| - | - |
| Mainnet (official) | `https://rpc.lumera.io` |
| Mainnet (community) | `https://lumera-rpc.polkachu.com` |
| Testnet (community) | `https://lumera-testnet-rpc.polkachu.com` |
The official mainnet endpoint can be intermittent, so the examples below use the community endpoints.
Validators keep port 26657 closed to the public. Use the public endpoints above, or run your own full node when you need guaranteed access and no rate limits.
## /status
Node identity, sync state, and the latest block. Check `catching_up` to see whether a node is fully synced.
```bash theme={null}
curl -fsSL https://lumera-rpc.polkachu.com/status | jq '.result.sync_info'
```
## /abci\_info
The application version the node runs. This is the same check the validator runbooks use to confirm the current network binary.
```bash theme={null}
curl -fsSL https://lumera-rpc.polkachu.com/abci_info | jq -r .result.response.version
```
## /block
The latest block, or a specific block when you pass `height`.
```bash theme={null}
# Latest block header
curl -fsSL https://lumera-rpc.polkachu.com/block | jq '.result.block.header'
# A specific height
curl -fsSL "https://lumera-rpc.polkachu.com/block?height=1000000" | jq '.result.block.header'
```
## /tx
A transaction by hash. The hash must carry the `0x` prefix. A `code` of `0` in `tx_result` means the transaction succeeded.
```bash theme={null}
curl -fsSL "https://lumera-rpc.polkachu.com/tx?hash=0xTX_HASH" | jq '.result.tx_result.code'
```
## /validators
The active validator set at the latest height, or at a given `height`. Lumera caps the active set at 50 validators, so one page covers it. Governance can change the cap.
```bash theme={null}
curl -fsSL "https://lumera-rpc.polkachu.com/validators?per_page=100" | jq '.result.total'
```
## /net\_info
Peer count and peer details. Useful when you run your own node and want to verify connectivity.
```bash theme={null}
curl -fsSL https://lumera-rpc.polkachu.com/net_info | jq '.result.n_peers'
```
## /broadcast\_tx\_sync
Broadcasts a signed transaction and returns after the mempool check. Most applications broadcast through `lumerad` or an SDK instead of calling this endpoint by hand.
```bash theme={null}
curl -fsSL "https://lumera-testnet-rpc.polkachu.com/broadcast_tx_sync?tx=0xSIGNED_TX_BYTES"
```
## Beyond HTTP GET
Every endpoint also accepts JSON-RPC over HTTP POST, and a WebSocket at `/websocket` supports event subscriptions. See the [CometBFT RPC documentation](https://docs.cometbft.com/v0.38/rpc/) for parameters, response schemas, and the endpoints not shown here.
## Next steps
Ethereum-style RPC on EVM enabled networks.
All Lumera API surfaces at a glance.
Chain IDs, endpoints, and seeds for mainnet and testnet.
# EVM JSON-RPC reference
Source: https://docs.lumera.io/api/evm-jsonrpc
Call standard Ethereum JSON-RPC methods on Lumera EVM nodes over HTTP and WebSocket.
Lumera Protocol nodes on EVM enabled networks serve a standard Ethereum JSON-RPC API. HTTP runs on port 8545 and WebSocket on port 8546, both enabled by default on upgraded nodes. Any tool that speaks Ethereum RPC works unchanged. Hardhat, Foundry, Ethers, viem, and MetaMask all connect by pointing at the endpoint and chain ID below.
The EVM is live on testnet today. Mainnet runs `v1.12.0` and gains the EVM JSON-RPC with its [EVM upgrade](/upgrades/evm-upgrade).
## Base URLs and chain ID
| Network | HTTP endpoint | Status |
| - | - | - |
| Testnet | `https://evm-rpc.testnet.lumera.io` | Live |
| Mainnet | `https://evm-rpc.lumera.io` | Answers after the EVM upgrade |
| Your own node | `http://localhost:8545` | EVM enabled builds (v1.20.0 and later) |
The EVM chain ID is `76857769` on every Lumera network. JSON-RPC returns it as hex `0x494c1a9`. The currency symbol is LUME, exposed to the EVM as the 18 decimal denom `alume`.
WebSocket subscriptions run on port 8546 on nodes you operate yourself.
## Quick checks
Verify connectivity with `eth_chainId` and `eth_blockNumber`.
```bash eth_chainId theme={null}
curl -s -X POST https://evm-rpc.testnet.lumera.io \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' | jq -r '.result'
```
```bash eth_blockNumber theme={null}
curl -s -X POST https://evm-rpc.testnet.lumera.io \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | jq -r '.result'
```
`eth_chainId` returns `0x494c1a9`. `eth_blockNumber` returns the latest height as a hex number.
## Namespaces
Methods are grouped by namespace prefix. The default node configuration enables `eth`, `net`, `web3`, and `rpc`. Node operators can enable additional namespaces such as `debug` and `txpool` in `app.toml`, so their availability depends on the endpoint you call.
The fee market implements EIP-1559 with a dynamic base fee, so fee estimation behaves the way Ethereum tooling expects.
## OpenRPC discovery
The node ships a machine readable catalog of every JSON-RPC method it supports, in [OpenRPC](https://open-rpc.org/) format. The spec regenerates at build time from the RPC implementation, so it always matches the running node. There are two ways to fetch it.
* Call `rpc_discover` (or the alias `rpc.discover`) on the JSON-RPC endpoint.
* Fetch `/openrpc.json` from the Cosmos REST API port (1317) with a plain GET.
```bash theme={null}
curl -s -X POST https://evm-rpc.testnet.lumera.io \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"rpc_discover","params":[],"id":1}' | jq '.result.info'
```
The catalog covers about 743 methods with parameters, return types, and examples. Browse and execute them interactively in the [OpenRPC playground](/smart-contracts/openrpc-playground).
## Next steps
What the Lumera EVM offers contract developers.
Explore every RPC method from the browser.
Add the Lumera network to MetaMask.
# CascadeService gRPC reference
Source: https://docs.lumera.io/api/grpc/cascade-service
CascadeService provides streaming gRPC RPCs for registering files in Cascade permanent storage and downloading stored data from a Lumera SuperNode.
`CascadeService` is the gRPC interface you use to register files in Lumera's Cascade permanent-storage layer and to retrieve them later. Both operations use gRPC streaming. The `Register` RPC is bidirectional. You stream file chunks to the SuperNode while it streams progress events back. The `Download` RPC streams chunks from the SuperNode to you. The SuperNode handles RaptorQ encoding, P2P distribution, and on-chain finalization. Your client only needs to send the data and act on the event stream.
Two layers reach Cascade. Applications go through [SN-API](/api/sn-api), the HTTP gateway in front of the SuperNode network, which the [JavaScript](/sdk/javascript) and [Rust](/sdk/rust) SDKs use. `CascadeService` is the direct path to a SuperNode on port 4444, used by the [Go SDK](/sdk/go) and operator tooling. Use SN-API for app-facing upload and download.
## Proto definition
```proto theme={null}
service CascadeService {
rpc Register (stream RegisterRequest) returns (stream RegisterResponse);
rpc Download (DownloadRequest) returns (stream DownloadResponse);
}
```
***
## Register
`rpc Register(stream RegisterRequest) returns (stream RegisterResponse)`
Use `Register` to upload a file and anchor it permanently on the Lumera network. The RPC is a bidirectional stream. Your client opens the stream, sends the file in `DataChunk` messages, then sends a final `Metadata` message to identify the pre-created on-chain action. The SuperNode encodes the data with RaptorQ, distributes the encoded symbols across the P2P network, and finalizes the action on-chain. It streams `RegisterResponse` progress events back to you throughout.
### Client stream `RegisterRequest`
Each message in your outgoing stream is one of two types (`oneof`).
A slice of the file's raw bytes. Send as many chunk messages as needed to transfer the complete file. There is no prescribed chunk size. A few hundred kilobytes per message is a reasonable default.
The raw binary content of this chunk.
Sent **once**, after all `DataChunk` messages, to tell the SuperNode which on-chain action this upload corresponds to.
A client-assigned task identifier used for correlating logs and events.
The on-chain Cascade action ID that was created before calling this RPC. The SuperNode reads the fee and ownership details from this action.
### Server stream `RegisterResponse`
A progress milestone. See the event table below.
Human-readable description of the event, useful for logging.
Populated on the `ACTION_FINALIZED` event. It holds the transaction hash of the finalization transaction broadcast to the Lumera chain.
### Registration event sequence
The server emits these events in order during a successful registration.
| Event | Description |
| - | - |
| `ACTION_RETRIEVED` | Action details fetched from the Lumera chain |
| `ACTION_FEE_VERIFIED` | Required fee confirmed as paid |
| `TOP_SUPERNODE_CHECK_PASSED` | This node confirmed as a top-ranked SuperNode |
| `INPUT_ENCODED` | File data encoded with RaptorQ into erasure-coded symbols |
| `RQID_GENERATED` | RaptorQ symbol identifiers generated |
| `ARTEFACTS_STORED` | Encoded symbols distributed and stored in the P2P network |
| `ACTION_FINALIZED` | Finalization transaction submitted on-chain with `tx_hash` set |
### grpcurl example
```shell theme={null}
# Stream file registration (sends metadata only; pipe real file data in production)
grpcurl -plaintext \
-d '{"metadata": {"task_id": "task-001", "action_id": "action-abc"}}' \
localhost:4444 \
lumera.supernode.v1.CascadeService/Register
```
Production clients should use a gRPC library (e.g., the [supernode Go SDK](https://github.com/LumeraProtocol/supernode/tree/master/sdk)) to open a real bidirectional stream and send file data as `DataChunk` messages before sending the `Metadata` message. The `grpcurl` snippet above illustrates the shape of the metadata message only.
***
## Download
`rpc Download(DownloadRequest) returns (stream DownloadResponse)`
Use `Download` to retrieve a file that was previously registered via Cascade. You send a single unary request containing the action ID and a signature proving ownership. The SuperNode streams back progress events followed by the raw file data in chunks.
### Request message `DownloadRequest`
The on-chain Cascade action ID whose data you want to retrieve.
A base64-encoded signature over the `action_id`, produced with the LumeraID key associated with the action's owner address. The SuperNode verifies this signature before serving the data.
### Server stream `DownloadResponse`
Each message in the response stream is one of two types (`oneof`).
Progress milestone emitted during the retrieval phase.
The event milestone. Download emits `ACTION_RETRIEVED`, `METADATA_DECODED`, `NETWORK_RETRIEVE_STARTED`, `ARTEFACTS_DOWNLOADED`, and `DECODE_COMPLETED`, then `SERVE_READY` immediately before the first `chunk`.
Human-readable description of the event.
A slice of the reconstructed file's raw bytes. After all progress events are emitted, the SuperNode streams the complete file as a sequence of `DataChunk` messages.
Raw binary content of this chunk of the recovered file.
### grpcurl example
```shell theme={null}
grpcurl -plaintext \
-d '{"action_id": "action-abc", "signature": "base64-sig..."}' \
localhost:4444 \
lumera.supernode.v1.CascadeService/Download
```
The `signature` field must be produced by signing the `action_id` string with the **LumeraID key** linked to the owner address recorded on the action. Signatures produced with a different key are rejected with an `UNAUTHENTICATED` gRPC status. See the LumeraID documentation for how to generate a compatible signature.
***
## SupernodeEventType reference
The `SupernodeEventType` enum is shared by both `Register` and `Download`. Not every event appears in every flow.
| Value | Name | Flows |
| - | - | - |
| `0` | `UNKNOWN` | none |
| `1` | `ACTION_RETRIEVED` | Register, Download |
| `2` | `ACTION_FEE_VERIFIED` | Register |
| `3` | `TOP_SUPERNODE_CHECK_PASSED` | Register |
| `4` | `METADATA_DECODED` | Download |
| `5` | `DATA_HASH_VERIFIED` | Download |
| `6` | `INPUT_ENCODED` | Register |
| `7` | `SIGNATURE_VERIFIED` | Download |
| `8` | `RQID_GENERATED` | Register |
| `9` | `RQID_VERIFIED` | Download |
| `10` | `FINALIZE_SIMULATED` | Register |
| `11` | `ARTEFACTS_STORED` | Register |
| `12` | `ACTION_FINALIZED` | Register |
| `13` | `ARTEFACTS_DOWNLOADED` | Download |
| `14` | `FINALIZE_SIMULATION_FAILED` | Register |
| `15` | `NETWORK_RETRIEVE_STARTED` | Download |
| `16` | `DECODE_COMPLETED` | Download |
| `17` | `SERVE_READY` | Download |
***
## HTTP alternative
`CascadeService` has no HTTP gateway. The SuperNode's own gateway on port 8002 exposes `SupernodeService` only, documented in [`docs/gateway.md`](https://github.com/LumeraProtocol/supernode/blob/master/docs/gateway.md). To move Cascade files over HTTP, use [SN-API](/api/sn-api).
The HTTP gateway is useful for quick testing and browser-based tooling, but for production file transfers you should use the native gRPC streaming interface or the Go SDK. Streaming large files over the HTTP gateway may be subject to buffering and timeout constraints that the gRPC path avoids.
# SupernodeService gRPC reference
Source: https://docs.lumera.io/api/grpc/supernode-service
Query system status and resource metrics for a Lumera SuperNode over gRPC, including CPU, memory, storage, P2P, and DHT data.
`SupernodeService` is a gRPC service exposed by every Lumera SuperNode. It gives you a real-time snapshot of the node's health. The snapshot covers software version, uptime, CPU and memory load, and connected peers. When you opt in, it also includes detailed DHT diagnostics. You can use this service to build monitoring dashboards, alerting pipelines, or operational tooling without any on-chain queries.
## Connection
SuperNodes listen on **port 4444** by default (configurable via `supernode.port` in `config.yml`). You can verify connectivity with `grpcurl`.
```shell theme={null}
grpcurl -plaintext localhost:4444 lumera.supernode.v1.SupernodeService/GetStatus
```
If TLS is enabled on the SuperNode, replace `-plaintext` with the appropriate `-cacert` / `-cert` / `-key` flags. Most production deployments terminate TLS at a reverse proxy and expose plaintext gRPC internally.
***
## Proto definition
```proto theme={null}
service SupernodeService {
rpc GetStatus(StatusRequest) returns (StatusResponse);
}
```
***
## GetStatus
Returns a point-in-time status snapshot of the SuperNode. The request lets you toggle expensive P2P diagnostics on or off, so routine polling stays lightweight.
### Request message `StatusRequest`
When `true`, the response includes the full `p2p_metrics` object containing DHT store/retrieve history, handle counters, ban list, database stats, and disk usage. Set this to `false` (or omit it) for routine health polling. The `peers_count` field is always fast and is returned regardless of this flag. Heavier diagnostics are served from a cached last-known-good snapshot.
### Response message `StatusResponse`
The SuperNode software version string (e.g., `"v2.5.2"`).
Seconds elapsed since the SuperNode process started.
Hardware resource utilisation at the time of the call.
Current CPU utilisation across all cores, expressed as a percentage (0 to 100).
Number of logical CPU cores available to the process.
Total physical memory installed, in GB.
Memory currently in use, in GB.
Memory available to new allocations, in GB.
Memory utilisation as a percentage (0 to 100).
Storage volumes monitored by the SuperNode. Currently the array contains one entry. That entry is the filesystem that hosts the configured base directory (`--basedir`).
Filesystem mount path being reported (e.g., `"/"`).
Total capacity of the volume in bytes.
Bytes currently consumed on the volume.
Bytes available for new writes.
Volume utilisation as a percentage (0 to 100).
Human-readable summary of the node's hardware profile (e.g., `"8 cores / 32GB RAM"`).
The service names this SuperNode is registered to provide. This is typically `["cascade", "sense"]` or a subset of it.
P2P network connectivity information.
Number of peers currently connected in the P2P network. Always present, even when `include_p2p_metrics` is `false`.
List of connected peer addresses in `"ID@IP:Port"` format.
This node's rank in the top-SuperNodes list as computed by the chain. Returns `0` if the node is not currently in the top list.
The publicly-advertised IP address and service port of this SuperNode (e.g., `"203.0.113.45:4444"`).
Detailed P2P and DHT diagnostics. **Only present when `include_p2p_metrics` is `true`.**
Recent DHT operation history.
Recent DHT store-operation data points. Each entry includes `time_unix` (epoch seconds), `requests` (total node RPCs attempted), `successful` (successful node RPCs), and `success_rate` (percentage 0 to 100).
Recent batch-retrieval data points. Each entry includes `time_unix`, `keys` (keys requested), `required`, `found_local`, `found_network`, and `duration_ms`.
Number of times a hot-path request was skipped because the target was banned.
Number of times a ban counter was incremented on the hot path.
Per-handler RPC counters. Each `HandleCounters` value contains `total`, `success`, `failure`, and `timeout` int64 fields.
Connection pool statistics keyed by metric name.
Peers currently banned by this node. Each entry contains `id`, `ip`, `port` (uint32), `count` (ban increment count), `created_at_unix`, and `age_seconds`.
P2P key-value database stats, containing `p2p_db_size_mb` (double) and `p2p_db_records_count` (int64).
Disk usage for the P2P data directory, reported as `all_mb`, `used_mb`, and `free_mb` (all double).
***
## Example response
```json theme={null}
{
"version": "v2.5.2",
"uptime_seconds": 86400,
"resources": {
"cpu": { "usage_percent": 12.5, "cores": 8 },
"memory": {
"total_gb": 64,
"used_gb": 12.3,
"available_gb": 51.7,
"usage_percent": 19.2
},
"storage_volumes": [
{
"path": "/",
"total_bytes": 2000398934016,
"used_bytes": 480374956032,
"available_bytes": 1520023977984,
"usage_percent": 24.0
}
],
"hardware_summary": "8-core CPU, 64GB RAM, 2TB storage"
},
"registered_services": ["cascade", "sense"],
"network": {
"peers_count": 15,
"peer_addresses": [
"12D3KooWExample1@203.0.113.10:4445",
"12D3KooWExample2@203.0.113.20:4445"
]
},
"rank": 3,
"ip_address": "203.0.113.45:4444"
}
```
***
## Full example with P2P metrics
Pass `include_p2p_metrics: true` to receive DHT diagnostics alongside the standard fields.
```shell theme={null}
grpcurl -plaintext \
-d '{"include_p2p_metrics": true}' \
localhost:4444 \
lumera.supernode.v1.SupernodeService/GetStatus
```
# Deploy LumeScope with Docker
Source: https://docs.lumera.io/api/lumescope
Run your own LumeScope instance with Docker to aggregate Lumera action metadata, SuperNode metrics, and network stats.
LumeScope is an open-source, self-hosted API aggregator that indexes Lumera chain data and serves it through a clean REST interface. It is written in Go using only the standard library `net/http` package, with no third-party web frameworks. It bundles an embedded PostgreSQL 14 database and runs as a single Docker container. No separate data store is required. When you deploy your own instance you control the sync cadence and keep all indexed data inside your infrastructure. You can also build analytics pipelines on top without depending on a third-party service.
## When to deploy your own instance
The public mainnet and testnet endpoints (`https://lumescope.lumera.io` and `https://lumescope.testnet.lumera.io`) are suitable for most integrations. Consider running your own LumeScope instance when you need any of the following.
* **Tune sync intervals.** Adjust how frequently actions, validators, SuperNodes, and probes refresh.
* **Ensure data sovereignty.** Keep all network data inside your own infrastructure.
* **Build analytics.** Connect your own dashboards or data pipelines directly to the embedded PostgreSQL database.
* **Run with high availability.** Run multiple replicas backed by a shared external PostgreSQL for failover.
***
## Quickstart
Get LumeScope running locally in under a minute with Docker.
```shell theme={null}
# Connect to Mainnet
docker run -d \
-p 18080:18080 \
-e LUMERA_API_BASE=https://lcd.lumera.io \
--name lumescope \
ghcr.io/lumeraprotocol/lumescope:latest
```
For testnet, replace the `LUMERA_API_BASE` value with `https://lcd.testnet.lumera.io`.
```shell theme={null}
# Liveness check. Returns 200 immediately if the process is running
curl -i http://localhost:18080/healthz
# Readiness check. Returns 200 once the first sync has completed
curl -i http://localhost:18080/readyz
# Fetch the first three actions
curl -s 'http://localhost:18080/v1/actions?limit=3' | jq .
```
Open `http://localhost:18080/docs` in your browser to browse and test every endpoint using the embedded Swagger UI. The machine-readable OpenAPI 3.0 spec is available at `http://localhost:18080/openapi.json`.
***
## Configuration
LumeScope reads all configuration from environment variables at startup. Copy `.env.example` from the repository and customise it, or pass variables directly with `-e` flags.
### Key environment variables
| Variable | Required | Default | Description |
| - | - | - | - |
| `LUMERA_API_BASE` | **Yes** | `http://localhost:1317` | Lumera REST (LCD) endpoint URL |
| `PORT` | No | `18080` | HTTP server listen port |
| `CORS_ALLOW_ORIGINS` | No | `*` | Comma-separated allowed CORS origins |
| `VALIDATORS_SYNC_INTERVAL` | No | `5m` | Validators sync frequency |
| `SUPERNODES_SYNC_INTERVAL` | No | `2m` | SuperNodes sync frequency |
| `ACTIONS_SYNC_INTERVAL` | No | `30s` | Actions sync frequency |
| `PROBE_INTERVAL` | No | `1m` | SuperNode probe frequency |
LumeScope reads more environment variables than shown here, including database connection settings, HTTP timeouts, and TCP dial timeouts. Its README lists them all and ships with each LumeScope release in [LumeraProtocol/releases](https://github.com/LumeraProtocol/releases).
***
## Production deployment
### Persistent data volume
The embedded PostgreSQL 14 database stores its data files at `/var/lib/postgresql/data` inside the container. Mount a named volume to retain indexed data across container restarts and upgrades.
```shell theme={null}
docker run -d \
-p 18080:18080 \
-e LUMERA_API_BASE=https://lcd.lumera.io \
-e POSTGRES_PASSWORD=your-secure-password \
-v lumescope_data:/var/lib/postgresql/data \
--name lumescope \
ghcr.io/lumeraprotocol/lumescope:latest
```
Always set `POSTGRES_PASSWORD` to a strong value in production. The default password (`postgres`) is suitable only for local development.
### External PostgreSQL database
If you prefer to manage the database separately, point LumeScope at an external PostgreSQL 13+ instance.
```shell theme={null}
docker run -d \
-p 18080:18080 \
-e LUMERA_API_BASE=https://lcd.lumera.io \
-e DB_DSN="postgres://user:pass@your-pg-host:5432/lumescope?sslmode=require" \
--name lumescope \
ghcr.io/lumeraprotocol/lumescope:latest
```
LumeScope automatically creates all required tables and indexes on first startup.
### High-availability setup
Run multiple replicas pointing to a shared external PostgreSQL.
```shell theme={null}
# Instance 1
docker run -d -p 18081:18080 \
-e LUMERA_API_BASE=https://lcd.lumera.io \
-e DB_DSN="postgres://user:pass@pg-host:5432/lumescope?sslmode=require" \
--name lumescope-1 \
ghcr.io/lumeraprotocol/lumescope:latest
# Instance 2
docker run -d -p 18082:18080 \
-e LUMERA_API_BASE=https://lcd.lumera.io \
-e DB_DSN="postgres://user:pass@pg-host:5432/lumescope?sslmode=require" \
--name lumescope-2 \
ghcr.io/lumeraprotocol/lumescope:latest
```
Place nginx, HAProxy, or a cloud load balancer in front of both instances.
***
## Available endpoints
Once LumeScope is running, the following paths are available on your host.
| Endpoint | Description |
| - | - |
| `GET /healthz` | Liveness probe, always `200` while the process is alive |
| `GET /readyz` | Readiness probe, `200` once the database and first sync are ready |
| `GET /v1/actions` | List decoded Cascade and Sense actions |
| `GET /v1/supernodes/metrics` | SuperNode hardware metrics and probe status |
| `GET /openapi.json` | OpenAPI 3.0 specification |
| `GET /docs` | Interactive Swagger UI |
| `GET /metrics` | Prometheus metrics endpoint (currently stub data) |
***
## Troubleshooting
**Symptom.** The container starts but action and SuperNode data never populates. Container logs show connection errors.
**Steps.**
1. Verify that `LUMERA_API_BASE` is set to the correct LCD URL for your target network.
```shell theme={null}
# Mainnet
curl https://lcd.lumera.io/cosmos/base/tendermint/v1beta1/node_info
# Testnet
curl https://lcd.testnet.lumera.io/cosmos/base/tendermint/v1beta1/node_info
```
2. Check connectivity from inside the container.
```shell theme={null}
docker exec lumescope wget -qO- https://lcd.lumera.io/healthz
```
3. If you are pointing at a local `lumerad` node, confirm that port `1317` is exposed and the REST API is enabled in `app.toml` (`api.enable = true`).
**Symptom.** The browser blocks your frontend application with a CORS policy error. Direct `curl` calls still succeed.
**Steps.**
Set `CORS_ALLOW_ORIGINS` to your frontend's exact origin.
```shell theme={null}
docker run -d \
-p 18080:18080 \
-e LUMERA_API_BASE=https://lcd.lumera.io \
-e CORS_ALLOW_ORIGINS="https://your-app.com" \
--name lumescope \
ghcr.io/lumeraprotocol/lumescope:latest
```
Use `*` only during local development. Wildcard origins are not recommended in production because they allow any website to call your API.
**Symptom.** Several SuperNodes appear in `GET /v1/supernodes/unavailable` and their `failed_probe_counter` is incrementing.
**Cause.** LumeScope probes each SuperNode on ports 4444, 4445, and 8002. Some operators firewall these ports or disable their status API endpoint. This causes the probe to fail.
**Steps.**
1. This is **expected behavior** for a subset of SuperNodes. Probe failures do not indicate a LumeScope misconfiguration.
2. Query `GET /v1/supernodes/unavailable` to see which specific nodes are affected and how long they have been unreachable.
3. If a node you operate is listed, verify that its status API port is publicly accessible and that no firewall rule is blocking LumeScope's outbound TCP dial.
4. Adjust `PROBE_INTERVAL` if you want to probe less (or more) frequently to reduce noise.
**Symptom.** The container exits shortly after starting, or queries return errors. Logs mention a database connection failure.
**Steps.**
1. For the embedded Postgres, inspect the container logs for PostgreSQL startup errors.
```shell theme={null}
docker logs lumescope
```
2. For an external database, verify your `DB_DSN` connection string.
```shell theme={null}
psql "postgres://user:pass@host:5432/lumescope?sslmode=disable" -c "\dt"
```
3. Ensure the database user has `CREATE TABLE` and `CREATE INDEX` privileges. LumeScope sets up the required schema automatically on first startup.
# Lumera REST API overview
Source: https://docs.lumera.io/api/overview
Learn the base URLs, authentication model, and response format for the LumeScope, LCD, and SN-API REST surfaces.
Lumera Protocol gives you three complementary REST API surfaces. The **Lumera LCD** endpoint speaks directly to the chain. Use it to query state and broadcast transactions with no middleware in the way. **LumeScope** sits in front of the network as a neutral, read-only aggregator. It decodes Action metadata for Cascade and Sense, tracks SuperNode hardware metrics, and assembles network-wide statistics. Your application does not have to reconstruct that data from raw chain state. **SN-API** is the HTTP gateway in front of the SuperNode network and carries Cascade file transfers. All three return JSON. LCD and LumeScope need no authentication for reads; SN-API uploads and private downloads require an ADR-036 signature.
## API surfaces
**Lumera LCD (native chain API)** is the standard Cosmos SDK REST interface served by any `lumerad` node. Use it when you need to submit transactions, query on-chain parameters, or access data that LumeScope does not index.
**LumeScope API** is a purpose-built aggregator that decodes, stores, and serves Lumera-specific data in developer-friendly shapes. It is the recommended starting point for application developers who want paginated Action lists, decoded metadata, and SuperNode health without writing their own chain-indexing logic.
**SN-API** is the HTTP gateway that fronts the SuperNode network for Cascade uploads and downloads. The JavaScript and Rust SDKs call it for you. Call it directly when you already hold an on-chain action ID and an upload signature. The direct SuperNode gRPC service is a separate, lower-level path documented in the [CascadeService reference](/api/grpc/cascade-service).
## Base URLs
| Network | LumeScope Base | LCD Base | SN-API Base |
| - | - | - | - |
| Mainnet | `https://lumescope.lumera.io` | `https://lcd.lumera.io` | `https://snapi.lumera.io` |
| Testnet | `https://lumescope.testnet.lumera.io` | `https://lcd.testnet.lumera.io` | `https://snapi.testnet.lumera.io` |
All examples in this documentation target mainnet. Replace the base URL with the testnet equivalent when developing against **lumera-testnet-2**.
## Authentication
All LumeScope endpoints are **public**. No API key or bearer token is required. You can call any endpoint directly from a browser, curl, or your application's HTTP client.
## Response format
Every LumeScope response is JSON. Successful responses use HTTP `200` status codes. Error responses include a machine-readable `error` field and an appropriate 4xx or 5xx status code.
## OpenAPI specification and interactive docs
LumeScope embeds a full OpenAPI 3.0 specification and an interactive Swagger UI. Use them to explore and test every endpoint.
| Resource | URL |
| - | - |
| OpenAPI JSON spec | `{base}/openapi.json` |
| Swagger UI | `{base}/docs` |
For example, on mainnet browse to `https://lumescope.lumera.io/docs` to open the interactive documentation.
## Explore the API
Query Cascade and Sense actions with decoded metadata, transaction lifecycle tracking, and aggregated statistics.
Fetch SuperNode hardware metrics, payment statistics, version compatibility matrix, and availability status.
Upload and download Cascade files through the SuperNode HTTP gateway.
## Self-hosting LumeScope
You can run LumeScope yourself with a single Docker command. A private deployment gives you custom sync intervals, data sovereignty, and your own analytics pipeline. See the [LumeScope deployment guide](/api/lumescope) for instructions.
# SN-API REST gateway reference
Source: https://docs.lumera.io/api/sn-api
Upload and download Cascade files through the REST gateway that fronts the SuperNode network.
SN-API is the REST gateway in front of SuperNodes. It handles Cascade file uploads and downloads over plain HTTPS. The [JavaScript SDK](/sdk/javascript) and the [Rust SDK](/sdk/rust) route all file transfers through it. The [Go SDK](/sdk/go) skips the gateway and talks gRPC directly to SuperNodes on port 4444.
Call SN-API directly when you build in a language without an official SDK or need full control over the HTTP layer. Register the action on chain first. The gateway only moves bytes for actions that already exist.
## Base URLs
| Network | Base URL |
| - | - |
| Mainnet | `https://snapi.lumera.io` |
| Testnet | `https://snapi.testnet.lumera.io` |
The JavaScript SDK picks the endpoint from its `preset` configuration. The Rust SDK reads it from the `SNAPI_BASE` environment variable.
## Authentication
Uploads and private downloads require an ADR-036 signature. ADR-036 is the Cosmos standard for signing arbitrary data off chain. The SDKs produce these signatures for you. When calling the API directly, follow two steps.
1. Sign the relevant data with ADR-036 `signArbitrary`. Uploads sign the data hash. Downloads sign the action ID.
2. Include the Base64 encoded signature in the request body.
***
## POST /api/v1/actions/cascade
Starts a Cascade upload. Send the file as `multipart/form-data` after the on-chain registration.
### Form fields
Action ID returned by the on-chain registration transaction.
Base64 encoded ADR-036 auth signature.
The file data.
### Example response
```json theme={null}
{
"task_id": "uuid-task-id"
}
```
Upload task identifier. Use it with the task endpoints below.
### Status codes
| Code | Meaning |
| - | - |
| 202 | Accepted. The upload task was created and the response carries the `task_id`. |
| 400 | Invalid request. Missing fields or bad signature. |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Action not found. Retry after a short delay. |
| 409 | Conflict |
| 500 | Server error |
SuperNodes need a moment to index a freshly registered action. The SDKs retry this endpoint up to 5 times with 3 second delays. Apply the same retry policy when you call it directly.
***
## GET /api/v1/actions/cascade/tasks/\{task\_id}
Returns the stored request metadata for an upload task. This endpoint describes what was requested, not how far it has progressed. For task state use the status stream or the history endpoint below.
### Path parameters
Task ID returned by the upload endpoint.
### Example response
```json theme={null}
{
"task_id": "uuid",
"action_id": "232794",
"file_name": "example.png",
"file_path": "/data/uploads/example.png",
"file_size_kbs": 11,
"signature": "base64-encoded ADR-036 signature",
"created_at": "2026-01-01T00:00:00Z"
}
```
### Response fields
Upload task identifier.
The on-chain action this upload belongs to.
Name of the uploaded file.
Path the SuperNode stored the incoming file at.
File size in kilobytes.
The ADR-036 signature submitted with the upload.
RFC 3339 timestamp of task creation.
***
## GET /api/v1/actions/cascade/tasks/\{task\_id}/status
Streams progress updates for an upload task as Server-Sent Events (SSE), a one-way HTTP stream of messages. Send `Accept: text/event-stream`.
Task ID returned by the upload endpoint.
Each event carries a JSON payload on a `data:` line with the fields `id`, `task_id`, `status`, `data`, and `created_at`.
***
## GET /api/v1/actions/cascade/tasks/\{task\_id}/history
Returns the status history for an upload task as JSON. Use this when you want to poll rather than hold a stream open.
Task ID returned by the upload endpoint.
***
## POST /api/v1/actions/cascade/\{action\_id}/downloads
Requests a download task for a stored file. Private files need an ADR-036 signature. Files stored with `isPublic: true` can be requested without one.
### Path parameters
The on-chain action ID of the stored file.
### Query parameters
Set to `true` for a file that was stored with `isPublic: true`. The signature then becomes optional.
### Request body
Base64 encoded ADR-036 signature of the action ID. Required unless you pass `public=true`.
### Example request for a private file
```json theme={null}
{
"signature": "base64-encoded ADR-036 signature of the action_id"
}
```
### Example request for a public file
```bash theme={null}
curl -X POST "https://snapi.lumera.io/api/v1/actions/cascade/{action_id}/downloads?public=true"
```
Requesting a private file without a signature returns `400` with the message `signature is required`.
### Example response
```json theme={null}
{
"task_id": "uuid-download-task-id"
}
```
Download task identifier. Use it with the status and file endpoints below.
### Status codes
| Code | Meaning |
| - | - |
| 202 | Accepted. The download task was created and the response carries the `task_id`. |
| 400 | Invalid request |
| 404 | Action not found |
| 500 | Server error |
As with the upload endpoint, this creates a task and returns `202 Accepted`. The file itself comes from the file endpoint below.
***
## GET /api/v1/downloads/cascade/\{task\_id}/status
Streams progress updates as Server-Sent Events (SSE), a one-way HTTP stream of messages. Send `Accept: text/event-stream`.
Download task ID returned by the download request endpoint.
Each event carries the same fields as a history entry, so `id`, `task_id`, `status`, `data`, and `created_at`. There is no percentage field. Progress is expressed as a sequence of named states, and `data` carries any detail that state has.
### Example stream
```text theme={null}
data: {"id":1,"task_id":"7ca7b6c9","status":"sdk:started","data":{},"created_at":"2026-01-01T00:00:00Z"}
data: {"id":2,"task_id":"7ca7b6c9","status":"sdk:supernodes_found","data":{"count":7,"total":10},"created_at":"2026-01-01T00:00:03Z"}
data: {"id":3,"task_id":"7ca7b6c9","status":"sdk:completed","data":{},"created_at":"2026-01-01T00:00:41Z"}
```
States are namespaced by who reported them. `sdk:` states come from the gateway's client, and `supernode:` states come from the SuperNode serving the request. The terminal states are `sdk:completed` and `sdk:failed`.
In browsers, consume the stream with `EventSource`.
```ts theme={null}
const es = new EventSource(
`https://snapi.testnet.lumera.io/api/v1/downloads/cascade/${taskId}/status`
);
es.onmessage = (event) => {
const update = JSON.parse(event.data);
console.log(update.status, update.data);
if (update.status === "sdk:completed" || update.status === "sdk:failed") {
es.close();
}
};
```
To poll for status as JSON instead of holding this stream open, use the history endpoint below.
***
## GET /api/v1/downloads/cascade/\{task\_id}/history
Returns the status history for a download task as JSON. This is the polling counterpart to the stream above.
Download task ID returned by the download request endpoint.
***
## GET /api/v1/downloads/cascade/\{task\_id}/file
Streams the file bytes once the download task completes. The response arrives with `Content-Type: application/octet-stream`.
Download task ID returned by the download request endpoint.
Consume the response as a `ReadableStream` to keep memory use flat on large files.
***
## Version fallback
The SDKs try versioned paths first (`/api/v1/...`) and fall back to legacy paths (`/api/...`) on a 404. This keeps them compatible across SN-API versions. Prefer the `/api/v1/` prefix when calling the API directly.
## Errors
Error bodies carry a message and a machine readable code.
```json theme={null}
{
"error": "action not found",
"code": "NOT_FOUND"
}
```
| Code | Cause |
| - | - |
| `NOT_FOUND` | The action does not exist or is not indexed yet |
| `UNAUTHORIZED` | The ADR-036 signature is invalid |
| `SERVICE_UNAVAILABLE` | No SuperNode is available to serve the request |
## Rate limits
The public SN-API endpoints may rate limit during high traffic. For high throughput production workloads, run your own SuperNode. The [SuperNode overview](/supernodes/overview) explains the requirements.
## Next steps
What happens between registration and DONE.
How SuperNodes reassemble your file from symbols.
Retry strategies for uploads and downloads.
# LumeScope SuperNodes API reference
Source: https://docs.lumera.io/api/supernodes
Query SuperNode hardware metrics, payment statistics, version compatibility matrix, and availability status using the LumeScope read-only REST API.
The LumeScope SuperNodes API aggregates hardware telemetry, payment statistics, availability probes, and version compatibility data for every SuperNode registered on the Lumera network. LumeScope probes each SuperNode on a configurable interval and stores the results. Your application can query current status, rank individual nodes by resources, and identify nodes that are temporarily unreachable. You never need direct access to an operator's machine.
All SuperNodes API endpoints are read-only and require no authentication. The base URL for mainnet is `https://lumescope.lumera.io`. Substitute `https://lumescope.testnet.lumera.io` for testnet.
***
## GET /v1/supernodes/metrics
Returns hardware and network metrics for all SuperNodes. Use query parameters to filter by status, version, or failure threshold, and to paginate large result sets.
### Query parameters
Filter by probe status. Common values are `available` and `unavailable`.
Filter by the node's on-chain validator state.
Filter to nodes running a specific software version string.
Return only nodes whose consecutive probe failure count is at or above this threshold. Useful for identifying persistently offline nodes.
Maximum number of SuperNodes to return per page.
Opaque pagination cursor returned by the previous response. Pass this value to retrieve the next page.
### Example request
```shell All supernodes theme={null}
curl https://lumescope.lumera.io/v1/supernodes/metrics
```
```shell Available nodes only theme={null}
curl "https://lumescope.lumera.io/v1/supernodes/metrics?status=available&limit=10"
```
### Example response
```json theme={null}
{
"supernodes": [
{
"id": "lumera15abc...",
"ip": "192.0.2.10",
"status": "available",
"rank": 1,
"version": "1.2.0",
"registered_services": ["cascade", "sense"],
"peer_count": 42,
"cpu_cores": 16,
"cpu_usage_percent": 23.4,
"memory_total_gb": 64,
"memory_used_gb": 18.2,
"storage_total_gb": 2000,
"storage_used_gb": 450,
"failed_probe_counter": 0
}
],
"cursor": "eyJpZCI6Imx1bWVyYTE1YWJjIn0="
}
```
### Response fields
Array of SuperNode metric objects.
Bech32 operator address of the SuperNode.
Public IP address of the node.
The probe status, either `available` or `unavailable`.
Relative ranking among all SuperNodes (lower is better).
Software version string reported by the node.
List of services the node has registered, e.g. `cascade`, `sense`.
Number of active peer connections at the time of the last probe.
Total CPU core count on the node.
CPU utilization percentage at the time of the last probe.
Total installed RAM in gigabytes.
RAM in use at the time of the last probe.
Total storage capacity in gigabytes.
Storage consumed at the time of the last probe.
Number of consecutive failed probes since the last successful contact.
Pagination cursor. Pass this as the `cursor` query parameter to retrieve the next page. Absent when there are no more results.
***
## GET /v1/supernodes/\{id}/metrics
Returns the current hardware and network metrics for a single SuperNode identified by its operator address.
### Path parameters
The Bech32 operator address of the SuperNode (e.g. `lumera15abc...`).
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/supernodes/lumera15abc.../metrics
```
### Example response
```json theme={null}
{
"id": "lumera15abc...",
"ip": "192.0.2.10",
"status": "available",
"rank": 1,
"version": "1.2.0",
"registered_services": ["cascade", "sense"],
"peer_count": 42,
"cpu_cores": 16,
"cpu_usage_percent": 23.4,
"memory_total_gb": 64,
"memory_used_gb": 18.2,
"storage_total_gb": 2000,
"storage_used_gb": 450,
"failed_probe_counter": 0
}
```
The response object uses the same field schema as the individual entries in `GET /v1/supernodes/metrics`.
***
## GET /v1/supernodes/\{id}/paymentInfo
Returns payment statistics broken down by denomination for a specific SuperNode. Use this endpoint to audit reward distribution or inspect historical payment totals.
### Path parameters
The Bech32 operator address of the SuperNode.
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/supernodes/lumera15abc.../paymentInfo
```
### Example response
```json theme={null}
{
"id": "lumera15abc...",
"payments": [
{
"denom": "ulume",
"total_amount": "1500000000",
"payment_count": 3200
}
]
}
```
### Response fields
Bech32 operator address of the SuperNode.
List of payment totals, one entry per denomination.
Token denomination (e.g. `ulume`).
Cumulative payment amount in the smallest unit of the denomination.
Number of individual payment transactions included in this total.
***
## GET /v1/supernodes/stats
Returns aggregated hardware statistics across all SuperNodes. You get network-wide totals and averages for CPU, memory, and storage. Use this endpoint for capacity-planning dashboards and fleet health snapshots.
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/supernodes/stats
```
### Example response
```json theme={null}
{
"total_supernodes": 120,
"available_count": 115,
"unavailable_count": 5,
"avg_cpu_usage_percent": 21.8,
"total_storage_tb": 240,
"total_memory_gb": 7680
}
```
### Response fields
Total number of SuperNodes tracked by LumeScope.
Number of SuperNodes with a passing probe status.
Number of SuperNodes that failed their most recent probe.
Fleet-wide average CPU utilization percentage.
Combined storage capacity across all available SuperNodes, in terabytes.
Combined RAM across all available SuperNodes, in gigabytes.
***
## GET /v1/supernodes/action-stats
Returns per-SuperNode action statistics. The response shows how many Cascade and Sense actions each node has participated in. Use this endpoint to identify the most active nodes or detect imbalanced workload distribution.
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/supernodes/action-stats
```
### Example response
```json theme={null}
{
"supernodes": [
{
"id": "lumera15abc...",
"cascade_count": 420,
"sense_count": 180,
"total_count": 600
}
]
}
```
### Response fields
Array of per-SuperNode action count objects.
Bech32 operator address of the SuperNode.
Number of Cascade actions this node has been involved in.
Number of Sense actions this node has been involved in.
Combined Cascade and Sense action count.
***
## GET /v1/supernodes/unavailable
Returns the list of SuperNodes whose most recent probe reported an unavailable status. This endpoint is the quickest way to triage connectivity or operator issues without filtering the full metrics list.
### Query parameters
Filter by on-chain validator state, e.g. `active`, `jailed`.
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/supernodes/unavailable
```
### Example response
```json theme={null}
{
"supernodes": [
{
"id": "lumera1xyz...",
"ip": "192.0.2.99",
"failed_probe_counter": 12,
"last_seen_at": "2024-01-14T08:00:00Z"
}
]
}
```
### Response fields
Array of unavailable SuperNode objects.
Bech32 operator address.
Public IP address of the node.
Consecutive probe failures since the node was last reachable.
RFC 3339 timestamp of the last successful probe.
***
## POST /v1/supernodes/sync
Triggers an immediate, out-of-schedule sync and probe of all SuperNodes. Under normal operation LumeScope syncs automatically on the configured `SUPERNODES_SYNC_INTERVAL`. Use this endpoint when you need fresh data immediately after an operator change or network event.
Manual sync is only available when the LumeScope instance is configured to allow it. On the public mainnet and testnet endpoints this endpoint may return `403 Forbidden`.
### Example request
```shell theme={null}
curl -X POST https://lumescope.lumera.io/v1/supernodes/sync
```
### Example response
```json theme={null}
{
"status": "sync triggered",
"triggered_at": "2024-01-15T11:00:00Z"
}
```
### Response fields
Confirmation message indicating the sync was accepted.
RFC 3339 timestamp at which the sync was initiated.
***
## GET /v1/version/matrix
Returns the version compatibility matrix for SuperNode software, implementing a partial view of the Lumera Enhancement Proposal 2 (LEP2) versioning scheme. Use this endpoint to determine which node versions are compatible with the current network state.
### Example request
```shell theme={null}
curl https://lumescope.lumera.io/v1/version/matrix
```
### Example response
```json theme={null}
{
"matrix": [
{
"node_version": "1.2.0",
"compatible_versions": ["1.1.0", "1.2.0"],
"deprecated": false
},
{
"node_version": "1.1.0",
"compatible_versions": ["1.1.0"],
"deprecated": true
}
],
"current_recommended": "1.2.0"
}
```
### Response fields
Array of version compatibility entries.
The SuperNode software version this entry describes.
List of other software versions that this version can inter-operate with.
`true` if this version is end-of-life and operators should upgrade.
The latest recommended SuperNode software version according to the compatibility matrix.
***
Poll `GET /v1/supernodes/unavailable` on a schedule and alert your team when `failed_probe_counter` exceeds a threshold. This gives you early warning of persistent connectivity issues before they affect action processing.
# Protocol architecture
Source: https://docs.lumera.io/architecture
How Lumera's layered architecture coordinates consensus, protocol services, and decentralized execution.
Lumera separates consensus, protocol logic, decentralized services, and developer interfaces into distinct architectural layers. The blockchain serves as the protocol's control plane, coordinating requests, governance, settlement, and cryptographic proofs, while economically bonded SuperNodes execute decentralized intelligent services. Together, these layers allow protocol services to scale independently while preserving the security and trust guarantees of blockchain consensus.
## Protocol Foundation
The protocol foundation provides consensus, accounts, governance, interoperability, and multiple smart contract execution environments. Together, these components provide the shared foundation upon which Lumera's protocol services are built.
| Component | Purpose |
| - | - |
| **Cosmos SDK** | Core blockchain framework providing accounts, staking, governance, transactions, gRPC, and REST APIs shipped as a single `lumerad` binary. |
| **CometBFT** | Byzantine Fault Tolerant consensus engine providing fast deterministic finality. |
| **CosmWasm** | Deterministic Rust smart contracts executed within a WebAssembly sandbox and gas metering. |
| **Cosmos EVM** | Native Solidity execution with Ethereum-compatible tooling and accounts. [Find out how →](/smart-contracts/overview) |
| **IBC** | Native interoperability with other Cosmos chains through token transfers, messaging, and Interchain Accounts. [Learn more →](/cascade/concepts/interchain-accounts) |
## Validator–SuperNode architecture
Lumera separates consensus from decentralized service execution through two complementary node types. This architectural separation allows the blockchain to establish shared truth while specialized infrastructure performs the protocol services applications consume.
**Validators** secure the network through Delegated Proof of Stake, proposing and validating blocks with CometBFT, maintaining consensus, and participating in on-chain governance. Together, they provide the security guarantees upon which every protocol service depends. [Run a validator →](/validators/overview)
**SuperNodes** are specialized network nodes operated alongside validators. They execute decentralized intelligent services—including permanent storage, verification, retrieval, and decentralized execution—while remaining economically bonded to the validator they serve. Because protocol services execute outside of consensus, Lumera can scale sophisticated decentralized services without increasing the cost or complexity of block production. [Learn more about SuperNodes →](/supernodes/overview)
The blockchain acts as the protocol's **control plane**, recording requests, commitments, payments, governance, and cryptographic proofs. SuperNodes form the **execution and data plane**, performing work directly for applications and returning verifiable evidence of completion. Data moves directly between clients and SuperNodes without passing through consensus, allowing the network to remain both scalable and trust-minimized.
## Protocol Modules
Lumera's protocol logic is implemented through a set of custom Cosmos SDK modules that coordinate protocol services, decentralized identity, SuperNode operations, and network state. Support modules introduced with the Cosmos EVM extend interoperability while preserving the same architectural model.
The EVM upgrade has added support modules (`x/vm`, `x/feemarket`, `x/precisebank`, `x/erc20`, `x/evmigration`) documented [here](/upgrades/evm-upgrade).
| Module | What it does |
| - | - |
| `x/action` | Coordinates the lifecycle of protocol Actions from registration through settlement |
| `x/supernode` | Registers SuperNodes, maintains the active service set, tracks performance, and distributes Proof-of-Service rewards. |
| `x/lumeraid` | Anchors decentralized identities to Lumera addresses |
| `x/claim` | Handles legacy token claims and migration |
### The action lifecycle
Every decentralized intelligent service is executed as one or more protocol Actions—on-chain records that move through a common lifecycle from registration to settlement.
A client submits the registration transaction. The service fee is locked in escrow and the action gets a unique ID. The state is `PENDING`.
Top ranked SuperNodes pick up the action, receive the file over gRPC, and do the work. The state is `PROCESSING`.
When the work completes and results are committed, the state becomes `DONE`. An action still unfinalized at its `expirationTime` becomes `EXPIRED` and the chain refunds the escrowed fee.
Where a flow requires explicit approval, an approval transaction moves the action to `APPROVED` and the escrowed fee pays out to the SuperNodes that served it.
Fee escrow aligns incentives for both clients and service providers. Applications pay only when protocol services complete successfully, while SuperNodes are compensated only after verifiable completion. Governance determines the fee parameters for each protocol service.
### SuperNode registry and ranking
The `x/supernode` module is the source of truth for SuperNode registrations. It stores each node's address, endpoint, and stake, maintains the ranked top list that `x/action` routes work to, and accounts for completed actions. A SuperNode must stay tied to an active validator. If the validator is jailed or unbonds, the SuperNode drops off the list.
## Protocol Services
Protocol services are the decentralized capabilities applications consume through Lumera. Every service is implemented through the Action framework, executed by economically bonded SuperNodes, and coordinated by the blockchain through a common execution and settlement lifecycle.
Current protocol services demonstrate this architecture today, while future services extend the same framework rather than introducing separate systems.
Each SuperNode runs the `supernode` binary alongside `lumerad`.
### Cascade
Cascade is permanent file storage, paid once at upload. The SuperNode receives a file through its streaming gRPC endpoint, encodes it with RaptorQ erasure coding, and spreads the symbols across a Kademlia DHT. Any sufficiently large subset of symbols rebuilds the file, so the network self heals when nodes leave. Start with [how Cascade works](/cascade/how-cascade-works) and go deeper in [erasure coding](/cascade/concepts/erasure-coding).
### Identity with LumeraID
LumeraID anchors identity records to Lumera addresses through the `x/lumeraid` module. There is no separate username. Your address is your identity, and the record is controlled by your private key. The protocol uses it wherever ownership must be proven. Cascade downloads of private files require a signature that matches the registering identity, which is why files are private by default even though their encoded symbols live on a public network. Signatures follow ADR-036, the Cosmos standard for signing arbitrary data.
### Future Services
Lumera's execution framework is designed to support a growing ecosystem of decentralized intelligent services. Because every service shares the same Action framework, execution lifecycle, settlement model, and verification architecture, new capabilities extend the protocol rather than introducing separate systems.
Future protocol services extend this same architectural foundation to capabilities such as durable memory, decentralized identity, verifiable execution, settlement, and additional decentralized intelligent services.
## Developer Interfaces
**Lumera Hub** is the official client for web, desktop, and mobile at [hub.lumera.io](https://hub.lumera.io). It covers balances, staking, storage, and identity. See the [Hub overview](/hub/overview).
**LumeScope** is a read-only REST API aggregator for developers who want decoded network state without running a node. It decodes action metadata, aggregates SuperNode metrics, and serves 16 endpoints with a Swagger UI. See the [LumeScope API reference](/api/lumescope).
**`lumerad`** is the chain binary and CLI. It runs nodes, manages keys, broadcasts transactions, and queries every module. See the [CLI reference](/sdk/lumerad-cli).
## Extending the Protocol
The architecture above is intentionally composable. Every new decentralized intelligent service is implemented as another protocol Action moving through the same registration, execution, verification, and settlement lifecycle.
Rather than introducing separate systems for memory, identity, execution, settlement, or future protocol capabilities, Lumera extends a shared architectural foundation built on common protocol modules, economically bonded SuperNodes, and a unified execution framework.
As the protocol evolves, developers continue building against the same APIs, execution model, and architectural foundation while inheriting new protocol capabilities as native services.
The [SDKs](/sdk/javascript) wrap the chain APIs and the SuperNode protocol for JavaScript and Go applications.
## Fee model
Every protocol Action pays a one-time service fee in `ulume` (1 LUME = 1,000,000 ulume). The fee is a pure function of data size, so you can estimate it before submitting. File size is rounded up to the next whole kilobyte.
```text theme={null}
fee = base_action_fee + fee_per_kbyte × ceil(fileBytes / 1024)
```
| Parameter | Value | In LUME |
| - | - | - |
| `base_action_fee` | `30000` ulume | 0.03 LUME |
| `fee_per_kbyte` | `10` ulume | 0.00001 LUME per KB |
Every successful storage adds `30000 + 10 × sizeKB` ulume to what you have spent.
| File size | Storage fee (ulume) | In LUME |
| - | - | - |
| 1 KB | 30,010 | 0.03001 |
| 1 MB (1,024 KB) | 40,240 | 0.04024 |
| 10 MB (10,240 KB) | 132,400 | 0.13240 |
| 100 MB (102,400 KB) | 1,054,000 | 1.05400 |
| 1 GB (1,048,576 KB) | 10,515,760 | 10.51576 |
| Parameter | Value | In LUME |
| - | - | - |
| `base_action_fee` | `10000` ulume | 0.01 LUME |
| `fee_per_kbyte` | `10` ulume | 0.00001 LUME per KB |
Every successful storage adds `10000 + 10 × sizeKB` ulume to what you have spent.
| File size | Storage fee (ulume) | In LUME |
| - | - | - |
| 1 KB | 10,010 | 0.01001 |
| 1 MB (1,024 KB) | 20,240 | 0.02024 |
| 10 MB (10,240 KB) | 112,400 | 0.11240 |
| 100 MB (102,400 KB) | 1,034,000 | 1.03400 |
| 1 GB (1,048,576 KB) | 10,495,760 | 10.49576 |
Governance can change these parameters, so query the exact amount from the chain instead of hardcoding it. The SDK expects the size in KB, rounded up, and returns the fee in `ulume`.
```ts theme={null}
const fileSizeKb = Math.ceil(file.length / 1024);
const { amount } = await client.Blockchain.Action.getActionFee(fileSizeKb);
// amount is a string in ulume. The SDK sets this as the action price.
```
The fee is locked in escrow at registration and released when the Action settles, following [the action lifecycle](#the-action-lifecycle). On mainnet, 98% of each fee pays the SuperNodes that served the Action and 2% goes to the foundation. If the Action expires before finalization, the chain refunds the full fee. Storage is paid once, with no recurring charges, and reads are not metered by the protocol.
The service fee is separate from the gas fee charged on every transaction. The minimum gas price is `0.025ulume`. See [Networks](/networks) for chain parameters and [Protocol revenue](/protocol-revenue) for how fees flow through the network.
## Next steps
The full journey of a Cascade upload through these layers.
The EVM environment on the base layer.
Join the consensus layer.
# Set up the Cascade client
Source: https://docs.lumera.io/cascade/client-setup
Create a Lumera client with network presets, custom endpoints, and a wallet signer.
Every Cascade operation starts with a client. The `createLumeraClient` factory connects to the Lumera Protocol blockchain and configures the SuperNode API client in one call. This page shows how to build the client with each supported signer and how to point it at any network.
## Chain configuration
Keplr requires a chain configuration to register the Lumera network. Define it once and reuse it.
```ts chain-config.ts theme={null}
export const CHAIN_ID = "lumera-testnet-2";
export const LUMERA_CHAIN_INFO = {
chainId: CHAIN_ID,
chainName: "Lumera Testnet",
rpc: "https://rpc.testnet.lumera.io",
rest: "https://lcd.testnet.lumera.io",
bip44: { coinType: 60 },
bech32Config: {
bech32PrefixAccAddr: "lumera",
bech32PrefixAccPub: "lumerapub",
bech32PrefixValAddr: "lumeravaloper",
bech32PrefixValPub: "lumeravaloperpub",
bech32PrefixConsAddr: "lumeravalcons",
bech32PrefixConsPub: "lumeravalconspub",
},
currencies: [
{ coinDenom: "LUME", coinMinimalDenom: "ulume", coinDecimals: 6 },
],
feeCurrencies: [
{
coinDenom: "LUME",
coinMinimalDenom: "ulume",
coinDecimals: 6,
gasPriceStep: { low: 0.025, average: 0.03, high: 0.04 },
},
],
stakeCurrency: {
coinDenom: "LUME",
coinMinimalDenom: "ulume",
coinDecimals: 6,
},
features: ["stargate", "ibc-transfer", "eth-address-gen", "eth-key-sign"],
};
```
## Choose a signer
The client accepts any signer that provides `signDirect`, `signAmino`, and `signArbitrary`. The `signArbitrary` method produces ADR-036 signatures, the Cosmos standard for signing arbitrary offline data. Cascade uses those signatures to authenticate with SuperNodes.
### Browser with Keplr
```ts keplr-client.ts theme={null}
import { createLumeraClient, getKeplrSigner } from "@lumera-protocol/sdk-js";
import { CHAIN_ID, LUMERA_CHAIN_INFO } from "./chain-config";
// Register the Lumera chain and request wallet access
await window.keplr.experimentalSuggestChain(LUMERA_CHAIN_INFO);
// getKeplrSigner returns a unified signer with getAccounts, signDirect,
// signAmino, and signArbitrary already wired up.
const signer = await getKeplrSigner(CHAIN_ID);
const [account] = await signer.getAccounts();
const client = await createLumeraClient({
preset: "testnet",
signer,
address: account.address,
gasPrice: "0.025ulume",
});
```
### Node.js with a mnemonic
In Node.js you build the signer from a mnemonic. You need a Direct signer for transaction signing and an Amino signer for `signArbitrary`.
```ts node-client.ts theme={null}
import { createLumeraClient } from "@lumera-protocol/sdk-js";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import {
Secp256k1HdWallet,
makeSignDoc as makeAminoSignDoc,
} from "@cosmjs/amino";
const mnemonic = process.env.LUMERA_MNEMONIC!;
const directWallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: "lumera",
});
const aminoWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: "lumera",
});
const [account] = await directWallet.getAccounts();
const signer = {
getAccounts: () => directWallet.getAccounts(),
signDirect: (addr: string, doc: any) => directWallet.signDirect(addr, doc),
signAmino: (addr: string, doc: any) => aminoWallet.signAmino(addr, doc),
async signArbitrary(
chainId: string,
signerAddress: string,
data: string
) {
const signDoc = makeAminoSignDoc(
[
{
type: "sign/MsgSignData",
value: {
signer: signerAddress,
data: Buffer.from(data).toString("base64"),
},
},
],
{ gas: "0", amount: [] },
"",
"",
0,
0
);
const { signature } = await aminoWallet.signAmino(signerAddress, signDoc);
return {
signed: data,
signature: signature.signature,
pub_key: signature.pub_key,
};
},
};
const client = await createLumeraClient({
preset: "testnet",
signer: signer as any,
address: account.address,
gasPrice: "0.025ulume",
});
```
Never hardcode mnemonics. Use environment variables or a secrets manager.
## Configuration options
```ts theme={null}
interface LumeraClientConfig {
// Use a preset for testnet or mainnet endpoints
preset?: "testnet" | "mainnet";
// Or specify endpoints manually
// (without a preset, rpcUrl, chainId, and snapiUrl are all required)
rpcUrl?: string;
lcdUrl?: string;
chainId?: string;
snapiUrl?: string;
// Signer (wallet or DirectSecp256k1HdWallet) - required
signer: OfflineSigner;
// Sender bech32 address - required
address: string;
// Gas price for transaction fee estimation
gasPrice?: string;
// HTTP client options for SN-API requests
http?: {
timeout?: number; // default 30000
maxRetries?: number; // default 3
};
}
```
Set `gasPrice` to `0.025ulume`. That is the minimum gas price on both networks.
### Presets
| Preset | Chain ID | RPC | LCD | SN-API |
| - | - | - | - | - |
| `testnet` | `lumera-testnet-2` | `https://rpc.testnet.lumera.io` | `https://lcd.testnet.lumera.io` | `https://snapi.testnet.lumera.io` |
| `mainnet` | `lumera-mainnet-1` | `https://rpc.lumera.io` | `https://lcd.lumera.io` | `https://snapi.lumera.io` |
The public mainnet RPC and LCD endpoints are coming soon. Use the `testnet` preset while you build and test.
### Custom endpoints
Pass explicit endpoints for local development or self-hosted nodes.
```ts theme={null}
const client = await createLumeraClient({
chainId: "", // whatever you passed to `lumerad init --chain-id`
rpcUrl: "http://localhost:26657",
lcdUrl: "http://localhost:1317",
snapiUrl: "http://localhost:8080",
signer: wallet,
address: account.address,
gasPrice: "0.025ulume",
});
```
## Client structure
Once created, the client exposes two primary sub-clients.
```ts theme={null}
// --- Blockchain queries (nested under Action / Supernode / Tx) ---
client.Blockchain.Action.getParams();
client.Blockchain.Action.getAction(actionId);
client.Blockchain.Action.getActionFee(dataSizeKb); // size in KB, rounded up
client.Blockchain.Supernode.listSupernodes();
client.Blockchain.Tx.signAndBroadcast(signerAddress, messages, fee);
// --- Cascade storage (high-level) ---
client.Cascade.uploader.uploadFile(file, params); // upload + monitor
client.Cascade.downloader.download(actionId); // signs the action ID via ADR-036
```
## Go client
The Go SDK follows the same shape. The `lumerasdk.New` entry point takes a config and a keyring, then surfaces `client.Blockchain` for chain queries and `client.Cascade` for storage.
```go main.go theme={null}
package main
import (
"context"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
lumerasdk "github.com/LumeraProtocol/sdk-go/client"
"go.uber.org/zap"
)
func main() {
ctx := context.Background()
kr, _ := keyring.New("lumera", "test", "/tmp", nil)
client, err := lumerasdk.New(ctx, lumerasdk.Config{
ChainID: "lumera-testnet-2",
GRPCEndpoint: "grpc.testnet.lumera.io:443",
RPCEndpoint: "https://rpc.testnet.lumera.io",
Address: "lumera1...",
KeyName: "my-key",
}, kr, lumerasdk.WithLogger(zap.NewExample()))
if err != nil {
panic(err)
}
defer client.Close()
}
```
See [Go SDK](/sdk/go) for uploads, downloads, and event subscriptions in Go.
## Next steps
See every phase of an upload in detail.
Diagnose failures and tune retries and timeouts.
Build a browser upload app with Keplr.
# Cascade download lifecycle
Source: https://docs.lumera.io/cascade/concepts/download-lifecycle
How the SDK authenticates, tracks, and streams a file download from SuperNodes.
To retrieve a file from Cascade, Lumera Protocol's permanent storage service, you only need the `action_id` returned during upload. Knowing the four steps behind a download helps you show progress in your app and handle failures cleanly.
Every download request is authenticated with an [ADR-036](https://docs.cosmos.network/main/build/architecture/adr-036) signature. ADR-036 is the Cosmos standard for signing arbitrary data outside a transaction. The SDK signs the `action_id` string with the connected wallet.
```ts theme={null}
const signature = await signer.signArbitrary(chainId, address, actionId);
```
This proves the requester holds the private key for their Lumera address. Any wallet can download a public file. Only authorized wallets can download a private file.
The SDK sends a download request to the SN-API, the HTTP API served by SuperNodes.
```text theme={null}
POST /api/v1/actions/cascade/{action_id}/downloads
Content-Type: application/json
{
"signature": "base64-encoded ADR-036 signature"
}
```
The SN-API responds with a `task_id` that tracks the download preparation. The SDK retries this request up to 3 times on transient server errors (HTTP 500). SuperNodes may need time to locate and gather the required chunks.
The SDK monitors download progress over Server-Sent Events (SSE), a one-way HTTP stream of status updates.
```text theme={null}
GET /api/v1/downloads/cascade/{task_id}/status
data: {"status": "processing", "progress": 45}
data: {"status": "processing", "progress": 80}
data: {"status": "completed", "progress": 100}
```
In browsers the SDK uses the native `EventSource` API. In Node.js it uses a custom fetch-based SSE parser.
Once the task completes, the file is available as a binary stream.
```text theme={null}
GET /api/v1/downloads/cascade/{task_id}/file
Content-Type: application/octet-stream
```
The SDK returns a `ReadableStream` that your application consumes.
```ts theme={null}
const stream = await client.Cascade.downloader.download(actionId);
// Read the stream
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
// Reassemble into a single buffer
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
// Decode as text (if applicable)
const text = new TextDecoder().decode(result);
```
## Download task states
| Status | Meaning |
| - | - |
| `sdk:download_completed` | File ready for streaming |
| `sdk:completed` | Generic success |
| `sdk:download_failure` | Reconstruction failed |
| `sdk:failed` | Generic failure |
## Convenience methods
### Basic download
```ts theme={null}
// Returns a ReadableStream
const stream = await client.Cascade.downloader.download(actionId);
```
### Download with options
```ts theme={null}
const stream = await client.Cascade.downloader.downloadFile({
actionId: "your-action-id",
taskOptions: {
pollInterval: 2000,
timeout: 120000,
},
});
```
### Private download
The call is identical for private files. The auth signature restricts who can download.
```ts theme={null}
const stream = await client.Cascade.downloader.downloadPrivate(actionId);
```
## SN-API fallback
The SN-API client tries versioned endpoints first (`/api/v1/...`). On a 404 response it falls back to legacy paths (`/api/...`). This keeps the SDK compatible across SN-API versions.
## Next steps
What happens between selecting a file and permanent storage.
How SuperNodes reconstruct your file from a subset of symbols.
Full method reference for uploads, downloads, and task options.
# RaptorQ erasure coding in Cascade
Source: https://docs.lumera.io/cascade/concepts/erasure-coding
Why Cascade uses RaptorQ fountain codes and how LEP-1 derives layout IDs.
Erasure coding is why files stored through Cascade on Lumera Protocol stay available even when many SuperNodes go offline. Understanding it explains Cascade's durability guarantees and the layout IDs that appear in every upload.
## Why erasure coding beats replication
Simple replication stores N full copies of a file. That is wasteful. If your data must survive half the nodes going offline, replication needs 2x the storage. Erasure coding reaches the same reliability with only about 1.5x overhead.
Cascade uses RaptorQ, a fountain code standardized in [RFC 6330](https://www.rfc-editor.org/rfc/rfc6330). A fountain code generates a practically unlimited stream of encoded symbols from a source file. RaptorQ has three properties that matter for permanent storage.
* It encodes a file into N symbols where any K symbols (`K < N`) reconstruct the original.
* Its decoding failure probability is near zero.
* It operates efficiently on large files.
## Encoding during upload
1. The SDK loads the file into the RaptorQ WASM module (`rq-library-wasm`).
2. The encoder produces a layout that describes source blocks and encoding parameters.
3. Encoded symbols are distributed across SuperNodes.
4. Each SuperNode stores a subset of the symbols.
SuperNodes run the same encoding through [`rq-go`](https://pkg.go.dev/lumera.build/rq-go), the official RaptorQ Go library.
## Decoding during download
1. The SN-API, the HTTP API served by SuperNodes, gathers available symbols from responsive SuperNodes.
2. As long as at least K symbols are available, the original file is reconstructed.
3. The BLAKE3 hash of the reconstructed file is verified against the on-chain record.
## LEP-1 layout and ID derivation
LEP-1 (Lumera Enhancement Proposal 1) defines the deterministic algorithm that derives layout IDs from the encoding layout.
### Layout generation
```ts theme={null}
const layout = await createSingleBlockLayout(fileBytes, {
rq_ids_ic: randomSeed,
rq_ids_max: actionParams.rq_ids_max,
});
```
`rq_ids_ic` is a random counter seed. `rq_ids_max` sets how many layout IDs are derived. The layout JSON is compacted (no whitespace) before hashing so every implementation produces identical bytes.
### ID derivation algorithm
Each layout ID is derived deterministically so the on-chain Go implementation can independently verify it.
```text theme={null}
For i = 0 to rq_ids_max - 1:
counter = rq_ids_ic + i
input = Base64(compacted_layout_json) + "." + Base64(adr036_signature) + "." + String(counter)
compressed = zstd_compress(input, level=3)
hash = BLAKE3(compressed)
layout_id = Base58(hash)
```
### Why determinism matters
* **Determinism.** The same file, signer, and seed always produce the same layout IDs.
* **Verifiability.** The chain can check that the claimed layout IDs match the submitted layout.
* **Uniqueness.** Each symbol gets a globally unique identifier.
## The LEP-1 index file
The index file aggregates the layout IDs and the layout signature.
```json theme={null}
{
"version": 1,
"layout_ids": ["7K9xQ2b...", "3Jm2pNd...", "..."],
"layout_signature": "base64-encoded ADR-036 signature"
}
```
The index file is itself signed with ADR-036, the Cosmos standard for signing arbitrary data, and included in the `MsgRequestAction` transaction.
## Next steps
Where layout generation and ID derivation fit in an upload.
Store to Cascade from any IBC-connected Cosmos chain.
Full method reference for uploads, downloads, and task options.
# Interchain accounts and Cascade
Source: https://docs.lumera.io/cascade/concepts/interchain-accounts
Use Cascade storage from any IBC-connected Cosmos chain through ICS-27.
Your application does not need to run on Lumera Protocol to use Cascade. Any IBC-connected Cosmos chain can register storage actions through [Interchain Accounts (ICS-27)](https://ibc.cosmos.network/v8/apps/interchain-accounts/overview/), the IBC standard that lets one chain control an account on another.
This opens cross-chain use cases.
* A DeFi protocol on Injective can store trade receipts permanently on Cascade.
* An NFT marketplace on Osmosis can use Cascade for immutable media storage.
* A DAO on the Cosmos Hub can archive governance proposals.
## How it works
The flow has six steps.
1. The controller chain submits `MsgSendTx` containing a packed `MsgRequestAction`.
2. The IBC relayer delivers the packet to Lumera, the host chain.
3. Lumera's ICA host module unpacks and executes the `MsgRequestAction`.
4. An ICA address on Lumera pays the escrow fee. It must be pre-funded.
5. The action is registered on-chain with an `action_id`.
6. The application uploads the file directly to the SN-API, the HTTP API served by SuperNodes, using the `action_id`.
## Application-level keypairs
The ICA address on Lumera has no user-held private key. The controller chain controls it. Cascade solves this with an `app_pubkey` field in `MsgRequestAction`.
* The application generates a keypair on the controller side.
* The public key is included in the action registration.
* The private key signs file uploads to the SN-API.
* This separates the control plane (IBC messages) from the data plane (file transfers).
## ICA helpers in the Go SDK
The [Go SDK](/sdk/go) provides helpers for ICA-based Cascade.
```go theme={null}
import "github.com/LumeraProtocol/sdk-go/client"
// Pack a MsgRequestAction for ICA submission
packedMsg := client.PackRequestForICA(msg)
// Pack a MsgApproveAction for ICA submission
packedApprove := client.PackApproveForICA(approveMsg)
// Extract action IDs from the IBC acknowledgement
actionIDs := client.ExtractRequestActionIDsFromAck(ackData)
```
Configure the SDK client for ICA.
```go theme={null}
cfg := &client.Config{
ICAOwnerKeyName: "ica-controller-key",
ICAOwnerHRP: "cosmos", // Bech32 prefix of the controller chain
}
```
## The ICA client CLI
The [Lumera ICA client](https://github.com/LumeraProtocol/lumera-ica-client) provides a reference CLI.
```bash theme={null}
# Upload a file via ICA
lumera-ica-client upload --file document.pdf --controller-chain cosmos
# Check action status
lumera-ica-client action status --action-id
# Download a file
lumera-ica-client download --action-id
```
## Security considerations
* **Control plane isolation.** IBC messages carry only metadata and fee escrow. File data never touches the IBC channel.
* **Data plane authentication.** File uploads to the SN-API use application-level signatures, independent of the ICA address.
* **Fee pre-funding.** The ICA address must hold `ulume` before registering actions. Monitor its balance to prevent failed registrations.
## Next steps
Install and configure the Go SDK used for ICA flows.
What happens after the action registers on-chain.
The end-to-end Cascade storage model in one page.
# Cascade upload lifecycle
Source: https://docs.lumera.io/cascade/concepts/upload-lifecycle
Phase by phase internals of a Cascade upload from file hash to permanent storage.
Knowing what happens inside a Cascade upload helps you debug failed uploads and build custom flows on Lumera Protocol. This page walks through each phase, the on-chain action states, and the task states the SDK reports.
A Cascade upload has three phases. The `uploadFile` method in the JavaScript SDK (`@lumera-protocol/sdk-js`) runs all three for you. You can also run them individually for fine-grained control.
| Phase | SDK method |
| - | - |
| Prepare | `prepareFile` |
| Register | `registerAction` |
| Send | `sendFileToSupernodes` |
## Prepare the file
```ts theme={null}
const { fileBytes, dataHash } = await client.Cascade.uploader.prepareFile(file);
```
The prepare phase does two things.
1. It converts the input file (a `Uint8Array`, `File`, or `Blob`) to a `Uint8Array`.
2. It computes a BLAKE3 hash over the file bytes. This hash becomes the permanent content identifier stored on-chain.
## Register the action on-chain
```ts theme={null}
const { actionId, authSignature } = await client.Cascade.uploader.registerAction(
{ fileBytes, dataHash },
{
fileName: "paper.pdf",
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
}
);
```
Registration runs seven sub-steps.
### Fetch action parameters
The SDK queries the chain for the current action parameters.
```ts theme={null}
const actionParams = await client.Blockchain.Action.getParams();
// Returns: { max_raptor_q_symbols, ... }
```
These parameters determine the RaptorQ encoding configuration. The SDK caches the result for 5 minutes.
### Generate the RaptorQ layout
The SDK runs the file through the RaptorQ WASM module to generate an erasure coding layout.
* `rq_ids_ic` is a random counter seed in `[0, max_raptor_q_symbols)`.
* The layout contains block-level encoding metadata that SuperNodes need to store and reconstruct the file.
* The layout JSON is compacted (no whitespace) to match the Go reference implementation.
See [erasure coding](/cascade/concepts/erasure-coding) for how the layout works.
### Sign the layout with ADR-036
The compacted layout bytes are signed with [ADR-036](https://docs.cosmos.network/main/build/architecture/adr-036) `signArbitrary`. ADR-036 is the Cosmos standard for signing arbitrary data outside a transaction.
```ts theme={null}
signer.signArbitrary(chainId, address, layoutBytes)
```
This signature proves the layout was created by the account that registered the action.
### Derive layout IDs
Layout IDs are deterministic identifiers derived from the layout and its signature. The algorithm must match the on-chain Go implementation exactly.
```text theme={null}
For each i in [0, rq_ids_max):
counter = rq_ids_ic + i
input = Base64(layout) + "." + Base64(signature) + "." + decimal(counter)
compressed = zstd(input, level=3)
hash = BLAKE3(compressed)
id = Base58(hash)
```
These IDs are included in the on-chain action. SuperNodes use them to verify data integrity.
### Build the LEP-1 index file
LEP-1 (Lumera Enhancement Proposal 1) defines a compact index that aggregates the layout IDs and the layout signature.
```json theme={null}
{
"version": 1,
"layout_ids": ["7K9x...", "3Jm2...", "..."],
"layout_signature": "base64..."
}
```
The index file is signed with ADR-036 and included in the on-chain registration.
### Create the auth signature
The SDK creates a separate signature over the `dataHash`. This auth signature later authenticates the file upload to the SN-API, the HTTP API served by SuperNodes.
### Broadcast MsgRequestAction
The SDK simulates the transaction to estimate gas, then signs and broadcasts it.
```ts theme={null}
// Simplified. The SDK builds this internally.
const msg = {
typeUrl: "/lumera.action.v1.MsgRequestAction",
value: {
creator: address,
actionType: "ACTION_TYPE_CASCADE",
dataHash: dataHash,
rqIds: layoutIds,
// ... other fields
},
};
```
The chain validates the message, escrows the storage fee, and emits an `action_registered` event. The SDK extracts the `action_id` from the event attributes. The `action_id` is your permanent reference to the stored file.
## Send the file to SuperNodes
```ts theme={null}
const uploadResult = await client.Cascade.uploader.sendFileToSupernodes(
actionId,
authSignature,
fileBytes,
{ taskOptions: { pollInterval: 2000, timeout: 300000 } }
);
```
The send phase has four steps.
1. The SDK sends the file as multipart form data to `POST /api/v1/actions/cascade`.
2. The SN-API distributes chunks to the SuperNode mesh.
3. The SDK polls `GET /api/v1/actions/cascade/tasks/{task_id}` at the configured interval.
4. The upload completes when the task reaches a terminal status.
The multipart request carries three fields.
```text theme={null}
FormData {
action_id: string
signature: string (auth signature)
file: Blob
}
```
## Upload in one call
For most use cases, call `uploadFile` to run all three phases.
```ts theme={null}
const task = await client.Cascade.uploader.uploadFile(fileBytes, {
fileName: "document.pdf",
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
taskOptions: {
pollInterval: 2000, // Check every 2 seconds
timeout: 300000, // Fail after 5 minutes
},
});
```
`uploadFile` resolves to the completed SN-API task, not the action ID. If you need to store the action ID, run the three phases yourself as shown above.
## Action states on chain (simplified)
This is the simplified path a Cascade upload takes. The chain's `ActionState` enum has eight values, including `UNSPECIFIED`, `APPROVED` and `REJECTED`, which a Cascade upload does not pass through. See [Actions](/intelligence-layer/verifiable-execution/actions) for the full enum.
```text theme={null}
PENDING --> PROCESSING --> DONE
| |
+------------+--> FAILED / EXPIRED
```
| State | Meaning |
| - | - |
| `PENDING` | Action registered and fee escrowed, waiting for the file |
| `PROCESSING` | SuperNodes encode the file and distribute symbols |
| `DONE` | Symbols stored, the file is permanently available |
| `FAILED` | Finalization failed |
| `EXPIRED` | No SuperNode finalized before `expirationTime` |
The storage fee is escrowed at registration. The chain refunds it if the action expires before finalization. The current mainnet fee is a 30,000 `ulume` base plus 10 `ulume` per KB. A 1 MB file costs about 0.04 LUME. Testnet uses a 10,000 `ulume` base, so the same file costs about 0.02 LUME there. Fee parameters can change through governance.
## SDK task states
The SDK reports progress through task statuses.
| Status | Phase | Meaning |
| - | - | - |
| `sdk:completed` | Upload | File successfully stored |
| `sdk:upload_completed` | Upload | Upload confirmed |
| `sdk:failed` | Upload | Generic failure |
| `sdk:supernodes_unavailable` | Upload | No SuperNode accepted the task |
| `sdk:registration_failure` | Register | On-chain registration failed |
| `sdk:upload_failed` | Upload | File transfer to the SN-API failed |
| `sdk:processing_failed` | Upload | The SN-API could not process the file |
| `sdk:processing_timeout` | Upload | Processing exceeded the timeout |
## Retry behavior
The SDK retries the SN-API upload up to 5 times with a 3-second delay between attempts. This covers the window where a SuperNode has not yet indexed the on-chain action. The chain and the SN-API are eventually consistent.
## Next steps
How authenticated downloads stream files back from SuperNodes.
How RaptorQ encoding and LEP-1 layout IDs work.
Handle failed task states and retries in production.
# Handle Cascade errors
Source: https://docs.lumera.io/cascade/error-handling
Diagnose common Cascade failures, tune retries and timeouts, and understand automatic refunds.
Cascade uploads touch two systems, the Lumera Protocol chain and the SuperNode network, so errors can surface in several places. This page lists the failures you are most likely to hit, the fix for each one, and how the SDK handles retries, timeouts, and refunds.
## Common errors
### RPC connection errors
If the Lumera RPC node is down or unreachable, client creation and blockchain transactions fail.
```ts theme={null}
try {
const client = await createLumeraClient({ preset: "testnet", signer, address, gasPrice: "0.025ulume" });
} catch (err) {
// "Failed to connect to RPC endpoint" or "ECONNREFUSED"
console.error("Cannot reach RPC node. Check your network or try a different endpoint.");
}
```
Switch to a different RPC endpoint, or wait and retry. You can pass a custom endpoint instead of the preset.
```ts theme={null}
const client = await createLumeraClient({
chainId: "lumera-testnet-2",
rpcUrl: "https://lumera-testnet-rpc.polkachu.com",
lcdUrl: "https://lumera-testnet-api.polkachu.com",
snapiUrl: "https://snapi.testnet.lumera.io",
signer,
address,
gasPrice: "0.025ulume",
});
```
### Insufficient funds
Uploads need LUME to pay the action fee. An empty wallet fails with this error.
```text theme={null}
"insufficient funds: 0ulume is smaller than 50000ulume"
```
Fund your wallet with testnet tokens from the [faucet](/faucet) before uploading.
### Keplr extension not found
The user has not installed Keplr, or the page loaded before the extension injected `window.keplr`. Wait for the extension before you access it.
```ts theme={null}
async function waitForKeplr(timeout = 3000): Promise {
if (window.keplr) return true;
return new Promise((resolve) => {
const timer = setTimeout(() => resolve(false), timeout);
window.addEventListener("keplr_keystorechange", () => {
clearTimeout(timer);
resolve(true);
});
});
}
```
### Wallet popup rejected
Keplr asks the user to approve every signature request. A click on Reject surfaces as an error.
```ts theme={null}
try {
await client.Cascade.uploader.uploadFile(file, params);
} catch (err) {
if (err.message?.includes("Request rejected")) {
console.error("Please approve the signature request to continue");
}
}
```
Prompt the user to try again and approve the popup. Cascade requests ADR-036 signatures for authentication, not transactions. Consider a pre-sign dialog that explains what is being signed. One upload can trigger several popups, one each for the layout, the index, and auth.
### SuperNodes unavailable
The task fails when no SuperNodes are online to handle your request.
```text theme={null}
"Task failed with status: sdk:supernodes_unavailable"
```
Wait a few minutes and retry. This usually means the testnet SuperNodes are temporarily down or overloaded.
### Upload timeout
Large files or slow networks can push a task past the default timeout of 5 minutes.
```text theme={null}
"Task timed out after 300000ms"
```
Increase the timeout in `taskOptions`.
```ts theme={null}
const result = await client.Cascade.uploader.uploadFile(file, {
fileName: "large-file.zip",
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60),
taskOptions: {
timeout: 600000, // 10 minutes
},
});
```
### Action not registered
The transaction went through, but the SDK could not find the `action_registered` event in the response.
```text theme={null}
"action_registered event not found in transaction. Available events: coin_spent, coin_received, transfer, message, tx"
```
This usually means the upload started too soon after the client was created, or the transaction failed silently on chain. Work through three checks.
1. Add a short delay before uploading so the chain can finalize the transaction.
2. Confirm the transaction succeeded on [Lumera Portal](https://portal.testnet.lumera.io/lumera-testnet-2).
3. Make sure you hold enough funds for the action fee. The transaction may have failed on gas.
### Action not found
The action was registered on chain, but the SuperNode has not indexed it yet, or the action ID is invalid. SN-API responds with a 404.
```ts theme={null}
try {
const stream = await client.Cascade.downloader.download("99999");
} catch (err) {
// HttpError with statusCode 404
console.error("Action not found. Check the Action ID.");
}
```
The SDK retries this automatically with 5 attempts and 3-second delays. If the error persists, work through these steps.
1. Verify the action exists on chain in [Lumera Portal](https://portal.testnet.lumera.io/lumera-testnet-2).
2. Increase the timeout in `taskOptions`.
3. Try a different SN-API endpoint.
### Download failed or empty stream
The SuperNodes could not reconstruct the file. This happens with very recent uploads that are still processing, or with actions that have expired.
1. Wait a few minutes after upload before you attempt a download.
2. Verify the action status with `client.Blockchain.Action.getAction(actionId)`.
3. Check that `state === "ACTION_STATE_DONE"`.
### Private file access denied
A wallet that did not upload a private file cannot download it.
```text theme={null}
"failed to verify download signature"
```
Only the wallet that uploaded the file can download it when `isPublic: false`. Connect with the wallet that created the action.
## Automatic refunds
The storage fee is escrowed on chain when you register an action. The chain refunds it to your wallet automatically when the action expires because no SuperNode finalized it before its `expirationTime`. A rejected finalization does not refund on its own; the action stays open until it is finalized or expires.
You do not file a claim or send another transaction. The refund is part of the protocol.
## Built-in retries
The SDK retries failed HTTP requests with exponential backoff, which doubles the wait after each attempt. The defaults are listed below.
| Setting | Default |
| - | - |
| Max attempts | `3` |
| Initial delay | `1000ms` |
| Max delay | `30000ms` |
| Backoff multiplier | `2x` |
| Retryable status codes | `408, 429, 500, 502, 503, 504` |
You can override the defaults when you create the client.
```ts theme={null}
const client = await createLumeraClient({
preset: "testnet",
signer,
address,
gasPrice: "0.025ulume",
http: {
timeout: 60000,
retry: {
maxAttempts: 5,
initialDelay: 2000,
},
},
});
```
File uploads and downloads skip automatic retries because the request body or response stream can only be consumed once. The SDK retries everything else automatically, such as status checks and metadata queries.
## Task options and status polling
SuperNodes process upload and download tasks asynchronously. The SDK polls the task status until the task completes, then returns the result. Two settings control the polling.
```ts theme={null}
taskOptions: {
timeout: 300000, // 5 min default, how long to wait before giving up
pollInterval: 2000, // 2s default, how often to check task status
}
```
If an upload times out, the task may still be processing on the SuperNode. Do not retry immediately or you may create a duplicate action on chain.
## Getting help
| Channel | Where |
| - | - |
| Discord | [discord.com/invite/lumeraprotocol](https://discord.com/invite/lumeraprotocol) |
| GitHub issues | [LumeraProtocol/releases](https://github.com/LumeraProtocol/releases/issues) (choose `sdk-js` as the product) |
| Lumera Portal | [portal.testnet.lumera.io](https://portal.testnet.lumera.io/lumera-testnet-2) |
## Next steps
See every phase of an upload in detail.
Learn how files are reconstructed and streamed back.
# Cascade Everlight overview
Source: https://docs.lumera.io/cascade/everlight/overview
Learn how Everlight funds ongoing Cascade storage from a single upfront payment.
Everlight is the retention economics behind Cascade, the permanent storage service of Lumera Protocol. It pays SuperNodes ongoing compensation for the Cascade data they keep, so the network can afford to hold your files long after you upload them. You still pay once. Everlight changes how that payment keeps working over time.
Everlight is rolling out in phases. The chain-side pieces ship through governance-approved upgrades. This page describes the model, not a delivery schedule.
## The problem it solves
Cascade charges one fee when you register a file. See [How Cascade works](/cascade/how-cascade-works) for that flow. Storage, however, is not a one-time cost. The SuperNodes that hold your data pay for disk, bandwidth, and hardware replacement every month, for as long as the data exists.
Without ongoing funding, every upload creates a storage obligation that nobody is paid to meet. Operators would carry the cost themselves, and the permanence promise would rest on goodwill instead of economics. Everlight closes that gap. It turns the one-time fee model into a funded, recurring compensation stream for the operators who actually retain the data.
## The promise
The user-facing contract is simple.
> Pay once. Stored as long as the network sustains it.
Three things follow from that sentence.
* Registration is a one-time payment. There are no subscriptions and no renewals.
* Retention is funded, not assumed. A dedicated pool pays SuperNodes every payment period for the Cascade bytes they hold.
* The guarantee is honest. Under normal conditions data is expected to persist indefinitely, but the current model is best-effort rather than a bounded time commitment.
A later evolution of Everlight adds an endowment. Part of a registration payment would be staked, and the staking yield would fund retention for a guaranteed minimum period, with best-effort retention continuing beyond it. That keeps the pay-once experience while adding a bounded guarantee. The endowment is a design direction, not a shipped feature, and no launch date is committed.
## How it works
Everlight adds two mechanisms to the protocol.
1. **The Everlight SN Pool.** A module account inside `x/supernode` that collects LUME from several funding sources. Every payment period it distributes to eligible SuperNodes in proportion to the Cascade data each one retains. [How Everlight payments work](/cascade/everlight/payments) covers the pool, the periods, and the funding sources.
2. **The STORAGE\_FULL state.** A SuperNode that fills its disk stops receiving new storage assignments but keeps serving the data it holds, keeps earning Everlight payouts for that data, and stays available for compute work. [Everlight for SuperNode operators](/cascade/everlight/supernode-operators) covers the state machine and payout eligibility.
## What changes for you
If you upload to Cascade, nothing changes. You register an action, pay one fee, and upload your file exactly as before. Everlight works behind the scenes to fund the retention of what you stored.
If you run a SuperNode, Everlight is new revenue. You earn recurring payouts for the Cascade data you retain, on top of the existing action fees. Start with the [SuperNode overview](/supernodes/overview) if you are new to operating one.
## Next steps
The pool, payment periods, and funding sources.
The STORAGE\_FULL state and payout eligibility.
Follow an upload from registration to permanent storage.
# How Everlight payments work
Source: https://docs.lumera.io/cascade/everlight/payments
See how the Everlight SN Pool collects funding and pays SuperNodes for retained Cascade data.
Everlight pays SuperNodes recurring compensation for the Cascade data they retain. This page explains where that money accumulates, when it is paid out, how each SuperNode's share is calculated, and where the funding comes from. Read the [Everlight overview](/cascade/everlight/overview) first if you are new to the model.
Everlight is rolling out in phases. The mechanisms on this page ship through governance-approved chain upgrades, and parameters can change through governance at any time.
## The Everlight SN Pool
The Everlight SN Pool is a module account inside `x/supernode`, the module that manages SuperNode registration, ranking, and rewards. There is no separate Everlight module. The pool account accumulates LUME between distributions, and any address can send funds to it with a standard bank transfer. The distribution logic does not care where the balance came from. It pays out whatever the pool holds when a payment period ends.
## Payment periods
Payment periods are measured in block height, not wall-clock time. A governance parameter, `payment_period_blocks`, sets the number of blocks between distributions. At the end of every block the module checks how many blocks have passed since the last distribution. Once the interval has elapsed, the distribution runs automatically in that block. No transaction or off-chain trigger is involved.
Two edge cases are handled cleanly.
* If the pool is empty at the boundary, the period simply advances and SuperNodes receive nothing that period.
* If no SuperNode is eligible at the boundary, the balance carries over to the next period.
## Proportional distribution
The pool distributes to eligible SuperNodes in proportion to the Cascade data each one retains. The weight metric is `cascade_kademlia_db_bytes`, the number of bytes a SuperNode holds in its Cascade Kademlia store. SuperNodes report this metric through LEP-4, the Lumera Enhancement Proposal that defines self-reported node metrics. See the [LEP repository](https://github.com/LumeraProtocol/lumera/tree/master/docs/leps) for the proposal texts.
```text theme={null}
payout = pool_balance * (your_retained_bytes / total_eligible_bytes)
```
A SuperNode is eligible for a distribution when all of the following hold.
* Its state is `ACTIVE` or `STORAGE_FULL`. See [Everlight for SuperNode operators](/cascade/everlight/supernode-operators) for the state rules.
* Its retained Cascade bytes meet the minimum payment threshold.
* Its latest metrics report is recent enough to count as fresh.
Reported bytes pass through guardrails before they become payout weight. A smoothing window averages reports over several periods, a growth cap limits how fast a node's reported share can rise, and new SuperNodes ramp up to full weight gradually. These guardrails make point-in-time manipulation of the metric unprofitable.
## Funding sources
The pool draws from several sources, so no single stream is a point of failure.
| Source | How it reaches the pool |
| - | - |
| Foundation staking rewards | The Foundation claims its own staking rewards and sends them to the pool with a standard bank transfer. No chain change is needed. |
| Share of action registration fees | A governance parameter routes a share of every action registration fee to the pool. The designed share is 2 percent. |
| Community Pool transfers | Any token holder can propose a transfer to the pool through governance with `MsgCommunityPoolSpend`. |
Registration fees themselves do not go up. The routed share comes out of the existing fee split, so uploaders pay the same one-time price. You are never billed again after registration.
## Governance parameters
Every economic knob in Everlight is a governance parameter. Token holders can tune the system through standard proposals, without a hard fork.
| Parameter | Controls |
| - | - |
| `payment_period_blocks` | Blocks between distributions |
| `registration_fee_share_bps` | Share of action registration fees routed to the pool |
| `min_cascade_bytes_for_payment` | Minimum retained bytes to qualify for payouts |
| `measurement_smoothing_periods` | Rolling average window for payout weight |
| `new_sn_ramp_up_periods` | Ramp-up length for new SuperNodes |
| `usage_growth_cap_bps_per_period` | Maximum growth rate of reported bytes per period |
Query current values on chain once the parameters are live, and treat any number in this page as changeable by governance.
## Next steps
The STORAGE\_FULL state and what payouts mean for your node.
How parameter changes are proposed and voted on.
The upload flow that Everlight keeps funded.
# Everlight for SuperNode operators
Source: https://docs.lumera.io/cascade/everlight/supernode-operators
Understand the STORAGE_FULL state, payout eligibility, and what Everlight means for your node.
Everlight turns retained Cascade data into recurring revenue for your SuperNode. This page covers what changes on the operator side, the new `STORAGE_FULL` state, and what keeps your node eligible for payouts. Read [How Everlight payments work](/cascade/everlight/payments) for the distribution mechanics.
SuperNode software compatibility for Everlight shipped in `v2.5.0-testnet`. Everlight itself is rolling out in phases, and the chain-side pieces arrive through governance-approved upgrades. Keep your node current with `sn-manager` and watch upgrade announcements. The [operations guide](/supernodes/operations) covers updates and monitoring.
## The STORAGE\_FULL state
Today a SuperNode that violates any compliance threshold is moved to `POSTPONED` and excluded from all work. That is the right response to an unreliable node, but a full disk is not unreliability. A node that has filled its storage allocation still holds its Cascade data and can still run compute services.
Everlight introduces `STORAGE_FULL`, a state for exactly this case. The transition rules are strict.
* A node enters `STORAGE_FULL` when its `disk_usage_percent` exceeds `max_storage_usage_percent` and no other compliance check is failing. The threshold is an existing SuperNode parameter, and governance can change it.
* If any other check fails, with or without a full disk, the node goes to `POSTPONED` as before. The more restrictive state always wins.
* When the node frees storage below the threshold, it returns to `ACTIVE`.
```text theme={null}
ACTIVE ........ storage full, nothing else wrong ....... STORAGE_FULL
ACTIVE ........ any other violation .................... POSTPONED
STORAGE_FULL .. storage freed .......................... ACTIVE
STORAGE_FULL .. another violation appears .............. POSTPONED
```
## STORAGE\_FULL versus POSTPONED
The two states look similar but their economics are opposites.
| Capability | STORAGE\_FULL | POSTPONED |
| - | - | - |
| New Cascade storage assignments | Excluded | Excluded |
| Sense (compute) | Eligible | Excluded |
| Agents (compute) | Eligible | Excluded |
| Everlight payouts | Paid for held data | Not eligible |
A `STORAGE_FULL` node keeps earning Everlight payouts for every byte of Cascade data it already holds, and it stays eligible for compute work such as Sense verification and AI inference. Only new storage assignments stop. A `POSTPONED` node earns nothing from Everlight until it resolves its violations.
Filling your disk does not end your storage revenue. Held data keeps paying, and compute work keeps flowing. Adding capacity puts you back in line for new storage assignments on top of that.
## Payout eligibility
Your node qualifies for a distribution when all of the following hold.
* Its state is `ACTIVE` or `STORAGE_FULL`.
* Its retained Cascade bytes, reported as `cascade_kademlia_db_bytes` through LEP-4 metrics, meet the minimum payment threshold.
* Its latest metrics report is fresh.
Payouts are proportional to retained bytes, so the more Cascade data your node holds, the larger its share of each distribution. New nodes ramp up to full payout weight over several periods, and a smoothing window averages reported bytes across periods. All thresholds are governance parameters and can change.
## What to do as an operator
1. Run a compatible SuperNode version. Compatibility shipped in `v2.5.0-testnet`, and `sn-manager` keeps your node on current releases.
2. Keep your node compliant. Everlight rewards retention, but only for nodes that are `ACTIVE` or `STORAGE_FULL`. A `POSTPONED` node forfeits payouts entirely.
3. Watch your disk headroom. `STORAGE_FULL` protects your revenue on held data, but only `ACTIVE` nodes receive new storage assignments that grow it.
4. Follow governance. Payment period length, minimum thresholds, and the fee share that funds the pool are all on-chain parameters.
## Next steps
Updates, configuration, and monitoring for your SuperNode.
The pool, payment periods, and proportional distribution.
What SuperNodes do and how they earn.
# Build a browser app
Source: https://docs.lumera.io/cascade/guides/browser-app
Build a Vite and TypeScript single page app that stores files on Cascade through Keplr.
In this guide you build a Vite and TypeScript single page app that connects to the Keplr wallet, uploads files to Cascade, and retrieves them again by action ID. Cascade is the permanent storage service of Lumera Protocol. Storage is paid once at upload, and reads are not metered by the protocol.
Every upload returns an action ID. This is the permanent on-chain reference you later use to download the file.
The app talks to Cascade straight from the browser. The SDK handles the SN-API transfer, so you do not need a backend server for storage.
## What you need
* [Node.js](https://nodejs.org/) 18 or later
* The [Keplr](https://www.keplr.app/) browser extension
* Testnet LUME for fees. Request tokens from the [faucet](/faucet).
## Build the app
Create a new Vite project and install the SDK together with the browser build tooling.
```bash theme={null}
npm create vite@latest my-cascade-app -- --template vanilla-ts
cd my-cascade-app
npm install @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
npm install -D vite-plugin-node-polyfills vite-plugin-wasm vite-plugin-top-level-await
```
The SDK depends on WASM modules and Node.js globals that need polyfilling in the browser.
```ts vite.config.ts theme={null}
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
export default defineConfig({
plugins: [
wasm(),
topLevelAwait(),
nodePolyfills({
include: ["buffer", "process"],
globals: { Buffer: true, global: true, process: true },
}),
],
optimizeDeps: {
exclude: ["undici", "@bokuweb/zstd-wasm"],
},
define: {
"process.env": "{}",
},
});
```
This file holds the testnet chain ID, the gas price, and the chain record the app suggests to Keplr.
```ts src/config.ts theme={null}
export const CHAIN_ID = "lumera-testnet-2";
export const GAS_PRICE = "0.025ulume";
export const CHAIN_INFO = {
chainId: CHAIN_ID,
chainName: "Lumera Testnet",
rpc: "https://rpc.testnet.lumera.io",
rest: "https://lcd.testnet.lumera.io",
bip44: { coinType: 60 },
bech32Config: {
bech32PrefixAccAddr: "lumera",
bech32PrefixAccPub: "lumerapub",
bech32PrefixValAddr: "lumeravaloper",
bech32PrefixValPub: "lumeravaloperpub",
bech32PrefixConsAddr: "lumeravalcons",
bech32PrefixConsPub: "lumeravalconspub",
},
currencies: [
{ coinDenom: "LUME", coinMinimalDenom: "ulume", coinDecimals: 6 },
],
feeCurrencies: [
{
coinDenom: "LUME",
coinMinimalDenom: "ulume",
coinDecimals: 6,
gasPriceStep: { low: 0.025, average: 0.03, high: 0.04 },
},
],
stakeCurrency: {
coinDenom: "LUME",
coinMinimalDenom: "ulume",
coinDecimals: 6,
},
features: ["stargate", "ibc-transfer", "eth-address-gen", "eth-key-sign"],
};
```
`experimentalSuggestChain` registers the Lumera testnet in Keplr the first time a user connects.
```ts src/wallet.ts theme={null}
import { CHAIN_ID, CHAIN_INFO } from "./config";
export async function connectKeplr(): Promise<{
address: string;
signer: any;
}> {
if (!window.keplr) {
throw new Error("Keplr extension not found. Please install Keplr.");
}
// Suggest the Lumera chain to Keplr
await window.keplr.experimentalSuggestChain(CHAIN_INFO);
await window.keplr.enable(CHAIN_ID);
const offlineSigner = await window.keplr.getOfflineSignerAuto(CHAIN_ID);
const accounts = await offlineSigner.getAccounts();
return {
address: accounts[0].address,
signer: offlineSigner,
};
}
```
The `getKeplrSigner` helper wraps the Keplr signer with the ADR-036 `signArbitrary` support that Cascade uses to authenticate storage operations. ADR-036 is the Cosmos standard for signing arbitrary off-chain messages.
```ts src/cascade.ts theme={null}
import { createLumeraClient, getKeplrSigner } from "@lumera-protocol/sdk-js";
import { CHAIN_ID, GAS_PRICE } from "./config";
let client: Awaited> | null = null;
export async function initCascade(address: string) {
const signer = await getKeplrSigner(CHAIN_ID);
client = await createLumeraClient({
preset: "testnet",
signer,
address,
gasPrice: GAS_PRICE,
});
return client;
}
export async function uploadFile(
file: File,
onProgress?: (status: string) => void
): Promise {
if (!client) throw new Error("Client not initialized");
onProgress?.("Reading file...");
const fileBytes = new Uint8Array(await file.arrayBuffer());
const uploader = client.Cascade.uploader;
const prepared = await uploader.prepareFile(fileBytes);
onProgress?.("Registering action on-chain...");
const { actionId, authSignature } = await uploader.registerAction(prepared, {
fileName: file.name,
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
});
onProgress?.("Sending file to SuperNodes...");
await uploader.sendFileToSupernodes(actionId, authSignature, prepared.fileBytes, {
taskOptions: { pollInterval: 2000, timeout: 300000 },
});
onProgress?.(`Upload complete! Action ID: ${actionId}`);
return actionId;
}
export async function downloadFile(actionId: string): Promise {
if (!client) throw new Error("Client not initialized");
const stream = await client.Cascade.downloader.download(actionId);
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) chunks.push(value);
}
// Reassemble chunks
const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
```
The page has three controls. A connect button, an upload button with a file picker, and a download button with an action ID field.
```ts src/main.ts theme={null}
import { connectKeplr } from "./wallet";
import { initCascade, uploadFile, downloadFile } from "./cascade";
const connectBtn = document.getElementById("connect") as HTMLButtonElement;
const uploadBtn = document.getElementById("upload") as HTMLButtonElement;
const downloadBtn = document.getElementById("download") as HTMLButtonElement;
const fileInput = document.getElementById("file") as HTMLInputElement;
const actionIdInput = document.getElementById("actionId") as HTMLInputElement;
const status = document.getElementById("status") as HTMLDivElement;
let address: string | null = null;
connectBtn.addEventListener("click", async () => {
try {
const wallet = await connectKeplr();
address = wallet.address;
await initCascade(address);
status.textContent = `Connected: ${address}`;
uploadBtn.disabled = false;
downloadBtn.disabled = false;
} catch (err: any) {
status.textContent = `Error: ${err.message}`;
}
});
uploadBtn.addEventListener("click", async () => {
const file = fileInput.files?.[0];
if (!file) {
status.textContent = "Please select a file first.";
return;
}
try {
const actionId = await uploadFile(file, (s) => {
status.textContent = s;
});
actionIdInput.value = actionId;
status.textContent = `File stored permanently! Action ID: ${actionId}`;
} catch (err: any) {
status.textContent = `Upload error: ${err.message}`;
}
});
downloadBtn.addEventListener("click", async () => {
const actionId = actionIdInput.value.trim();
if (!actionId) {
status.textContent = "Enter an action ID to download.";
return;
}
try {
status.textContent = "Downloading...";
const data = await downloadFile(actionId);
status.textContent = `Downloaded ${data.length} bytes.`;
// Trigger browser download
const blob = new Blob([data]);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "cascade-file";
a.click();
URL.revokeObjectURL(url);
} catch (err: any) {
status.textContent = `Download error: ${err.message}`;
}
});
```
```html index.html theme={null}
Cascade File Storage
Cascade File Storage
Upload
Download
Not connected
```
```bash theme={null}
npm run dev
```
Open `http://localhost:5173`, connect Keplr, and upload a file. The returned action ID is your permanent reference. Use it to retrieve the file from anywhere, at any time.
## Key takeaways
1. Use `getKeplrSigner` from the SDK. It wraps the Keplr extension signer with the ADR-036 `signArbitrary` support that Cascade requires.
2. The action ID is your permanent reference. Store it in your database, on chain, or wherever your application tracks state.
3. Uploads run in two phases. The SDK first registers the action on chain, then transfers the file to a SuperNode.
4. Downloads are streamed. Read from the `ReadableStream` so large files never sit fully in memory.
## Next steps
Encrypt files client-side before uploading them to Cascade.
Explore a full publishing platform built on Cascade.
Run the same SDK on a server with a programmatic signer.
# Store encrypted files on Cascade
Source: https://docs.lumera.io/cascade/guides/encrypted-storage
Encrypt files client-side with wallet-derived keys before uploading them to Cascade.
Cascade, the permanent storage service of Lumera Protocol, stores files publicly by default. Any wallet can download a file if it knows the action ID. For private data, encrypt files client-side before you upload them. This guide implements wallet-based encryption with libsodium, so users only need their wallet and never a separate password.
You need a browser app with a connected Cascade client and a signer that supports `signArbitrary` (ADR-036). Follow [Build a browser app](/cascade/guides/browser-app) first if you are starting fresh. The [Research Archive](/cascade/guides/research-archive) uses this exact pattern for encrypted drafts with secure collaboration.
## How it works
The scheme uses two keys. The wallet key is re-derived from a wallet signature whenever needed. A random document key encrypts the file itself.
## Implement the pattern
```bash npm theme={null}
npm install libsodium-wrappers-sumo
```
```bash yarn theme={null}
yarn add libsodium-wrappers-sumo
```
```bash pnpm theme={null}
pnpm add libsodium-wrappers-sumo
```
If you use Vite, exclude libsodium from dependency optimization.
```ts vite.config.ts theme={null}
export default defineConfig({
// ... other config
optimizeDeps: {
exclude: ["libsodium-sumo", "libsodium-wrappers-sumo"],
},
resolve: {
alias: {
"libsodium-sumo": "libsodium-sumo/dist/modules/libsodium-sumo.js",
},
},
});
```
Instead of asking users to manage separate encryption passwords, derive a deterministic key from a wallet signature.
```ts src/crypto.ts theme={null}
import sodium from "libsodium-wrappers-sumo";
await sodium.ready;
const DERIVE_MESSAGE = "Lumera App: Derive encryption key";
export async function deriveKeyFromWallet(
chainId: string,
address: string,
signArbitrary: (chainId: string, address: string, data: string) => Promise
): Promise {
// Sign a fixed message with ADR-036
const signResult = await signArbitrary(chainId, address, DERIVE_MESSAGE);
const signatureBytes = Uint8Array.from(
atob(signResult.signature),
(c) => c.charCodeAt(0)
);
// Hash the signature with BLAKE2b to produce a 256-bit key
const key = sodium.crypto_generichash(
sodium.crypto_secretbox_KEYBYTES,
signatureBytes
);
return key;
}
```
**Why ADR-036?** Signing a fixed message means the same wallet always produces the same signature, which derives the same encryption key. The key is never stored. It is re-derived on demand, so users only need their wallet to decrypt.
The helpers wrap libsodium's `crypto_secretbox` authenticated encryption.
```ts src/crypto.ts theme={null}
export function encrypt(
plaintext: Uint8Array,
key: Uint8Array
): { ciphertext: Uint8Array; nonce: Uint8Array } {
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
const ciphertext = sodium.crypto_secretbox_easy(plaintext, nonce, key);
return { ciphertext, nonce };
}
export function decrypt(
ciphertext: Uint8Array,
nonce: Uint8Array,
key: Uint8Array
): Uint8Array {
return sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);
}
```
Each file gets its own random document key. The document key encrypts the file. The wallet key encrypts the document key so the owner can recover it later. Everything travels inside one JSON manifest.
```ts src/encrypted-cascade.ts theme={null}
import { encrypt, deriveKeyFromWallet } from "./crypto";
export async function uploadEncrypted(
client: any,
file: Uint8Array,
fileName: string,
walletKey: Uint8Array
): Promise<{ actionId: string; documentKey: string }> {
// Generate a random document key (not the wallet key)
const documentKey = sodium.crypto_secretbox_keygen();
// Encrypt the file with the document key
const { ciphertext, nonce } = encrypt(file, documentKey);
// Build a manifest with metadata + encrypted content
const manifest = {
version: 1,
fileName,
encrypted: true,
nonce: sodium.to_base64(nonce),
ciphertext: sodium.to_base64(ciphertext),
// Encrypt the document key with the wallet key for recovery
encryptedDocumentKey: sodium.to_base64(
encrypt(documentKey, walletKey).ciphertext
),
encryptedDocumentKeyNonce: sodium.to_base64(
encrypt(documentKey, walletKey).nonce
),
};
const manifestBytes = new TextEncoder().encode(JSON.stringify(manifest));
const uploader = client.Cascade.uploader;
const prepared = await uploader.prepareFile(manifestBytes);
const { actionId, authSignature } = await uploader.registerAction(prepared, {
fileName: `${fileName}.encrypted.json`,
isPublic: true, // The encryption handles confidentiality
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
});
await uploader.sendFileToSupernodes(actionId, authSignature, prepared.fileBytes, {
taskOptions: { pollInterval: 2000, timeout: 300000 },
});
return {
actionId,
documentKey: sodium.to_base64(documentKey),
};
}
```
Files are uploaded as `isPublic: true` even when encrypted. The `isPublic` flag controls access at the SuperNode API level, but client-side encryption is the real confidentiality mechanism. This keeps the encrypted blob available to any collaborator you share the key with.
Download the manifest, recover the document key with the wallet key, then decrypt the file.
```ts src/encrypted-cascade.ts theme={null}
export async function downloadDecrypted(
client: any,
actionId: string,
walletKey: Uint8Array
): Promise<{ plaintext: Uint8Array; fileName: string }> {
// Download the encrypted manifest
const stream = await client.Cascade.downloader.download(actionId);
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) chunks.push(value);
}
const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
const bytes = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
bytes.set(chunk, offset);
offset += chunk.length;
}
const manifest = JSON.parse(new TextDecoder().decode(bytes));
// Recover the document key using the wallet key
const documentKey = decrypt(
sodium.from_base64(manifest.encryptedDocumentKey),
sodium.from_base64(manifest.encryptedDocumentKeyNonce),
walletKey
);
// Decrypt the file with the document key
const plaintext = decrypt(
sodium.from_base64(manifest.ciphertext),
sodium.from_base64(manifest.nonce),
documentKey
);
return { plaintext, fileName: manifest.fileName };
}
```
To share an encrypted file, re-encrypt the document key under the collaborator's wallet-derived key.
```ts theme={null}
// Owner creates a share invitation
// collaboratorKey is the collaborator's wallet-derived key
const { ciphertext, nonce } = encrypt(documentKey, collaboratorKey);
const invitation = {
draftId: actionId,
encryptedDocumentKey: sodium.to_base64(ciphertext),
nonce: sodium.to_base64(nonce),
};
// Upload the invitation to Cascade
await client.Cascade.uploader.uploadFile(
new TextEncoder().encode(JSON.stringify(invitation)),
{
fileName: `invitation_${collaboratorAddress}_${actionId}.json`,
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
}
);
```
The collaborator downloads the invitation, decrypts the document key with their wallet, and uses it to decrypt the file.
For a complete implementation of this pattern, see the [Research Archive example](/cascade/guides/research-archive).
## Next steps
See this encryption pattern inside a full application.
Follow an upload from registration to completion.
Look up the uploadFile and download options.
# Use the SDK in Node.js
Source: https://docs.lumera.io/cascade/guides/nodejs
Run the Lumera JavaScript SDK on a server with a programmatic signer.
The JavaScript SDK for Lumera Protocol works in both browsers and Node.js. This guide covers the Node.js setup for server-side applications, CLI tools, and backend services. In this environment a programmatic `DirectSecp256k1HdWallet` signer replaces browser wallet extensions.
You build two small scripts. One uploads a file to Cascade and prints its action ID, the permanent on-chain reference for the file. The other downloads the file again.
## What you need
* [Node.js](https://nodejs.org/) 18 or later
* A testnet account mnemonic with LUME for fees. Request tokens from the [faucet](/faucet).
## Build the scripts
```bash npm theme={null}
npm install @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate @cosmjs/amino
```
```bash yarn theme={null}
yarn add @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate @cosmjs/amino
```
```bash pnpm theme={null}
pnpm add @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate @cosmjs/amino
```
The scripts below use top-level await, so set `"type": "module"` in your `package.json`.
In Node.js there is no browser wallet. Use `DirectSecp256k1HdWallet` for transaction signing and build a custom signer that adds `signArbitrary` (ADR-036) for Cascade's off-chain signatures.
```ts signer.ts theme={null}
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { Secp256k1HdWallet, makeSignDoc as makeAminoSignDoc } from "@cosmjs/amino";
export async function createNodeSigner(mnemonic: string) {
const directWallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "lumera" });
const aminoWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "lumera" });
const [account] = await directWallet.getAccounts();
const signer = {
getAccounts: () => directWallet.getAccounts(),
signDirect: (addr: string, doc: any) => directWallet.signDirect(addr, doc),
signAmino: (addr: string, doc: any) => aminoWallet.signAmino(addr, doc),
async signArbitrary(chainId: string, signerAddress: string, data: string) {
const signDoc = makeAminoSignDoc(
[{
type: "sign/MsgSignData",
value: {
signer: signerAddress,
data: Buffer.from(data).toString("base64"),
},
}],
{ gas: "0", amount: [] },
"",
"",
0,
0
);
const { signature } = await aminoWallet.signAmino(signerAddress, signDoc);
return { signed: data, signature: signature.signature, pub_key: signature.pub_key };
},
};
return { signer, account };
}
```
The scripts read the account mnemonic from the `LUMERA_MNEMONIC` environment variable.
```bash theme={null}
export LUMERA_MNEMONIC="your twelve word mnemonic goes here"
```
Treat the mnemonic like a private key. Load it from an environment variable or a secrets manager and keep it out of source control.
The upload registers an action on chain, escrows the storage fee, and transfers the file to a SuperNode. The optional `expirationTime` sets a deadline for the upload. If no SuperNode finalizes the action by then, it becomes `EXPIRED` and the escrowed fee is refunded.
```ts upload.ts theme={null}
import fs from "node:fs";
import { createLumeraClient } from "@lumera-protocol/sdk-js";
import { createNodeSigner } from "./signer";
const { signer, account } = await createNodeSigner(process.env.LUMERA_MNEMONIC!);
const client = await createLumeraClient({
preset: "testnet",
signer: signer as any,
address: account.address,
gasPrice: "0.025ulume",
});
const file = await fs.promises.readFile("./my-document.pdf");
const expirationTime = String(Math.floor(Date.now() / 1000) + 25 * 60 * 60); // 25 hours
const uploader = client.Cascade.uploader;
const prepared = await uploader.prepareFile(new Uint8Array(file));
const { actionId, authSignature } = await uploader.registerAction(prepared, {
fileName: "my-document.pdf",
isPublic: true,
expirationTime,
});
await uploader.sendFileToSupernodes(actionId, authSignature, prepared.fileBytes, {
taskOptions: { pollInterval: 2000, timeout: 300000 },
});
console.log("Stored with action ID:", actionId);
```
Downloads arrive as a stream of chunks. Reassemble them and write the result to disk.
```ts download.ts theme={null}
import fs from "node:fs";
import { createLumeraClient } from "@lumera-protocol/sdk-js";
import { createNodeSigner } from "./signer";
const { signer, account } = await createNodeSigner(process.env.LUMERA_MNEMONIC!);
const client = await createLumeraClient({
preset: "testnet",
signer: signer as any,
address: account.address,
gasPrice: "0.025ulume",
});
const stream = await client.Cascade.downloader.download("your-action-id");
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) chunks.push(value);
}
const fileBytes = new Uint8Array(chunks.reduce((acc, c) => acc + c.length, 0));
let offset = 0;
for (const chunk of chunks) {
fileBytes.set(chunk, offset);
offset += chunk.length;
}
await fs.promises.writeFile("./downloaded-file.pdf", fileBytes);
```
## Server-side alternatives
If you prefer Go on the server, the [Go SDK](/sdk/go) covers the same Cascade upload and download operations.
## Next steps
Run the same flows in the browser with Keplr.
Look up every client method and option.
Handle failed uploads, timeouts, and retries.
# Research Archive example app
Source: https://docs.lumera.io/cascade/guides/research-archive
Explore a decentralized academic publishing platform built on Cascade.
The [Lumera Research Archive](https://github.com/kaleababayneh/Lumera-Research-Archive) is an open source decentralized academic publishing platform. It is a production grade example of integrating Cascade, the permanent storage service of Lumera Protocol. This page walks through its architecture so you can reuse the same patterns in your own app.
The app supports four things.
* Public paper publishing with permanent archival on Cascade
* Encrypted private drafts with wallet-based XChaCha20-Poly1305 encryption
* Secure collaboration through wallet-derived key exchange
* Paper discovery through LumeScope, a read-only REST aggregator that indexes Cascade actions
To run it locally you need Node.js 18 or later, the Keplr extension, and testnet LUME from the [faucet](/faucet).
## Tech stack
| Component | Technology |
| - | - |
| Runtime | Vite (vanilla TypeScript SPA) |
| Blockchain SDK | `@lumera-protocol/sdk-js` v0.2.7 |
| Cosmos | `@cosmjs/proto-signing`, `@cosmjs/stargate` |
| Encryption | `libsodium-wrappers-sumo` (XChaCha20-Poly1305) |
| Wallet | Keplr browser extension |
| Indexer | LumeScope API |
| Chain | `lumera-testnet-2` |
## Architecture
The key design decision is that there is no backend server. The entire application runs in the browser. It talks to the Lumera blockchain over RPC for transactions and to the SuperNode API for file storage.
## Three file types on Cascade
The app stores three distinct file types on Cascade and tells them apart by filename conventions.
### Published papers
Public, unencrypted academic papers permanently archived on Cascade.
```ts theme={null}
const manifest = {
title: "My Research Paper",
abstract: "A study on...",
authors: ["lumera1abc..."],
keywords: ["blockchain", "storage"],
content: btoa(paperContent), // Base64-encoded plaintext
publishedAt: new Date().toISOString(),
};
const result = await client.Cascade.uploader.uploadFile(
new TextEncoder().encode(JSON.stringify(manifest)),
{
fileName: `${manifest.title}.json`,
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
taskOptions: { pollInterval: 2000, timeout: 300000 },
}
);
```
### Encrypted drafts
Private drafts encrypted with a per-document key. The title stays readable as public metadata.
```ts theme={null}
const manifest = {
type: "draft",
draftId: crypto.randomUUID(),
title: "Work in Progress", // Public metadata (not encrypted)
version: 1,
encrypted: true,
nonce: sodium.to_base64(nonce),
ciphertext: sodium.to_base64(encryptedContent),
encryptedDocumentKey: sodium.to_base64(encKeyForOwner),
keyNonce: sodium.to_base64(keyNonce),
};
await client.Cascade.uploader.uploadFile(manifestBytes, {
fileName: `draft_${draftId}_v${version}.json`,
isPublic: true, // Encrypted, so publicly accessible but unreadable
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
});
```
### Collaboration invitations
Key-exchange files that grant draft access to collaborators.
```ts theme={null}
const invitation = {
type: "invitation",
draftId: draftId,
from: ownerAddress,
to: collaboratorAddress,
encryptedDocumentKey: sodium.to_base64(reEncryptedKey),
nonce: sodium.to_base64(inviteNonce),
};
await client.Cascade.uploader.uploadFile(invitationBytes, {
fileName: `invitation_${collaboratorAddress}_${draftId}.json`,
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
});
```
## Discovery with LumeScope
The app uses [LumeScope](/api/lumescope) to discover files without scanning the entire blockchain.
```ts theme={null}
// Fetch all actions by a specific creator
const response = await fetch(
`${LUMESCOPE_API}/v1/actions?creator=${address}&limit=10000&type=ACTION_TYPE_CASCADE`
);
const actions = await response.json();
// Filter by state and filename pattern
const papers = actions.filter(
(a) => a.state === "ACTION_STATE_DONE" && !a.fileName?.startsWith("draft_")
);
```
## Collaboration flow
Sharing a draft moves a wrapped document key from the owner to the collaborator.
The app link carries key material in the URL hash fragment (`#key=...`). Browsers never send the hash fragment to servers, so it stays on the client. That makes the link safe to share over any messaging channel.
## Local state
The app uses `localStorage` for three things.
* Draft metadata (IDs, titles, versions)
* Encrypted document keys wrapped with the wallet-derived key
* Collaboration invitations
Cascade is the source of truth for file content. `localStorage` only caches keys and metadata.
## Source code
Browse the full implementation at [github.com/kaleababayneh/Lumera-Research-Archive](https://github.com/kaleababayneh/Lumera-Research-Archive).
## Next steps
Implement the wallet-derived encryption pattern used for drafts.
Start your own Vite and Keplr app from scratch.
Look up every SDK method the archive calls.
# How Cascade works
Source: https://docs.lumera.io/cascade/how-cascade-works
Follow an upload from on-chain registration to permanent storage across the SuperNode network.
Cascade is the permanent storage service of Lumera Protocol. You pay one fee at upload time, and the network stores your file for good. This page follows one upload from start to finish, so you know what the SDK does on your behalf.
## Architecture at a glance
Cascade splits the work between two planes. The control plane is the Lumera blockchain. It records every stored file as an action and holds the storage fee. The data plane is the SuperNode network. It encodes, stores, and serves the actual bytes. The SDK drives both, so your app makes a single function call.
### Which layer you integrate against
Two layers reach Cascade, and they are not the same API.
1. **Application path.** The [JavaScript](/sdk/javascript) and [Rust](/sdk/rust) SDKs call [SN-API](/api/sn-api), the HTTP gateway in front of the SuperNode network. This is the path app and browser code should take.
2. **Direct SuperNode path.** [`CascadeService`](/api/grpc/cascade-service) gRPC streaming on a SuperNode, used by the [Go SDK](/sdk/go) and operator tooling. It needs outbound access to the node's gRPC port.
Use SN-API for app-facing upload and download. Reach for direct gRPC only when you are integrating with a SuperNode itself.
## The upload flow
The SDK prepares the file before it touches the chain.
1. It computes a BLAKE3 hash of the file. This `data_hash` becomes the permanent content identifier stored on chain.
2. It generates a RaptorQ layout. The layout is metadata that describes how the file will be encoded, not the encoding itself.
3. It signs the layout, derives deterministic layout IDs, and builds a signed index file.
The SDK then submits a `MsgRequestAction` transaction to the Lumera chain. The message carries the `data_hash`, the file name, a visibility flag for public or private access, and the one-time storage fee in `ulume`.
The chain validates the transaction, escrows the fee, and emits an `action_registered` event that contains the `action_id`. The SDK extracts the ID from the transaction response. The `action_id` is the permanent on-chain reference for your file.
The SDK sends a multipart POST to `/api/v1/actions/cascade` on SN-API, the REST gateway in front of the SuperNode network. The request carries three fields.
| Field | Contents |
| - | - |
| `action_id` | The on-chain reference from the previous step |
| `signature` | An ADR-036 signature of the `data_hash` |
| `file` | The raw file bytes |
ADR-036 is the Cosmos standard for signing arbitrary data offline. Cascade uses it to prove you control the uploading wallet without an extra transaction. SN-API returns a `task_id` that the SDK polls for progress.
The SuperNode that received the file does three things.
1. It verifies that the action exists on chain, the fee is valid, the BLAKE3 hash matches, and all signatures are correct.
2. It encodes the file with RaptorQ erasure coding. RaptorQ produces redundant symbols, and any sufficiently large subset of them can rebuild the file.
3. It distributes the encoded symbols across the SuperNode mesh, so no single node becomes a point of failure.
After encoding and distribution complete, the SuperNode submits a `MsgFinalizeAction` transaction back to the chain. The action state moves from `PENDING` through `PROCESSING` to `DONE`. If encoding or distribution fails, the state becomes `FAILED` instead. On success, the chain records the file as stored and retrievable, and the escrowed fee is released to the participating SuperNodes.
## Paid once
Cascade charges one storage fee when the action registers, and never bills again. The current mainnet fee is 30,000 `ulume` base plus 10 `ulume` per KB. A 1 MB file costs about 0.04 LUME. Testnet uses a 10,000 `ulume` base, so the same file costs about 0.02 LUME there. These parameters can change through governance. The protocol handles payment automatically. An upload still unfinalized at its `expirationTime` returns the escrowed fee to your wallet.
## Retrieving your file
Once the action reaches `DONE`, the file is permanently stored. To retrieve it, the SDK signs the `action_id` with an ADR-036 signature, authenticates with the SuperNode network, and streams the reconstructed file back to your app. See [Download lifecycle](/cascade/concepts/download-lifecycle) for the full flow.
## Next steps
Upload your first file in under five minutes.
Install the JavaScript and Go SDKs.
See every phase of an upload in detail.
# Install the Cascade SDKs
Source: https://docs.lumera.io/cascade/installation
Install the JavaScript and Go SDKs and prepare your bundler for the browser.
Lumera Protocol ships official SDKs for JavaScript and Go. This page installs both, explains the peer dependencies, and covers the bundler configuration that browser apps sometimes need.
## Requirements
| SDK | Requirement |
| - | - |
| JavaScript | Node.js 18 or later |
| Go | Go 1.26 |
You also need two things before you upload.
* A Lumera wallet. Use a mnemonic for programmatic signing, or [Keplr](https://www.keplr.app/) in the browser.
* Testnet `ulume` from the [faucet](/faucet).
## Install the JavaScript SDK
The JavaScript and TypeScript SDK is the main way to use Cascade from web and Node.js applications.
```bash npm theme={null}
npm install @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
```
```bash yarn theme={null}
yarn add @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
```
```bash pnpm theme={null}
pnpm add @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
```
`@cosmjs/proto-signing` and `@cosmjs/stargate` are peer dependencies. The SDK uses them for wallet and transaction handling. For a full walkthrough, see [JavaScript SDK](/sdk/javascript).
## Browser bundler configuration
If you use Vite, you may need polyfills for Node.js globals.
```bash npm theme={null}
npm install vite-plugin-node-polyfills vite-plugin-wasm vite-plugin-top-level-await
```
```bash yarn theme={null}
yarn add vite-plugin-node-polyfills vite-plugin-wasm vite-plugin-top-level-await
```
```bash pnpm theme={null}
pnpm add vite-plugin-node-polyfills vite-plugin-wasm vite-plugin-top-level-await
```
```ts vite.config.ts theme={null}
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
export default defineConfig({
plugins: [
wasm(),
topLevelAwait(),
nodePolyfills({
include: ["buffer", "process"],
globals: { Buffer: true, global: true, process: true },
}),
],
optimizeDeps: {
exclude: ["undici", "@bokuweb/zstd-wasm"],
},
define: {
"process.env": "{}",
},
});
```
For webpack in Next.js, add fallbacks in `next.config.js`.
```js next.config.js theme={null}
/** @type {import('next').NextConfig} */
module.exports = {
webpack: (config) => {
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
path: false,
crypto: false,
};
return config;
},
};
```
## Install the Go SDK
The Go SDK targets server-side applications, CLI tools, and backend services. It queries the chain over gRPC and connects to SuperNodes directly. Add it inside a Go module.
```bash theme={null}
# Initialize a module if you don't have one yet
go mod init your-project
# Add the SDK
go get github.com/LumeraProtocol/sdk-go@latest
```
For a full walkthrough, see [Go SDK](/sdk/go).
## Verify the installation
Follow the [Quickstart](/quickstart) to confirm everything works. It creates a client, connects to testnet, and uploads a file in under 50 lines.
## Next steps
Create a client with presets, custom endpoints, and a signer.
Walk through the JavaScript SDK from setup to upload.
Build server-side uploads with the Go SDK.
# Cascade
Source: https://docs.lumera.io/cascade/overview
The technical model behind Cascade: content addressing, RaptorQ fountain coding, availability commitments, storage challenges, and self-healing.
Cascade is the Protocol's permanent storage service. A file is hashed, erasure-coded into a redundant symbol set, and distributed across the SuperNode mesh. Reconstruction requires only a subset of those symbols, so availability does not depend on any particular operator staying online.
This page covers the storage model and the mechanisms underneath it. The end-to-end request flow is in [How Cascade works](/cascade/how-cascade-works), and LEP-1 layout ID derivation is in [RaptorQ erasure coding](/cascade/concepts/erasure-coding).
## The storage model
Cascade is content-addressed and immutable. An object is identified by what it contains rather than where it sits, and it is never modified in place.
| Identifier | Derived from | Purpose |
| - | - | - |
| `data_hash` | BLAKE3 of the file bytes | Permanent content identifier, recorded on chain |
| `action_id` | The registering transaction | On-chain handle for the stored object |
| Layout IDs | Deterministic derivation over the compacted layout, signature, and counter | Globally unique symbol identifiers the chain can verify independently |
| Merkle root | BLAKE3 tree over fixed-size file chunks | Binds the uploader to the file's exact contents |
Integrity is intrinsic. A retrieved file either hashes to the recorded `data_hash` or it does not, and that check is a local computation over bytes you already hold.
Immutability means there is no edit, rename, or delete. Storing a modified file produces a new object with a new hash. Applications that need mutable pointers hold those pointers themselves.
## Two partitions, two purposes
Cascade divides a file twice, for unrelated reasons. Conflating them is the most common source of confusion.
| Partition | Unit | Size | Used for |
| - | - | - | - |
| RaptorQ encoding | Symbol | Uniform symbol size `T` | Redundancy and reconstruction |
| Availability commitment | Chunk | Power of two, up to 256 KiB | Proving an operator holds the file |
Symbols exist so the file survives node loss. Chunks exist so an operator can be challenged to prove possession. The commitment is built over chunks of the original file rather than over symbols, because chunk hashing needs only BLAKE3 over raw bytes and avoids depending on bit-identical RaptorQ library versions across client and SuperNode.
## RaptorQ
Cascade encodes with RaptorQ, the fountain code standardized in [RFC 6330](https://www.rfc-editor.org/rfc/rfc6330).
| Term | Meaning |
| - | - |
| Source block | A unit the encoder operates over. The SDK's `createSingleBlockLayout` produces a single-block layout |
| Source symbol | One of K fixed-size pieces of the source block |
| Symbol size (`T`) | Byte length of every symbol, uniform across the block |
| `K'` | K padded up to the nearest supported value from the RFC's table |
| Repair symbol | Any encoding symbol beyond the source symbols. Supply is effectively unbounded |
| ESI | Encoding Symbol Identifier, distinguishing symbols within a block |
| SBN | Source Block Number, distinguishing blocks within a file |
RaptorQ is **systematic**, so the first K encoding symbols are the source symbols themselves. A decoder that receives exactly those does no reconstruction work.
## Why a fountain code
Fountain codes are **rateless**. A Reed-Solomon style code fixes N and K at encode time. A fountain code fixes only K and then emits as many encoding symbols as asked, up to 2^24 - 1 per source block under RFC 6330.
Ratelessness is what makes permanence tractable. Redundancy is not a decision frozen at upload. If symbols are lost as operators leave, more can be generated from any recovered source block, without the original uploader and without a pristine copy existing anywhere. That property is the foundation the repair machinery is built on.
## LT codes, Raptor, RaptorQ
RaptorQ is the third generation of one idea, and each generation fixed a defect in the last.
**LT codes** were the first practical fountain codes. Each encoding symbol is the XOR of some number of randomly chosen source symbols, with that number drawn from a tuned degree distribution. Decoding peels the graph. Find a degree-one symbol, which reveals a source symbol directly, substitute it into every symbol referencing it, repeat. It works, but peeling stalls unless the distribution is tuned aggressively, recovering the last few symbols is expensive, and decoding cost grows as O(K log K).
**Raptor codes** added a pre-code. The source block is first expanded into a larger set of intermediate symbols using an outer erasure code. The LT stage then only needs to recover *most* intermediate symbols, because the pre-code reconstructs the rest. That relaxation buys linear-time decoding and low overhead.
**RaptorQ** strengthens the pre-code and widens the arithmetic. Its constraints combine a low-density parity-check component over GF(2), which is plain XOR and fast, with a high-density component over GF(256). Working in a larger field for that portion sharply reduces the chance of rank deficiency during decoding, at an arithmetic cost paid on only a small part of the matrix. RaptorQ also supports much larger source blocks and defines sub-blocking so decoding memory can be bounded independently of block size.
The result is the reception overhead.
| Symbols received | Approximate decode probability |
| - | - |
| `K'` | 99% |
| `K'` + 1 | 99.99% |
| `K'` + 2 | 99.9999% |
Two symbols of slack over the theoretical minimum puts failure near one in a million.
## Distribution
Encoding creates redundancy. Distribution converts it into fault tolerance.
Symbols are spread across the SuperNode mesh and stored in a Kademlia DHT, with placement following the keyspace rather than the uploader's choice. No operator holds a complete copy.
This is why the guarantee is a threshold rather than a replica count. Reconstruction needs any sufficiently large subset of symbols, not particular symbols from particular operators.
## Durability versus replication
Replication is intuitive and wasteful. Surviving the loss of half the network with whole copies costs roughly 2x the stored bytes. Erasure coding reaches comparable reliability at roughly 1.5x, because redundancy is spread across the symbol space instead of duplicated wholesale. The advantage grows with file size.
## Reconstruction
Retrieval inverts the process. Symbols are gathered from responsive SuperNodes, the decoder rebuilds the source block once it holds enough, and the result is hashed with BLAKE3 and compared against the on-chain `data_hash`.
Verification is end-to-end and client-side. A corrupted or substituted symbol set cannot produce bytes matching the recorded hash. Access control for private objects is enforced separately by signature, covered in [Download lifecycle](/cascade/concepts/download-lifecycle).
## Proving possession at finalization
Erasure coding says nothing about whether an operator actually received the file. Because everything needed to compute valid layout IDs is derivable from on-chain data, an operator could once have finalized an action and collected fees without storing anything.
**LEP-5, the Cascade Availability Commitment, closes that gap.** It is implemented.
At registration the client splits the file into fixed-size chunks, hashes each with domain separation, builds a Merkle tree, and commits the root together with a set of challenge indices.
```text theme={null}
leaf_i = BLAKE3(0x00 || uint32be(i) || chunk_i)
parent = BLAKE3(0x01 || left || right)
```
Domain separation prevents a leaf from being confused with an internal node. Odd levels duplicate the final node.
Chunking is constrained by the chain. Chunk size is a power of two up to 256 KiB, defaulting to 256 KiB for files of 1 MiB or more, reduced for smaller files so that every commitment yields at least four chunks. Minimum file size is 4 bytes. The number of challenged chunks is `min(svc_challenge_count, num_chunks)`, which lands between four and eight.
At finalization the SuperNode rebuilds the tree from the file it received, confirms the root matches the on-chain commitment, and submits Merkle proofs for exactly the challenged indices. The chain verifies each proof against the stored root before moving the action to `DONE` and releasing fees. Proof size is O(log N) per chunk.
Partial storage is caught probabilistically. An operator holding fraction `p` of chunks evades `m` challenges with probability `p^m`.
| Chunks held | `m` = 5 | `m` = 8 |
| - | - | - |
| 90% | 59% | 43% |
| 80% | 33% | 17% |
| 50% | 3.1% | 0.39% |
At the mainnet default of eight challenges, holding less than half the file has under a 0.4% chance of passing a single round.
## Ongoing challenges and self-healing
LEP-5 proves possession once, at finalization. Retention over time is enforced separately, by periodic storage challenges.
The current mechanism is epoch-based. Challengers, recipients, and observers are selected deterministically, a challenged operator returns a hash over a byte range of a stored artifact, and observers reach quorum on the result. Evidence is batched into epoch health reports rather than submitted as individual transactions. Repair already exists as well, gated by request, verify, and commit, with reconstructed content validated against the expected hash before it is persisted.
**LEP-6, Storage-Truth Enforcement and Ticket-Driven Self-Healing, is approved** and upgrades both halves. The runtime ships in SuperNode v2.6.0 and later, gated by operator config and by the `x/audit` parameter `storage_truth_enforcement_mode`, which disables every LEP-6 service while it is `UNSPECIFIED`. Its substantive changes are worth knowing because they define the direction of the integrity model.
* One third of active SuperNodes are challenged per epoch, deterministically selected, keeping traffic bounded.
* Each challenged operator receives one compound challenge containing a **recent-ticket** and an **old-ticket** subchallenge, so ingestion and long-term retention are tested separately.
* Byte sampling moves from a fixed 1 KiB prefix to four deterministic 256-byte ranges, which removes the value of optimizing for the start of a file.
* Outcomes update three distinct on-chain scores. **Node suspicion** drives audit enforcement. **Ticket deterioration** drives repair scheduling. **Reporter reliability** penalizes operators whose reports diverge from the network consensus.
* Repair is scheduled from ticket deterioration rather than an indirect watchlist, assigned to a single deterministic healer, and accepted only after independent verifiers confirm the healer actually restored the data, followed by a probation window.
Separating node suspicion from ticket deterioration is the key design point. One question is whether an operator should be penalized. The other is whether a piece of data needs repair. They are not the same question and they no longer share a signal.
Track status in the [LEP repository](https://github.com/LumeraProtocol/lumera/tree/master/docs/leps).
## Retention economics
Challenges establish whether data is held. They do not explain why an operator keeps holding it after the upload fee is consumed.
That is [Everlight](/cascade/everlight/overview). Payouts are proportional to retained Cascade bytes, reported through LEP-4 node metrics as `cascade_kademlia_db_bytes`. Because that metric is self-reported, it passes through guardrails before becoming payout weight, including a smoothing window across periods, a growth cap, and a ramp-up for new operators.
The two mechanisms are complementary and worth keeping distinct. Everlight decides compensation from reported volume. Storage challenges establish truth and carry consequences. See [Everlight for SuperNode operators](/cascade/everlight/supernode-operators).
## Properties and limits
* **Immutable and content-addressed.** No in-place edit, delete, or rename.
* **Opaque bytes.** The network does not parse, index, or interpret content. Filenames and schemas are not searchable.
* **No retrieval by meaning.** Objects are fetched by address. Semantic recall belongs above the Protocol.
* **Private by default.** Private objects require a signature resolving to a permitted identity. For sensitive data, encrypt before upload so the network provably retains a record it cannot read. See [Store encrypted files on Cascade](/cascade/guides/encrypted-storage).
* **Asynchronous write path.** Registration is a transaction, but encoding, distribution, and proof submission complete afterward, moving the action through `PENDING` and `PROCESSING` before `DONE`.
* **Minimum file size is 4 bytes**, a consequence of the four-chunk floor on availability commitments.
The end-to-end upload and registration flow.
LEP-1 layouts and deterministic ID derivation.
How long-term retention is funded.
The RaptorQ library SuperNodes encode with.
# Interchain accounts in practice
Source: https://docs.lumera.io/cross-chain/interchain-accounts
Run the full ICS-27 Cascade flow from a controller chain with the Go SDK.
This page is the hands-on runbook for driving Cascade over ICS-27. You register an interchain account on Lumera, fund it, send a packed `MsgRequestAction` across IBC, upload the file, and approve the finished action. The theory behind the flow, including why file bytes never touch the IBC channel, is on the [interchain accounts concept page](/cascade/concepts/interchain-accounts). Read it first if ICS-27 is new to you.
Before you start you need three things.
* The [Go SDK](/sdk/go) installed. Its `ica` package assembles the packets.
* An IBC connection between your controller chain and Lumera with a running relayer. See [relayer setup](/cross-chain/relayer-setup).
* A funded key on the controller chain.
The [lumera-ica-client](https://github.com/LumeraProtocol/lumera-ica-client) reference CLI wraps this whole flow. Run it first if you want to see a working end to end example before writing code.
## Step 1. Register the interchain account
Your controller chain's ICA controller module opens a channel to Lumera and creates the account. On CosmWasm chains a contract can own the account through [cw-ica-controller](https://github.com/srdtrk/cw-ica-controller). Once the channel handshake completes, query the controller module for the host address. It is a normal `lumera1...` address, controlled only by your chain.
## Step 2. Fund the ICA address
The ICA address pays the fee escrow for every action it registers. Send `ulume` to it before you submit anything. Current mainnet fees are 30,000 `ulume` base plus 10 `ulume` per KB, about 0.04 LUME for a 1 MB file. Testnet uses a 10,000 `ulume` base, about 0.02 LUME for the same file. Governance can change these parameters.
You can top the address up from any Lumera account, or over IBC from a treasury on the controller chain. On testnet, the [faucet](/faucet) covers experimentation.
An underfunded ICA address makes registrations fail on the host side. Monitor its balance and alert before it runs out.
## Step 3. Build and pack the MsgRequestAction
Create a Cascade client configured with the ICA owner settings. `ICAOwnerHRP` is the bech32 prefix of your controller chain.
```go theme={null}
import "github.com/LumeraProtocol/sdk-go/cascade"
cascadeClient, err := cascade.New(ctx, cascade.Config{
ChainID: "lumera-testnet-2",
GRPCAddr: "grpc.testnet.lumera.io:443",
Address: hostAddr,
KeyName: hostKeyName,
ICAOwnerKeyName: controllerKeyName,
ICAOwnerHRP: "cosmos", // bech32 prefix of the controller chain
Timeout: 30 * time.Second,
}, kr)
```
Build the action message without broadcasting it, then pack it for ICA delivery.
```go theme={null}
import "github.com/LumeraProtocol/sdk-go/ica"
// Build a MsgRequestAction for the ICA address without broadcasting it
msg, err := cascadeClient.CreateRequestActionMessage(ctx, icaAddr, filePath, opts)
// Pack it into protobuf Any bytes
packedMsg := ica.PackRequestForICA(msg)
// Wrap it into InterchainAccountPacketData for EXECUTE_TX
packetData := ica.BuildICAPacketData(packedMsg)
```
## Step 4. Send the packet from the controller chain
Submit the packet with `MsgSendTx` on the controller chain. `BuildMsgSendTx` assembles it for you, and your controller chain signer broadcasts it. You can also submit the same packet data through your controller chain's CLI if you prefer scripts.
```go theme={null}
// Build the controller side MsgSendTx, then broadcast it with your controller chain signer
sendTx := ica.BuildMsgSendTx(owner, connectionID, packetData)
```
The relayer delivers the packet. Lumera's ICA host module unpacks the `MsgRequestAction` and executes it, and the ICA address pays the escrow.
## Step 5. Extract the action ID from the acknowledgement
The IBC acknowledgement carries the registration result back to the controller chain.
```go theme={null}
actionIDs := ica.ExtractRequestActionIDsFromAck(ackData)
```
If you drive the flow from scripts, the `ica` package also ships `ParseTxHashJSON`, `ExtractPacketInfoFromTxJSON`, and `DecodePacketAcknowledgementJSON` for decoding CLI transaction output.
## Step 6. Upload the file
The ICA address has no user-held private key, so uploads authenticate with an application level keypair instead. Generate the keypair on the controller side. The public key travels with the action as `app_pubkey`, and the private key signs the upload. Pass the ICA options to `Upload`.
```go theme={null}
res, err := cascadeClient.Upload(ctx, addr, nil, filePath,
cascade.WithICACreatorAddress(icaAddr),
cascade.WithAppPubkey(pubkey),
cascade.WithICASendFunc(sendFunc),
)
```
`WithICASendFunc` takes the callback the SDK invokes when it needs to send an ICA message from your controller chain. The file itself streams over gRPC directly to SuperNodes. It never crosses the IBC channel.
Track progress with `SubscribeToEvents`, or poll `GetAction` until the state moves from `PENDING` through `PROCESSING` to `DONE`. An action still unfinalized at its `expirationTime` becomes `EXPIRED` and the escrowed fee is refunded.
## Step 7. Download the file
Downloads are authenticated too. Sign the request with a controller chain key by passing the signer address.
```go theme={null}
_, err := cascadeClient.Download(ctx, actionID, destDir,
cascade.WithDownloadSignerAddress(controllerAddr),
)
```
## Step 8. Approve the action
Cross-chain actions support a final confirmation step. After the action reaches `DONE`, build a `MsgApproveAction`, pack it, and send it through the same ICA path. The action then moves to `APPROVED`.
```go theme={null}
// Build and pack the approval, then send it in a second MsgSendTx
approveMsg, err := cascadeClient.CreateApproveActionMessage(ctx, actionID)
packedApprove := ica.PackApproveForICA(approveMsg)
```
Wait for the acknowledgement, then query the action until its state reads `APPROVED`.
The [sdk-go examples](https://pkg.go.dev/github.com/LumeraProtocol/sdk-go/examples) contain runnable samples for the ICA request and approve flows.
## Operational notes
* **Control plane isolation.** IBC packets carry only metadata and fee escrow. File data moves on the direct gRPC data plane.
* **Data plane authentication.** Uploads and downloads use application level signatures, independent of the ICA address.
* **Relayer health.** Every step that crosses IBC waits on the relayer. A backed up relayer stalls registrations and approvals, so keep ICA writes off any user-facing hot path.
## Next steps
Run the Hermes relayer this flow depends on.
When contract driven ICA is the right choice, and when it is not.
Every ICA helper and Cascade option in one place.
# Cross-chain storage on Lumera
Source: https://docs.lumera.io/cross-chain/overview
Use Cascade as the permanent storage layer for apps on any chain.
Your application does not need to run on Lumera Protocol to use Cascade. An app on any chain can register a storage action, upload the bytes, and keep only a pointer in its own state. The pointer is the numeric `action_id`. Cascade holds the bytes permanently on the SuperNode network.
This section covers the two ways to wire that up, the relayer plumbing for Cosmos chains, and the integration patterns proven in real reference builds.
## Why cross-chain storage
Most chains keep consensus-critical state on-chain and push large supporting data to IPFS pins or cloud buckets. That data survives only as long as someone pays the hosting bill. Cascade replaces the recurring bill with a one time on-chain payment, RaptorQ erasure coding across SuperNodes, and self-healing redundancy. Your chain keeps the small state. Cascade keeps the large, citable bytes. The `action_id` ties them together.
## Two integration families
**Cosmos chains over IBC.** Lumera is IBC native. Through [ICS-27 interchain accounts](/cascade/concepts/interchain-accounts), a module or contract on your chain controls an account on Lumera and registers Cascade actions remotely. This needs an IBC channel and a running relayer, and it is the only path where a contract itself can be the signer.
**Chains without IBC.** A backend service holds a funded Lumera account and writes to Cascade on behalf of your users, through the SDKs or an API gateway. This works from any stack that can run an SDK or make an HTTP call, including EVM chains.
## Reference integrations
Two worked integrations shaped the patterns in this section.
* **Injective.** A CosmWasm chain build that exercises all three write patterns, with contracts holding `action_id` pointers to permanent receipts and evidence.
* **LUKSO.** An EVM chain build where Universal Profile metadata points at hash-bound Cascade URLs, so standard LSP readers verify the bytes without knowing Cascade exists.
## What is in this section
The full ICS-27 flow with the Go SDK, from registration to approval.
The Lumera values you plug into a Hermes chain config.
User signed, server signed, and contract driven, and when to pick each.
How interchain accounts and application keypairs work under the hood.
## Next steps
Decide who signs the Cascade write before touching any plumbing.
The storage model every pattern builds on.
# Cross-chain integration patterns
Source: https://docs.lumera.io/cross-chain/patterns
Three proven ways to write to Cascade from an app on another chain.
Every cross-chain Cascade integration answers one question. Who signs the Cascade write? This page generalizes the worked Injective and LUKSO integrations into three patterns, with the trade-offs that decide between them. Whatever you pick, the read side stays the same, because reading by `action_id` needs no signature at all.
## The action\_id pointer pattern
Cascade's unit of storage is an action. Registering one returns a numeric `action_id`, and the bytes behind it stay retrievable permanently. Your app or contract stores that ID as an opaque string. Cascade holds the bytes.
```rust A CosmWasm contract storing a Cascade pointer theme={null}
pub struct StoredArtifact {
pub cid: String, // Cascade action_id, for example "15823"
pub submitter: Addr,
pub block_height: u64,
}
```
The contract validates only that the field is non-empty. It cannot read across chains synchronously, so resolution and integrity checks happen at read time, in an indexer or frontend, off the consensus path. That single string is the entire cross-chain coupling, which is why the same pattern works from CosmWasm, Solidity, or a plain backend.
## Pattern A. User signed
The user holds their own Lumera account and signs the Cascade write directly, usually from the same wallet that signs on your chain.
**Flow.** The app signs two transactions per artifact. First a Lumera transaction uploads the file and returns an `action_id`. Then a transaction on your chain stores that `action_id` in contract or app state.
**Choose it when** provenance matters. Every artifact carries a direct on-chain link to the user's own Lumera key, and your service holds no keys at all.
**Trust assumptions.** None beyond the two chains. The cost is UX. The user needs a funded Lumera address, pays the `ulume` fee, and signs twice. On chains that use `eth_secp256k1` keys, such as Injective, the same wallet mnemonic produces different addresses on each chain, so your UI must keep the two identities distinct.
## Pattern B. Server signed
Your backend holds one funded Lumera key and uploads on behalf of users. Users interact only with your chain or app, and the Cascade write is a server side call.
**Flow.** The user submits content to your backend. The backend uploads it to Cascade with its own key, gets the `action_id`, and returns it. The user, or the backend, then records the `action_id` on your chain. You can hold the Lumera key yourself with an SDK, or forward uploads to a Cascade API gateway that holds the key and pays the fee for you.
```ts Server side upload with your own Lumera key theme={null}
import { createLumeraClient } from "@lumera-protocol/sdk-js";
// One funded Lumera key uploads on behalf of users
const client = await createLumeraClient({
preset: "testnet",
signer,
address: serverLumeraAddress,
gasPrice: "0.025ulume",
});
const uploader = client.Cascade.uploader;
const prepared = await uploader.prepareFile(file);
const { actionId, authSignature } = await uploader.registerAction(prepared, {
fileName: "artifact.json",
isPublic: true,
expirationTime: String(Math.floor(Date.now() / 1000) + 25 * 60 * 60), // 25 hours
});
await uploader.sendFileToSupernodes(actionId, authSignature, prepared.fileBytes);
// actionId is the permanent reference to the stored bytes
```
**Choose it when** UX matters. One signature instead of two, no Lumera address required from users, and a single key you can monitor, quota, and rotate. This is the default in the Injective reference integration and the fastest path to shipping.
**Trust assumptions.** The operator key signs every artifact, so the Lumera signature proves your service uploaded it, not the user. If you need per-user provenance, have your contract store the `(user, action_id)` pair so the binding lives on your chain. Fund the key with `ulume`, watch its balance, and put quotas on any user-facing upload path so it cannot be drained.
**The EVM variant.** LUKSO shows Pattern B on a chain with no IBC at all. Universal Profile metadata stores a hash-bound URL pointing at a Cascade gateway. Readers fetch the bytes, recompute the hash, and reject any mismatch, so the gateway can deny service but never tamper. The same shape works on any EVM chain whose metadata standard carries a hash beside the URL.
## Pattern C. Contract driven over ICA
A contract or module on the controller chain owns an interchain account on Lumera and is itself the signer of the Cascade write.
**Flow.** The contract packs a `MsgRequestAction`, sends it across the IBC channel with `MsgSendTx`, and the interchain account on Lumera executes it and pays the escrow. The [interchain accounts page](/cross-chain/interchain-accounts) walks the full flow, including the application keypair that authenticates the file upload.
**Choose it when** the artifact must exist exactly when the state transition happens and there is genuinely no user or operator in the loop. Canonical, contract-deterministic writes are the narrow case this pattern exists for.
**Trust assumptions.** The strongest of the three. No user key, no operator key, only the contract and the IBC light clients. The price is latency and operations. The write takes an IBC round-trip of roughly 30 to 60 seconds, stalls when the relayer backs up, and requires you to keep the ICA address funded. Never put it on a user-facing hot path.
## Choosing a pattern
| | Pattern A user signed | Pattern B server signed | Pattern C contract driven |
| - | - | - | - |
| Best for | On-chain provenance to the user's key | Fast UX, most products | Canonical writes with no user in the loop |
| Who pays `ulume` | The user | Your service key | The ICA address |
| Signatures per artifact | Two, one per chain | One, on your chain | None from users |
| Key custody | None | Operator holds one Lumera key | Contract owns the ICA |
| Latency | Two confirmations | One confirmation plus an upload | IBC round-trip, 30 to 60 seconds |
| Works without IBC | Yes | Yes | No |
Start with Pattern B. Move specific artifact types to A when user provenance is a requirement, and reserve C for contract-only canonical writes where IBC latency is acceptable. The patterns are not exclusive, and many deployments use more than one, chosen per artifact type.
## Next steps
The full Pattern C runbook with the Go SDK.
The client used for Pattern A and B uploads.
Keep cross-chain artifacts private while storing them permanently.
# Hermes relayer setup for Lumera
Source: https://docs.lumera.io/cross-chain/relayer-setup
The Lumera values to plug into a standard Hermes chain configuration.
ICS-27 packets only move when a relayer carries them. This page gives you the Lumera specific values for a [Hermes](https://hermes.informal.systems) relayer serving a channel between Lumera and another Cosmos chain. It is not a general Hermes tutorial. Install Hermes, create the chain entries, and manage channels by following the official Hermes documentation, then plug in the values below.
## Lumera chain values
Add a chain entry for Lumera to your Hermes config with these values. The counterparty chain entry uses that chain's own documented values.
| Setting | Value |
| - | - |
| Chain ID | `lumera-mainnet-1` |
| RPC | `https://rpc.lumera.io` |
| gRPC | `https://grpc.lumera.io` |
| Account prefix | `lumera` |
| Gas price | `0.025ulume` |
The official mainnet RPC can be intermittent. A community alternative is `https://lumera-rpc.polkachu.com`.
| Setting | Value |
| - | - |
| Chain ID | `lumera-testnet-2` |
| RPC | `https://lumera-testnet-rpc.polkachu.com` |
| gRPC | `grpc.testnet.lumera.io:443` |
| Account prefix | `lumera` |
| Gas price | `0.025ulume` |
Fund the relayer's Lumera key with `ulume` to pay for packet delivery. The minimum gas price on Lumera is `0.025ulume`, and governance can change it.
## Key derivation depends on the network
This is the part that catches relayer operators. Lumera networks that have the EVM upgrade use Ethereum style keys, and networks that do not use standard Cosmos keys.
**Testnet, and any network after its EVM upgrade.** The Lumera chain entry needs the ethermint address type.
```toml theme={null}
address_type = { derivation = 'ethermint', proto_type = { pk_type = '/cosmos.evm.crypto.v1.ethsecp256k1.PubKey' } }
```
Add the relayer's Lumera key with the Ethereum HD path `m/44'/60'/0'/0/0`. A key added with the default Cosmos path produces the wrong address and every transaction fails.
**Mainnet today.** Mainnet runs `v1.12.0` and has not had the EVM upgrade yet. Use standard Cosmos key derivation with no `address_type` override.
When mainnet gains the EVM with its upgrade, existing mainnet relayers must re-key to the ethermint derivation. Follow the official [relayer migration guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/relayer-migration.md) and see the [EVM upgrade page](/upgrades/evm-upgrade) for operator checklists.
## Verify the setup
Once the chain entries are in place, use standard Hermes commands to health check both chains, create or restore the channel, and start relaying. Confirm the setup end to end by running the [interchain accounts flow](/cross-chain/interchain-accounts). A successful `MsgSendTx` acknowledgement proves packets move in both directions.
## Next steps
The ICS-27 flow your relayer now carries.
What changes for relayers when a network gains the EVM.
All Lumera endpoints and chain parameters.
# Ecosystem
Source: https://docs.lumera.io/ecosystem
Official applications, developer tools, infrastructure, wallets, and community resources for the Lumera Protocol.
The Lumera ecosystem brings together the official applications, developer tools, infrastructure, wallets, and community resources that power the protocol. Whether you're building decentralized applications, operating infrastructure, or participating in governance, this page serves as a central directory for the tools and resources available across the network.
## Official Applications
Official web, desktop, and mobile application for managing LUME, interacting with protocol services, and managing decentralized identity.
Network explorer, staking dashboard, governance portal, and validator monitoring for Mainnet.
Explorer, faucet, and operator dashboard for Testnet.
## Developer Tools
Official SDKs, APIs, node software, and libraries for building applications on the Lumera Protocol.
| Tool | Description |
| - | - |
| [`@lumera-protocol/sdk-js`](https://www.npmjs.com/package/@lumera-protocol/sdk-js) | TypeScript SDK for Node.js and browsers. |
| [`sdk-go`](https://pkg.go.dev/github.com/LumeraProtocol/sdk-go) | Go SDK for gRPC queries, transactions, and protocol services. |
| [`sdk-rs`](/sdk/rust) | Rust SDK for server-side applications. |
| [`lumerad`](https://github.com/LumeraProtocol/lumera) | Official chain binary and CLI for developers and node operators. |
| [`rq-go`](https://pkg.go.dev/lumera.build/rq-go) | Official Go implementation of RaptorQ (RFC 6330) erasure coding. |
| [LumeScope](/api/lumescope) | Read-only REST API for decoded protocol data, SuperNode metrics, and Action metadata. |
Start with the [JavaScript SDK](/sdk/javascript) or [Go SDK](/sdk/go). LumeScope endpoints are documented in the [API reference](/api/overview).
## Infrastructure
Infrastructure repositories and operational resources for running and supporting the Lumera Protocol.
| Resource | Description |
| - | - |
| [Validator Documentation](/validators/overview) | Operate a validator, participate in consensus, and secure the network. |
| [SuperNode Documentation](/supernodes/overview) | Provide decentralized protocol services and earn protocol-service fees. |
| [`lumera-networks`](https://github.com/LumeraProtocol/lumera-networks) | Genesis files, chain configuration, seeds, snapshots, and network resources. |
| [`supernode`](https://github.com/LumeraProtocol/supernode) | Open-source SuperNode implementation. |
| [`lumerad`](https://github.com/LumeraProtocol/lumera) | Official chain binary for validators, developers, and node operators. |
## Wallets
| Wallet | Description |
| - | - |
| Keplr | Cosmos wallet supporting native `lumera1...` addresses. |
| MetaMask | Ethereum-compatible wallet supporting Lumera's [Cosmos EVM](/smart-contracts/overview). |
See the [Hub Wallet Guide](/hub/wallet) for setup instructions.
## Community & Resources
Ask questions, get support, and connect with the Lumera community.
Browse the protocol source code, SDKs, and infrastructure repositories.
Follow protocol announcements, releases, and ecosystem updates.
## Building on Lumera
Whether you're building decentralized applications, operating infrastructure, or contributing to the protocol, every component of the Lumera ecosystem is designed around a common architectural foundation. Developers build against shared SDKs and protocol services, operators provide decentralized infrastructure through Validators and SuperNodes, and the community governs the protocol through staked LUME.
## Ready to Build?
Upload your first file and build your first application on Lumera.
Explore the JavaScript, Go, and Rust SDKs.
Understand how the protocol, SuperNodes, and execution framework work together.
# EVM Migration
Source: https://docs.lumera.io/evm-migration
Move your Lumera account to its EVM compatible address within three months of the mainnet upgrade to keep your staking.
Lumera mainnet is upgrading to full EVM compatibility. The upgrade changes how account keys derive, which means your existing address is replaced by a new one derived from the same recovery phrase.
Migration moves everything you hold at the old address to the new one. It is free, it takes a few minutes, and there is a deadline.
**The mainnet upgrade has been rescheduled. The new date is to be announced.** It was planned for block `6,715,000`. Mainnet passed that height on `v1.12.0` without upgrading, and no upgrade plan is on chain yet. Once the upgrade activates, you have three calendar months to migrate. [Network status and upgrades](/networks-status) carries the current versions and the queries behind them.
Open the EVM Migration page in the Lumera Portal once the mainnet upgrade activates.
## What happens if you do nothing
This is the part worth reading twice, because the two halves of your account behave differently.
**Your liquid balance is safe either way.** It stays at your legacy address and cannot be lost. You can still recover it after the window closes.
**Your staking is not.** Delegations, unbonding delegations, redelegations, and accrued rewards become permanently unrecoverable once the window expires. There is no recovery path and no exception process.
If you have anything staked, migrating inside the window is not optional.
## The four things to know
Zero gas. The migration transaction carries no fee, so you do not need a balance to migrate.
Your legacy address is permanently blocked once migration completes. Always use the official flow.
Everything moves in a single transaction. There is no partial state to recover from.
The window opens at the upgrade block and closes three calendar months later. Testnet has run the same window since 2 July 2026.
## Why Lumera is doing this
Lumera is adding a native Ethereum execution layer alongside the existing Cosmos layer. Both environments run on the same chain, share the same state, and share the same security.
In practice that means MetaMask compatible accounts, full Ethereum JSON-RPC, Solidity and Vyper contracts, and precompiles that let EVM contracts call Lumera staking and SuperNode logic directly. Developers deploy with Hardhat, Foundry, or Remix without learning a new SDK. Wallets, indexers, and explorers built for EVM chains integrate without custom work.
The reason is reach. Lumera is infrastructure for permanent data and decentralized services, and that depends on developers being able to build on it. Ethereum has the largest pool of contract developers and the deepest tooling of any platform. Reaching them from a Cosmos chain previously meant bridges and wrappers, which add friction for everyone. This removes it.
Nothing on the Cosmos side goes away. IBC, validators, SuperNodes, governance, and bech32 addresses all keep working exactly as they do today.
## Do you need to migrate
Everyone holding a pre-upgrade account does. What differs is the path.
| You are | What to do | Full guide |
| - | - | - |
| A token holder | Migrate through the Portal wizard, the helper scripts, or the CLI | [Migration guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/migration.md) |
| Holding many accounts | Use the bundled scripts, which add pre-flight checks and verification | [Helper scripts](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/migration-scripts.md) |
| A validator operator | Plan a maintenance window, then migrate the operator key | [Validator guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/validator-migration.md) |
| A SuperNode operator | Set `evm_key_name` and restart, or migrate first and let the daemon detect it | [SuperNode guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/supernode-migration.md) |
| A Hermes relayer operator | Re-key Hermes with the new derivation path before migrating | [Relayer guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/relayer-migration.md) |
| A node operator | Review the new EVM sections of `app.toml` | [Node config guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/node-evm-config-guide.md) |
| Connecting MetaMask | Add the Lumera network and import your migrated account | [MetaMask guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/metamask-configuration.md) |
## How to migrate
Most people should use the Portal wizard. It handles both signatures and shows you the result.
Keplr needs the coin type 60 profile alongside your existing one. You will end up with two Lumera profiles, the legacy one and the EVM one, and this is expected.
Connect with your legacy account. The page shows your legacy address, your new address, and everything currently held at the legacy address.
Confirm the destination matches what your recovery phrase derives under coin type 60. Migration is irreversible, so this is the step to slow down on.
The transaction carries two proofs, an ADR-036 signature from the legacy key and an EIP-191 signature from the new key. The wizard prompts for both. There is no fee.
Re-import your account under the EVM profile. The state panel on the migration page confirms what moved. Your balance and delegations should appear at the new address.
Prefer the command line, or migrating in bulk? The [migration guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/migration.md) covers the shell scripts and the raw `lumerad` path, including the offline four step ceremony for multisig accounts.
## What moves
Migration transfers all on-chain state in one transaction.
* Bank balances in every denomination
* Staking delegations, unbonding delegations, and redelegations
* Authz grants and feegrant allowances, as granter and as grantee
* Action records and claim records
* SuperNode registration, where applicable
* Vesting schedules, where applicable
For validators it additionally re-keys the operator address, every delegation pointing at the validator, distribution state, and any bound SuperNode.
## After you migrate
One account now works in both wallets. Keplr shows the bech32 form starting `lumera1`. MetaMask shows the hex form starting `0x`. Both point at the same funds.
To use MetaMask, add the Lumera network and import the migrated account. The [MetaMask guide](https://github.com/LumeraProtocol/lumera/blob/master/docs/evm-integration/user-guides/metamask-configuration.md) has the network values and the full walkthrough.
## Questions
No. Nothing works differently until mainnet upgrades. If you want to rehearse, testnet has run the identical flow since 2 July 2026. The testnet window closes on 2 October 2026.
The upgrade records it on chain as the `migration_end_time` parameter, three calendar months after the upgrade block time. Governance can change it. Once your network has upgraded, check it with `lumerad query evmigration params`.
No. The same phrase now derives a second address under coin type 60. That new address is your EVM compatible account, and migration moves your state to it.
Your liquid balance stays recoverable at the legacy address. Staking, delegations, unbonding, and rewards do not. They are permanently lost once the window closes, three calendar months after the upgrade.
No. The migration transaction is fee-free by design, so an empty account can still migrate.
No. The legacy account is removed from the auth module once migration completes, and the mapping is permanent. Only ever migrate through the official Portal flow or the guides linked here.
No. Only your operator key changes, from `secp256k1` to `eth_secp256k1`. Your `priv_validator_key.json` is untouched. Validators must use `MsgMigrateValidator`, which the chain requires instead of the standard claim message.
A K-of-N legacy multisig migrates to a K-of-N EVM multisig, same K and same N. The chain enforces this. The destination can do everything on the Cosmos side but cannot originate EVM transactions, so set up a separate single-key account if you need EVM DeFi access.
Yes, until the window closes. Migrating early costs nothing and removes the risk of forgetting, so there is no reason to wait.
## Get help
Ask in the support channel. Fastest route to a human.
The technical release record, new modules, and the developer view.
# Get testnet LUME
Source: https://docs.lumera.io/faucet
Request free testnet LUME from the faucet on the Lumera Portal.
Transactions on the Lumera Protocol testnet need LUME for gas, and Cascade uploads escrow a storage fee. The faucet on the Lumera Portal sends free testnet LUME to your address.
Create a wallet with Keplr, or generate a key from the command line.
```bash theme={null}
lumerad keys add my-key
```
Your address starts with `lumera1`.
Open the [Portal faucet](https://portal.testnet.lumera.io/lumera-testnet-2/faucet), paste your address, and submit the request. The community [AstroStake faucet](https://faucet.astrostake.xyz/lumera) is an alternative if the Portal faucet is empty. Faucets limit requests per wallet per day.
Open the [Lumera Portal](https://portal.testnet.lumera.io/lumera-testnet-2) and search for your address. Your balance appears on the account page once the faucet transaction lands.
Testnet LUME has no value. There is no faucet on mainnet.
## Next steps
Upload your first file to Cascade from TypeScript.
Find chain IDs, endpoints, seeds, and genesis files.
# Protocol Governance
Source: https://docs.lumera.io/governance
How the Lumera Protocol evolves through on-chain governance.
Protocol Governance allows the Lumera Protocol to evolve without relying on centralized decision-making or disruptive hard forks. Economic parameters, protocol services, software upgrades, and ecosystem funding are all governed on-chain through proposals approved by holders of staked LUME.
Governance is therefore part of the protocol itself. Rather than treating the network as static after launch, Lumera allows the community to adapt protocol parameters as technology, network usage, and economic conditions evolve.
Voting power is determined by **staked LUME**. One staked LUME represents one unit of voting power, aligning governance with the economic participants responsible for securing the network. Unstaked tokens do not participate in governance.
## What Governance Controls
* **Issuance** - the target bonded ratio, issuance bounds, and rate of change
* **Fees** - transaction fee parameters, the burn share, and distribution splits
* **Protocol Service** - service pricing, Action parameters, execution costs, and protocol-service economics
* **Ecosystem funding** - spending from the community pool
* **Protocol upgrades** - coordinated chain upgrades and module changes
* **Governance itself** - deposits, periods, and thresholds
## Governance as Protocol Evolution
Governance is the mechanism through which Lumera evolves over time. Rather than introducing separate versions of the protocol for every economic adjustment or service enhancement, protocol parameters can be modified through transparent, on-chain governance.
As new decentralized intelligent services are introduced, they inherit the same governance framework as the rest of the protocol. Economic parameters, service pricing, protocol modules, and ecosystem funding all evolve through one consistent governance process.
## Proposal Process
A proposal is submitted with an initial deposit. Anyone holding LUME can submit one.
The proposal must reach the minimum deposit threshold before it advances. Any community member can contribute to the deposit, so a proposal with support can clear the bar even if the author cannot fund it alone. If the threshold is not met before the deposit period expires, the proposal is removed and the deposit is refunded.
Staked LUME votes. Options are Yes, No, Abstain, and No With Veto.
Approved proposals are executed according to their proposal type. Parameter changes take effect automatically, while software upgrades and governance-approved actions follow their defined execution path.
Deposits are burned only when a proposal is vetoed. That puts a real cost on harmful proposals. A proposal that fails, misses quorum, or never reaches its minimum deposit refunds its depositors.
## Governance Parameters
Governance parameters are set per network and change through proposals. Query the live values on any node with this command.
```bash theme={null}
lumerad query gov params
```
| Parameter | Mainnet | Testnet |
| - | - | - |
| Minimum deposit | 1,000 LUME | 1,000 LUME |
| Minimum initial deposit ratio | 0.25% | 0.25% |
| Minimum deposit ratio | 1.0% | 1.0% |
| Maximum deposit period | 3 days (`259200s`) | 3 days (`259200s`) |
| Voting period | 4 days (`345600s`) | 3 days (`259200s`) |
| Expedited minimum deposit | 5,000 LUME | 5,000 LUME |
| Expedited voting period | 1 day (`86400s`) | 12 hours (`43200s`) |
| Expedited threshold | 66.7% | 66.7% |
| Quorum | 33.4% | 33.4% |
| Threshold | 50% | 50% |
| Veto threshold | 40% | 40% |
| Burn deposit on veto | Yes | Yes |
| Burn deposit when quorum fails | No | No |
| Burn deposit when the deposit period expires | No | No |
Values as of 28 September 2026, at mainnet block 7,123,917 and testnet block 6,839,029.
Expedited proposals exist for changes that cannot wait a full voting period. They require a higher deposit and a 66.7% Yes threshold, and they pass through a shorter vote.
## Validators and Delegators
Governance follows Lumera's delegated staking model.
Validators cast votes on behalf of delegated stake by default. Delegators remain fully sovereign and may override their validator's vote on any proposal without affecting their broader staking relationship.
The practical effect is that passive holders stay represented while active holders retain full control. It also makes governance behavior part of how delegators evaluate validators, since a validator's voting record is public and applies to delegated stake.
## Veto
A proposal receiving **No With Veto** votes above the veto threshold is rejected regardless of the simple majority outcome, and its deposit is burned.
The veto mechanism protects the protocol from proposals considered harmful by a substantial minority, ensuring that governance reflects more than simple majority rule.
The operational walkthrough, including CLI commands.
Staking, supply mechanics, and voting power.
The fee structures governance can change.
# Lumera Hub Overview
Source: https://docs.lumera.io/hub/overview
Lumera Hub is the official cross-platform app for the Lumera network on web, desktop, and mobile.
Lumera Hub is the official application for interacting with Lumera Protocol, a Cosmos SDK Layer 1 blockchain built for decentralized storage, AI data services, and IBC-connected DeFi. It lets you manage your LUME wallet, stake to validators, participate in governance, and use the Cascade and Sense services. It runs on any device and requires no technical background to get started.
## Platforms
Lumera Hub is available across three platforms so you can interact with the network wherever you are. All three share the same feature set and connect to the same mainnet and testnet environments.
Access Lumera Hub directly in your browser at **hub.lumera.io** with no installation required. The web app is the fastest way to get started.
A native desktop application for **Windows, macOS, or Linux**.
Manage your wallet and interact with the network on **iOS or Android**.
## Key features
Lumera Hub covers everything you need to participate fully in the Lumera ecosystem.
### Wallet management
Create a new LUME wallet or import an existing one using a 24-word mnemonic seed phrase. Lumera Hub lets you manage multiple accounts, check your balance at a glance, and send LUME to any address on the network.
### Staking and delegation
Put your LUME to work by delegating it to one or more validators in the active set. Staking earns you a share of block rewards, and you can claim or restake your rewards at any time. Keep in mind that undelegating LUME starts an unbonding period before tokens become freely transferable again.
### Governance participation
Lumera is a community-governed chain. Use Lumera Hub to browse active governance proposals, review their details, and cast your vote (YES, NO, NO WITH VETO, or ABSTAIN) directly from your wallet.
### Cascade storage
Cascade is Lumera Protocol's decentralized file storage service. Through Lumera Hub you can upload files to the network and retrieve them later, with storage secured and replicated across the SuperNode network.
### Sense duplication detection
Sense is Lumera Protocol's near-duplicate detection service for NFT media assets. Submit your media fingerprints through Lumera Hub to check for near-duplicates across the network before minting.
### Token claiming (PSL → LUME migration)
If you held PSL tokens before the Lumera migration, you can claim your equivalent LUME allocation directly through Lumera Hub. The claiming interface walks you through verifying eligibility and completing the transfer.
### IBC transfers
Send LUME to other Cosmos chains or receive tokens from them using Inter-Blockchain Communication (IBC). Lumera Hub includes a built-in IBC transfer interface so you can move assets across the Cosmos ecosystem without leaving the app.
## Networks
Lumera Hub connects to both the production network and the test network.
| Network | Chain ID | Purpose |
| - | - | - |
| Mainnet | `lumera-mainnet-1` | Production, real LUME tokens |
| Testnet | `lumera-testnet-2` | Testing, free testnet tokens |
Always confirm which network you are connected to before signing any transaction. The current network is displayed in the Lumera Hub interface. Use **lumera-testnet-2** for experimentation and **lumera-mainnet-1** for real funds.
## Get started
Create or import a Lumera wallet, check your LUME balance, send tokens, and secure your seed phrase.
Delegate LUME to validators, track your staking rewards, and participate in network governance.
# Lumera Hub Wallet Guide
Source: https://docs.lumera.io/hub/wallet
Create or import a Lumera wallet in Lumera Hub and manage, send, and stake your LUME.
Your Lumera wallet is your identity on the network. It holds your LUME tokens, controls your staking positions and governance votes, and authorizes every action you take through Lumera Hub. This page walks you through creating or importing a wallet. It then covers the day-to-day operations you can perform, from checking your balance and sending tokens to staking, claiming rewards, and voting on proposals.
**Your seed phrase is the master key to your wallet.** Anyone who obtains your 24-word mnemonic can take full, permanent control of your funds. Never share it with anyone, including support staff. Never enter it on any website other than an application you have independently verified as authentic. Lumera Protocol support will never ask for your seed phrase.
## Wallet setup
Open Lumera Hub on whichever platform you prefer.
* **Web**. Navigate to [hub.lumera.io](https://hub.lumera.io) in your browser.
* **Desktop**. Launch the Lumera Hub application you installed on Windows, macOS, or Linux.
* **Mobile**. Open the Lumera Hub app on your iOS or Android device.
You will land on the wallet setup screen the first time you open the app.
Select **Create New Wallet** to generate a brand-new Lumera account.
Lumera Hub generates a **24-word mnemonic seed phrase** unique to your wallet. This is the only backup of your account. There is no account recovery without it.
Write down every word in order on paper (or stamp them into a metal backup plate). Once you have recorded the phrase, complete these steps.
1. Click **Continue** to proceed to the verification step.
2. Confirm your seed phrase by selecting the words in the correct order when prompted.
3. Set a local password or PIN to protect the app on this device.
After verification, your wallet address (beginning with `lumera1...`) is created and ready to use.
If you already have a Lumera wallet, or are migrating from a compatible Cosmos wallet, select **Import Wallet** instead.
Enter your 24-word (or 12-word) seed phrase, one word per field. Lumera Hub derives your wallet address from the phrase and restores full access to your account.
Connect your Ledger device via USB or Bluetooth, unlock it, and open the **Cosmos** app on the device. Select **Connect Ledger** in Lumera Hub and follow the on-screen pairing steps. Your private key never leaves the hardware device.
Your new wallet starts with a zero balance. You can acquire LUME in three ways.
* **From an exchange**. Withdraw LUME to your `lumera1...` address.
* **From another wallet**. Ask the sender to transfer LUME to your address.
* **From testnet faucet**. If you are on **lumera-testnet-2**, use the [community faucet](/faucet) to request test tokens at no cost.
Copy your wallet address from the **Receive** panel in Lumera Hub and share it with the sender.
Lumera Hub supports both available networks. Select the appropriate one from the network switcher in the app settings or the top navigation bar.
| Network | Chain ID | Use For |
| - | - | - |
| Mainnet | `lumera-mainnet-1` | Real LUME, live transactions |
| Testnet | `lumera-testnet-2` | Practice, development, testing |
Confirm the active network before signing any transaction. Transactions submitted to the wrong network cannot be reversed.
## Wallet features
### Checking your LUME balance
Your balance appears on the **Portfolio** or **Dashboard** screen as soon as your wallet is loaded. Lumera Hub displays four balances.
* **Available balance**. LUME you can freely send or delegate.
* **Staked balance**. LUME currently delegated to validators.
* **Unbonding balance**. LUME that has been undelegated and is still in the unbonding period.
* **Claimable rewards**. Staking rewards ready to withdraw.
### Sending LUME
1. Open the **Send** panel.
2. Enter the recipient's `lumera1...` address.
3. Enter the amount of LUME to send.
4. Review the transaction details (recipient, amount, and estimated gas fee).
5. Confirm and sign the transaction.
Double-check the recipient address before confirming. Cosmos transactions are irreversible. LUME sent to a wrong address cannot be recovered.
### Staking and delegating LUME
Staking locks your LUME with a validator to help secure the network and earn you a share of block rewards.
1. Go to the **Staking** section.
2. Browse the validator list and select a validator to delegate to. Consider the validator's commission rate, uptime, and community reputation.
3. Enter the amount of LUME to delegate.
4. Confirm and sign the delegation transaction.
When you undelegate LUME, it enters a **21-day unbonding period**. During this period it earns no rewards and cannot be transferred. Plan your liquidity needs accordingly.
You can delegate to multiple validators to spread risk across the active set.
### Claiming staking rewards
Staking rewards accumulate continuously but are not automatically credited to your available balance. To claim them, follow these steps.
1. Go to the **Staking** section and find your active delegations.
2. Click **Claim Rewards** next to a validator, or use **Claim All** to collect rewards from every delegation in a single transaction.
3. Confirm and sign the transaction. Rewards are added to your available balance immediately.
You can also **restake** (compound) rewards by delegating them back to the same validator in one step.
### IBC transfers
IBC lets you send LUME to other Cosmos chains (such as Osmosis or Cosmos Hub) or receive tokens from them.
1. Open the **IBC Transfer** panel.
2. Select the destination chain from the dropdown.
3. Enter the recipient address on the destination chain.
4. Enter the amount and the channel ID for the route you want to use.
5. Confirm and sign the transaction.
IBC transfers require an active relayer between Lumera and the destination chain. If a transfer appears stuck, it may be waiting for a relayer to relay the packet. Funds are not lost. The transfer will either complete or time out and be refunded.
### Voting on governance proposals
LUME holders can vote on on-chain governance proposals that shape the future of the network, including parameter changes, software upgrades, and community spending.
1. Go to the **Governance** section.
2. Browse open proposals and click one to read its full description.
3. Choose **Yes**, **No**, **No With Veto**, or **Abstain**.
4. Confirm and sign the vote transaction.
Voting power is proportional to your total staked LUME at the time the proposal's voting period ends.
## Security best practices
Write your 24-word seed phrase on paper immediately after generating it. For long-term storage, consider stamping or engraving the words on a metal plate that is resistant to fire and water.
**Never** save your seed phrase in a text file, screenshot, cloud note, email, or messaging app. Any digital copy is a potential attack surface. Keep physical backups in secure, separate locations.
For large holdings, use a Ledger hardware wallet. Lumera Hub supports Ledger devices. Your private key is generated and stored on the hardware device and never exposed to your computer or phone. Every transaction must be physically confirmed on the device.
Make sure you are running the latest Ledger firmware and the Cosmos app. Purchase Ledger devices only from the official Ledger website to avoid supply-chain attacks.
Always read the full transaction summary before tapping **Confirm**. Check the recipient address, the amount, and the network. Attackers sometimes use clipboard hijacking to silently replace copied addresses with their own.
On Ledger devices, verify the transaction details on the hardware screen before approving, not just in the app.
Lumera Hub clearly displays the active network. Before performing any transaction, confirm you are on the correct network.
* Use **lumera-mainnet-1** for real LUME and live operations.
* Use **lumera-testnet-2** for experimentation and development.
Gas fees and token balances are separate between networks. Testnet tokens have no monetary value.
**Keplr wallet integration.** If you already use Keplr to manage Cosmos accounts, you can add the Lumera mainnet or testnet to Keplr and import your existing Lumera address. Use the same seed phrase you used when setting up your Lumera Hub wallet to access the same account across both applications.
# Lumera Protocol
Source: https://docs.lumera.io/index
Infrastructure for permanent, provable data and decentralized intelligent services.
Lumera is a decentralized protocol that enables enterprises and developers to build applications on permanent, provable data and decentralized intelligent services. By combining permanent data, verifiable execution, and long-lived digital state within a common protocol framework, Lumera provides infrastructure that remains transparent, auditable, and independent of centralized providers.
Unlike general-purpose blockchains, Lumera is built around a simple architectural principle: consensus should secure the network, not perform every service. Validators maintain consensus and governance, while economically bonded SuperNodes provide storage, verification, retrieval, and decentralized execution. This separation allows sophisticated services to scale independently without compromising security or decentralization.
The blockchain serves as the network's coordination layer, recording requests, commitments, payments, proofs, and governance, while SuperNodes perform the underlying work and return cryptographic evidence of completion. Rather than forcing every computation into consensus, Lumera combines the scalability of distributed systems with the trust guarantees of blockchain infrastructure.
Lumera is a protocol designed to support an expanding ecosystem of decentralized intelligent services built on a shared architectural foundation. As the network evolves, each new capability extends the protocol through the same architectural principles, execution model, and economic framework—creating a unified platform where permanent storage, verification, identity, memory, execution, and settlement reinforce one another rather than exist as independent systems.
## Key Features
Store data once and retain it indefinitely. SuperNodes continuously prove they still hold the data they are responsible for, with cryptographic proofs recorded on chain.
Validators secure consensus while dedicated SuperNodes provide storage, verification, retrieval, and decentralized services, allowing execution to scale independently of consensus.
Every decentralized intelligent service is delivered as a modular protocol service with a common lifecycle for registration, execution, settlement, and verification. New services extend the protocol without introducing separate systems.
Protocol fees compensate network operators, sustain long-lived services, and align economic incentives between developers, users, validators, and SuperNodes.
Build with familiar EVM tooling, integrate across the Cosmos ecosystem through IBC, or consume Lumera's services without migrating existing applications.
LUME holders govern protocol parameters, service economics, treasury allocation, and future network upgrades through decentralized governance.
## Design Principle
Lumera is built around a simple architectural principle:
> **Reason off-chain. Verify on-chain.**
**Consensus should establish truth—not perform every computation.**
The blockchain acts as the protocol's control plane, recording commitments, permissions, payments, governance, and cryptographic proofs, while SuperNodes perform storage, verification, retrieval, and decentralized services.
By separating consensus from execution, Lumera allows sophisticated protocol services to scale independently while ensuring every operation can produce verifiable evidence rather than requiring users to trust the party performing the work.
## The Action and Agent framework
Every decentralized intelligent service on Lumera is implemented through a common execution framework built around **Actions** and **Agents**. Together, they provide a consistent model for how protocol services are requested, executed, verified, and settled across the network.
**Actions** are the atomic building blocks of decentralized intelligent services. Each Action represents a single verifiable unit of work executed by SuperNodes, following a common lifecycle for registration, execution, settlement, and proof generation. Because every Action follows the same framework, new protocol services extend the network without introducing new execution models.
### Example: Cascade
Cascade is an Action that permanently stores data using erasure coding, decentralized SuperNode storage, and continuous cryptographic retention proofs.
**Agents** build on top of Actions. Rather than performing work directly, Agents coordinate and compose one or more Actions into higher-level decentralized intelligent services, allowing applications to orchestrate storage, verification, computation, and future protocol services through a single execution framework.
This layered architecture allows Lumera to evolve through composition rather than reinvention. Every new decentralized intelligent service shares the same execution model, verification framework, and settlement lifecycle, creating a unified protocol where services remain interoperable by design.
## Building on Lumera
Applications built on Lumera consume protocol services through a common execution framework. Today, developers can permanently store data using Cascade, with every file erasure-coded, distributed across economically bonded SuperNodes, and continuously verified through cryptographic retention proofs. Rather than relying on a centralized storage provider, applications receive permanent, verifiable data backed by the protocol itself.
This foundation enables developers to build applications that require long-lived data, verifiable infrastructure, and decentralized execution without managing storage networks or trusted intermediaries. As additional protocol services are introduced, they become available through the same framework and developer experience.
Store your first file from TypeScript in just a few minutes.
Learn how permanent storage, fees, and retention proofs work.
Build using TypeScript, Go, or Rust.
## Protocol Architecture
Lumera separates consensus from service execution through a Validator–SuperNode architecture.
Validators secure the network through delegated Proof of Stake, proposing and validating blocks while governing protocol parameters. SuperNodes provide the protocol services applications consume, including storage, verification, retrieval, and decentralized execution. A SuperNode remains active only while bonded to an eligible validator, ensuring service capacity is backed by real economic commitment.
The blockchain serves as the protocol's control plane, recording requests, commitments, payments, governance, and cryptographic proofs. SuperNodes serve as the execution and memory layers, performing work directly for clients without routing data through consensus. LUME powers staking, transaction fees, governance, and protocol services, aligning the network's economic incentives around a single native asset.
Learn how validators, SuperNodes, modules, and clients interact.
Explore token utility, supply mechanics, and staking.
Understand protocol revenue, fee distribution, and sustainability.
Learn how protocol upgrades and parameter changes are governed.
## Lumera Vision
Lumera is designed as a long-lived protocol rather than a collection of independent products. The same architectural principles that enable permanent, provable data naturally extend to durable memory, decentralized identity, verifiable execution, settlement, trust, and future decentralized intelligent services.
Rather than introducing separate systems for each new capability, Lumera extends a common execution framework, economic model, and security architecture. Every new protocol service builds upon the same foundation, allowing the network to evolve through composition rather than reinvention.
The result is a unified protocol where storage, memory, identity, verification, execution, settlement, and intelligent services reinforce one another through a consistent architectural model—providing developers with a stable platform that becomes more capable over time without requiring them to rebuild on a different foundation.
# The Five Foundational Capabilities
Source: https://docs.lumera.io/intelligence-layer/capabilities
The five capabilities the Intelligence Layer is built from, how they depend on each other, and which are available today.
The Intelligence Layer is built from five capabilities. They are not five products. Each is a property the Protocol needs in order to support Decentralized Intelligent Services, and each becomes more useful in the presence of the others.
| Capability | What it provides | Available today |
| - | - | - |
| [Identity](/intelligence-layer/identity) | Sovereign on-chain identity, delegated authority, capability grants, attestation | LumeraID and claims |
| [Durable Memory](/intelligence-layer/durable-memory) | Persistent, integrity-checked state that outlives a session | Yes, through Cascade |
| [Verifiable Execution](/intelligence-layer/verifiable-execution) | Work performed by SuperNodes with signed receipts, settled on confirmed completion | Storage and attestation Actions |
| [Economic Coordination](/intelligence-layer/economic-coordination) | Funding, escrow, and settlement handled by the Protocol | Escrow and settlement for existing Actions |
| [Trust](/intelligence-layer/trust) | A record compiled from what participants actually did | Seeded by retention and settlement history |
## Identity
Every participant is represented by a LumeraID, an on-chain identity controlled by its holder's keys. People, applications, autonomous processes, datasets, and service providers are participants in the same sense.
Identity is the root of the dependency graph. Authorization needs a subject, escrow needs an account, a receipt needs a signer, and a reputation record needs something stable to attach to.
## Durable Memory
State that survives the end of a session, with integrity that can be demonstrated rather than assumed.
[Cascade](/cascade/how-cascade-works) provides this today. Data is erasure-coded across SuperNodes, retained for a single upfront payment, and re-proved continuously, with long-term retention funded by [Everlight](/cascade/everlight/overview). The architecture treats memory as existing at more than one temperature, with permanent artifacts and mutable working state served by the same identity and settlement model.
## Verifiable Execution
Work requested as an Action, performed by SuperNodes, evidenced by signed receipts, and settled only when completion is confirmed against the on-chain record.
This is the existing Action Lifecycle applied to a wider range of work. Storage was the first kind of work to move through it. It is not the only kind it can carry.
## Economic Coordination
Funding, escrow, and settlement treated as a coordination problem the Protocol owns rather than one participants negotiate.
A participant funds once and consumes across the network. Fees are escrowed before work begins and released on confirmed completion. Metered work and long-lived obligations are funded through deliberately separate mechanisms, because per-use fees cannot fund permanence.
## Trust
A record compiled from identity claims, delegations, attestations, signed receipts, settlements, disputes, and retention proofs.
Trust is derived rather than designed. It adds interpretation to evidence the other capabilities already produce, which is why it is the clearest example of composition in the section.
## How the capabilities depend on each other
The five form an acyclic graph. Identity is the root, and nothing it depends on sits above it. Execution needs to know who is asking. Memory needs to know who may read a private record. Settlement needs an account to draw against. Trust needs a stable subject and a stream of evidence.
The ordering matters because it constrains what can be built when. A capability cannot be completed before the ones it consumes.
## Why Durable Memory comes first in practice
Dependency order is not adoption order.
Adoption follows usefulness, and the capability with the most immediate practical value is Durable Memory. It is available on mainnet now. A developer can build against it today without waiting for anything else.
The two orderings are compatible because memory usage produces exactly the evidence the later capabilities consume. Retention proofs, storage settlements, and retrieval patterns are behavioral records anchored to identity, and they are objectively checkable. They are the first edges of the trust record, which is why trust does not have to wait for an execution layer before it becomes meaningful.
Start where the Protocol is strongest.
Store your first file from TypeScript.
The capability available today.
# Durable Memory
Source: https://docs.lumera.io/intelligence-layer/durable-memory
Persistent, integrity-checked state that outlives a session, and how the Protocol provides it at more than one temperature.
Durable Memory is the capability that gives software state which outlives the process that created it.
It is the capability available today. [Cascade](/cascade/how-cascade-works) has been live on mainnet since before the rest of the Intelligence Layer was defined, which means a developer can build against Durable Memory now without waiting for anything else.
## Why it exists
Most software keeps its state somewhere convenient. A session buffer, a hosted database, a vendor's object store. That works while a person is supervising the software and can restate context when it is lost.
Autonomous processes do not have that fallback. A process that forgets between invocations cannot be held to a decision it made yesterday. A process whose state lives on a single provider inherits that provider's incentives, retention policy, and failure modes. And a process that needs to prove to a counterparty that a record has not changed cannot do so if the only assurance is a service agreement.
Durable Memory addresses three distinct requirements at once. State must survive. Its integrity must be demonstrable rather than asserted. And it must not belong to a vendor.
## Memory at more than one temperature
Not all state has the same shape, and the Protocol does not store it all the same way. The split is deliberate.
| Where state lives | What it holds | Characteristics |
| - | - | - |
| Cascade | Artifacts. Datasets, model artifacts, transcripts, evidence, anything large and long-lived | Content-addressed, permanent, integrity-critical, written once |
| Structured store | Working state. Application data, preferences, session metadata, indexes | Mutable, low latency, read and written continuously |
| The chain | Identity, permissions, commitments, registries, escrows, settlement, dispute outcomes | Strongly consistent, and not memory |
The third row matters as much as the first two. The Control Plane is not a place to keep application state. It holds what must be strongly consistent, and nothing else. Treating it as a database would put consensus in the path of every read.
The first two rows share one identity system and one settlement model. That is the practical benefit of memory being a Protocol capability rather than a product. A developer building on Lumera does not run an auth server, and does not run a database server.
## What Cascade provides
Cascade is the artifact layer. Data is erasure-coded across economically bonded SuperNodes, retained for a single upfront payment, and re-proved continuously so that retention is demonstrated rather than promised.
The mechanics are documented in full. Start with [How Cascade works](/cascade/how-cascade-works) for the lifecycle, fees, and guarantees, and the [Quickstart](/quickstart) to store something.
## What makes permanence sustainable
A one-time payment for indefinite storage only works if something keeps paying operators after that payment is exhausted.
[Everlight](/cascade/everlight/overview) is that mechanism. It funds retention from protocol reward flows, fee shares, and endowment yield, and it pays operators for as long as they continue to hold data and prove they still have it.
This is why permanence is an economic property of the network rather than a commitment by a company. Nothing needs to remain solvent for stored data to remain retained.
## Access and privacy
Stored data is private by default. Retrieving a private artifact requires a signature that resolves to an identity permitted to read it, which is [Identity](/intelligence-layer/identity) doing its job inside an existing flow rather than a separate access system.
The network stores opaque bytes. It does not index filenames, schemas, or semantic content. For anything commercially sensitive, encrypt before upload, which lets the network prove it still holds a record it cannot read. See [Store encrypted files on Cascade](/cascade/guides/encrypted-storage).
## What this does not provide
Retrieval by meaning is not a Protocol capability. Cascade retrieves by content address, and structured state retrieves by key. Neither performs semantic search over accumulated memory.
Applications that need it build it above the Protocol, using Lumera for durability and provenance and their own index for recall. This boundary is worth knowing before designing around it.
## How it fits the architecture
Durable Memory is the memory plane in the [plane model](/architecture). Data moves directly between clients and SuperNodes and never passes through consensus, which is why throughput is not bounded by block time.
Memory is also where the rest of the Intelligence Layer starts. Every write produces evidence anchored to an identity, including retention proofs, settlements, and retrieval patterns. Those records are objectively checkable, and they are the first inputs to [Trust](/intelligence-layer/trust). Storing memory on the network generates trust data from the first write, which is why this capability is the one to build against first.
The artifact layer, documented in full.
How long-term retention is funded.
Store your first file from TypeScript.
What memory usage accumulates into.
# Agent Memory
Source: https://docs.lumera.io/intelligence-layer/durable-memory/agent-memory
Append-only, client-encrypted memory for autonomous agents, anchored in Cascade with retention proven on chain.
This page documents a design that is published ahead of availability. The architecture, data model, and durability semantics below are settled. The client libraries are not yet released, and this page intentionally carries no dates and no pricing. It describes how the system is built, not something you can install today.
Agent Memory is a memory layer for autonomous agents built directly on [Cascade](/cascade/how-cascade-works). An agent appends to a log, the log is sealed into immutable objects anchored on chain, and the network can be asked to prove that any given object is still retrievable.
It is a client surface over an existing Protocol Service rather than a new protocol capability. Everything beneath the client runs on mainnet today. There are **no new chain modules** anywhere in the design, which is the reason its behavior can be described with this much precision before release.
## Why agents need this
An agent that loses its state between invocations cannot be held to a decision it made earlier. That much is obvious, and hosted memory products solve it.
What they do not solve is the second problem. When memory is load-bearing, the agent's owner eventually needs to answer a question about the past: what did this agent know, when did it know it, and can you show me that the record has not been altered since. A hosted store answers that with a service agreement. It cannot answer it with evidence, because the store is also the party being asked.
Agent Memory separates those roles. The record is append-only and content-addressed, so alteration is detectable. Retention is funded by [Everlight](/cascade/everlight/overview) and evidenced by storage challenges, so continued possession is demonstrated by parties other than the one making the claim.
## System layers
Two modes exist because two different developers exist. Gateway mode hides keys, gas, and SuperNode routing entirely, so a developer never touches a wallet. Self-custody mode removes the gateway from the path and has the client anchor directly. The data written is identical either way, and a log created in one mode remains readable in the other.
## Data model
Three objects, one invariant.
| Object | What it is |
| - | - |
| **Entry** | The atomic unit an agent writes. Small, from bytes to tens of kilobytes. Addressed by `entry_cid = BLAKE3(entry bytes)` |
| **Segment** | A set of sequenced entries sealed into one immutable object and anchored as a single Cascade action |
| **Manifest** | A periodic checkpoint mapping entry CID to its segment, offset, and length. Manifests are themselves stored in Cascade and hash-linked into a chain |
The invariant is that the log is **append-only**. Entries are never edited and never deleted. A correction is a new entry that supersedes an earlier one, which means the history of what the agent believed remains intact and auditable rather than being overwritten.
Sealing entries into segments is what makes the economics work. One chain action per entry would be unaffordable. One action per sealed segment, with the manifest providing individual addressability, gives per-entry granularity at per-segment cost.
The latest manifest CID is the portable root of the entire log.
## Durability states
The design refuses to present a fast acknowledgement as durability. Every entry is in exactly one of three states, and the client always reports which.
| State | Meaning | Backed by |
| - | - | - |
| `accepted` | Held in the gateway's replicated staging log, or the client's local buffer in self-custody mode. Fast acknowledgement | Gateway infrastructure only |
| `anchored(H)` | The segment's Cascade action finalized at block H. LEP-5 possession proofs verified on chain, fee paid, retention funding engaged | Chain, SuperNodes, and Everlight |
| `proven(X)` | The most recent storage challenge covering the segment passed at block X | Ongoing challenge evidence |
The window between `accepted` and `anchored` is the only centralized durability window in the system. It is handled by keeping it short and by never describing it as anything else. A write returns `accepted`, and an acknowledged write is not represented as funded permanence.
This distinction is the point of the design rather than a caveat attached to it.
## Encryption
Encryption is on by default and is not a configuration option.
The client generates a log key and encrypts each entry before any bytes leave the agent's process. The gateway, the SuperNodes, and the chain see ciphertext only. Retention proofs operate on ciphertext and are unaffected, which produces the useful property that the network proves it is still holding data it cannot read.
The consequence is stated plainly rather than buried. **Losing the log key means losing the memory.** There is no escrow and no recovery path in the design.
Logs can optionally be created public, in plaintext, for cases where visible content is the purpose. Private is the default.
## Proving retention
This is the capability that does not exist in a hosted memory product.
Because every segment is a Cascade action, an object CID resolves to an action, and that action resolves to on-chain evidence. That gives a direct answer to a question a service agreement can only assert: *this object was last proven retrievable at block X.*
The chain of evidence behind that statement is verifiable independently. LEP-5 availability commitments prove possession at finalization, and ongoing storage challenges test retention afterward. Both are described in [Cascade](/cascade/how-cascade-works).
## Portability
A manifest CID plus the log key is the whole memory.
Anyone holding both can reconstruct the log from Cascade without the gateway, without the client library, and without Lumera's cooperation. Verification runs offline against the manifest chain.
This is what distinguishes portable memory from exportable memory. An export is a copy a vendor chooses to give you. A manifest root is the object itself.
## The client surface
Described as capabilities rather than signatures, because the API is not yet published.
* Append an entry, returning its CID, sequence number, and state
* Read recent entries, or entries since a point in the log
* Fetch and decrypt a specific entry by CID
* Force a seal, producing segment receipts
* Query the durability state of any entry
* Request a retention proof for an entry
* Export the manifest root
* Verify a manifest offline, with no gateway involved
The same shape is provided in Python and TypeScript. Framework integrations expose a subset of these as tools, including an MCP connector for hosts that support it.
## What this does not do
Stated as design scope, not fine print.
* **No semantic recall.** Recency and lookup only. Retrieval by meaning is not provided, and applications that need it build it above this layer.
* **No hot mutable state.** This is the durable tier. Mutable, low-latency state is a separate tier in the [Durable Memory](/intelligence-layer/durable-memory) architecture.
* **No multi-writer logs.** One writer per log. Shared memory across agents requires delegation and the hot tier.
* **No sharing or access control lists.** Sharing a log means handing over the manifest CID and the key, which works but is manual.
* **No key escrow or recovery.**
* **No durability claim for `accepted`** beyond the stated staging behavior.
The design sells provenance and permanence. It does not claim retrieval quality.
## How it settles
Anchoring a segment is an ordinary Cascade action and is priced as one, using the existing base plus per-kilobyte fee. No new billing rail is introduced, and no separate subscription exists in the design. Pricing for the hosted gateway is not yet published.
The storage service Agent Memory anchors to.
Where this sits in the Protocol architecture.
# Economic Coordination
Source: https://docs.lumera.io/intelligence-layer/economic-coordination
How the Protocol funds work, holds it in escrow, and settles it without bilateral relationships between participants.
Economic Coordination is the capability that lets participants pay and be paid without establishing a relationship with each other first.
It exists because payment is not incidental to autonomous work. It is part of the same transaction. An Action that cannot be funded cannot be requested, and an operator that cannot be paid on confirmed completion has no reason to serve. The Protocol treats funding, escrow, and settlement as a coordination problem it owns rather than one it leaves to its participants.
## Why coordination belongs to the Protocol
Consider the alternative. If every provider handled its own billing, an agent using ten services would hold ten accounts, ten credentials, and ten separate trust assumptions. Adding a provider would mean a new commercial relationship. Switching would mean unwinding one. Every provider would be a point of failure for the agent's ability to transact at all.
That model does not survive contact with autonomous operation. It puts a negotiation in the path of every new counterparty, and negotiation is the thing an agent cannot do unsupervised.
Moving coordination into the Protocol removes the negotiation. A participant funds once. Consumption draws against that balance wherever it happens on the network. A provider is paid by the Protocol on confirmed completion rather than by the requester on trust. Neither side has to have met.
## Escrow
Escrow is the mechanism that makes this safe in both directions.
When an Action is registered, its fee is committed on chain before work begins. The requester's funds are locked and cannot be spent elsewhere. The operator's compensation is real but contingent on completion.
If the Action completes and is confirmed, the escrow releases to the SuperNodes that served it. If it expires before finalization, the escrow returns to the requester. Neither outcome depends on either party's cooperation after the fact, because the condition is evaluated against the on-chain record.
This is the same discipline documented in the Action Lifecycle. Economic Coordination generalizes it rather than introducing a second payment path.
## Settlement is gated, not promised
Settlement follows confirmation. Payment flows for work the chain can see was completed.
The consequence is that a requester never needs to trust an operator's invoice, and an operator never needs to chase a payment. There is no accounts-receivable relationship between participants, because the Protocol is the counterparty to both sides of the exchange.
Fees are quoted in LUME and denominated by the resource consumed. Where the fee goes once released, and how it splits between operators, validators, and the burn, is documented in [Protocol Economics](/protocol-revenue).
## Two settlement modes, kept separate
Not all obligations have the same shape, and the Protocol does not fund them the same way.
**Metered settlement** covers work with a defined beginning and end. A request is escrowed, performed, confirmed, and settled. Cost tracks consumption.
**Sustained funding** covers obligations that outlive the payment that created them. Permanent storage is the clearest case. A one-time fee cannot pay an operator to hold data indefinitely, because the obligation continues after the payment is exhausted. [Everlight](/cascade/everlight/overview) is the mechanism for that class, funded through protocol reward flows, fee shares, and endowment yield.
Conflating the two would be a design error in both directions. Per-use fees cannot fund permanence. An endowment cannot meter individual requests. Keeping them separate is what allows the Protocol to make a durable promise without accumulating an unpriced liability.
## Relationship to Protocol Services
Every Protocol Service is priced, escrowed, and settled through the same path. A service is not a separate商 arrangement with its own billing. It is an Action type with a fee schedule.
The practical effect for a developer is that adding a capability to an application does not add a commercial integration. If the capability exists as a Protocol Service, it is already payable from the same balance, on the same terms, with the same evidence.
The asset fees are denominated and settled in.
How fees are distributed once settled.
# Escrow and Settlement
Source: https://docs.lumera.io/intelligence-layer/economic-coordination/escrow-and-settlement
How the Protocol holds a service fee during execution and releases it only against verified completion.
Settlement on Lumera is conditional. A service fee is committed on chain before work begins and released only when the chain has verified that the work completed.
The mechanism is escrow, and it is what allows a requester and an operator with no relationship to transact safely.
## Escrow at registration
When an action is registered, its fee is transferred out of the creator's account and held by the Protocol. This happens in the same transaction that creates the action, before any operator sees the request.
Two things are true from that moment. The creator's funds are committed and cannot be spent elsewhere. The operator's compensation exists but is contingent.
Neither party is exposed to the other's good behavior. The requester is not trusting an operator to deliver after payment, and the operator is not performing work on the promise of payment.
## Release on verified completion
The escrow releases when an action reaches `DONE`, which requires the chain to accept a finalization message carrying whatever evidence that action type demands.
For Cascade that evidence is Merkle proofs for the chunks committed at registration. An operator that never received the file cannot produce them, so it cannot reach `DONE`, so it cannot be paid. The economic consequence is enforced by verification rather than by dispute.
Payment goes to the SuperNodes that served the action.
## Refund on expiration
If the action is not finalized before its expiration, the state becomes `EXPIRED` and the escrow returns to the creator automatically. A finalization the chain rejects settles nothing and leaves the action open until a later attempt succeeds or it expires.
No claim is filed and no counterparty has to cooperate. The refund is a consequence of the state transition, evaluated by the chain against the on-chain record.
This is the property that makes the write path safe to retry. An upload that never finalizes costs a transaction fee, not the service fee.
## Where the fee goes
The released fee does not all reach the serving operators. A share of every action registration fee is routed to the Everlight SN Pool, governed by `registration_fee_share_bps` and designed at 2 percent.
That routing is what connects one-time payment to long-lived obligation. The fee that pays for storing a file also contributes to the pool that compensates operators for continuing to hold it. See [Everlight](/cascade/everlight/overview).
Registration fees do not increase to fund this. The share comes out of the existing split.
## Service fees and gas are different
Two payments occur when you register an action, and conflating them causes confusion when reconciling costs.
| Payment | What it pays for | Where it goes |
| - | - | - |
| Service fee | The work itself. Escrowed, then released or refunded | Serving SuperNodes, with a share to the Everlight pool |
| Transaction fee | Including the transaction in a block | Validators and delegators, with a share burned |
The service fee is refundable on expiration. Gas is not, because the transaction was processed regardless of whether the work succeeded. Distribution of both is covered in [Protocol Economics](/protocol-revenue).
## The approval path
Some flows insert an explicit approval before settlement. In those cases the action moves to `APPROVED` through `MsgApproveAction`, and the escrow releases on that approval rather than on finalization alone.
This exists primarily for cross-chain requests, where the party that pays and the party that authorizes completion are not the same account. See [Cross-chain overview](/cross-chain/overview).
## Why this is a Protocol capability
The alternative is bilateral. Every provider bills its own customers, every customer holds an account with every provider, and every new counterparty is a commercial negotiation.
That model cannot support software acting without supervision, because negotiation is precisely what an autonomous caller cannot do. Moving settlement into the Protocol removes it. The Protocol is counterparty to both sides, so there is no invoice to dispute and no receivable to chase.
The state transitions escrow is tied to.
How the escrowed amount is determined.
Where fees go once released.
The pool a share of every fee funds.
# Service Pricing
Source: https://docs.lumera.io/intelligence-layer/economic-coordination/service-pricing
How Protocol Service fees are calculated, what they include, and which parameters governance controls.
Protocol Service fees are priced by the resource consumed and quoted in LUME. There is no subscription, no tier, and no recurring charge for a completed action.
## How a Cascade fee is calculated
Cascade is priced with a base fee plus a per-size component.
```text theme={null}
fee = base_fee + (fee_per_kb × ceil(size_in_bytes / 1024))
```
Current mainnet parameters are a base of 30,000 `ulume` plus 10 `ulume` per KB. For a 1 MB file that is roughly 0.04 LUME. Testnet uses the same per KB rate with a 10,000 `ulume` base.
`ulume` is the base denomination. One LUME is 1,000,000 `ulume`.
| File size | Mainnet fee | Testnet fee |
| - | - | - |
| 100 KB | 0.031 LUME | 0.011 LUME |
| 1 MB | 0.04 LUME | 0.02 LUME |
| 10 MB | 0.13 LUME | 0.11 LUME |
| 100 MB | 1.05 LUME | 1.03 LUME |
Treat these as current values. Every parameter is governable, and the authoritative numbers live on chain.
## How a Sense fee differs
Sense is priced by the size of the data and the analysis performed, and it costs less than storing the same file through Cascade. The reason is what persists. Sense records an analysis result rather than retaining the submitted media, so the network takes on no long-term retention obligation.
This is the general rule behind Protocol pricing. Fees track the obligation created, not the size of the request.
## What the fee includes and excludes
**Included.** Encoding, distribution across the SuperNode mesh, verification at finalization, and the contribution to long-term retention funding. For Cascade, retrieval is not separately metered.
**Excluded.** The transaction fee that gets your registration into a block. That is gas, it is paid at the network minimum gas price, currently `0.025ulume`, and it is not refundable if the action later fails.
## One payment, not a subscription
A completed Cascade action is never billed again. Continued retention is funded by [Everlight](/cascade/everlight/overview), which draws from protocol reward flows, a share of registration fees, and endowment yield rather than from the uploader.
This is why permanence can be priced once. The obligation is funded by a mechanism designed for it, instead of by a fee that has already been spent.
## Expired actions refund
If an action expires before finalization, the escrowed service fee returns to the creator. You pay gas for the attempt and nothing for the service. See [Escrow and settlement](/intelligence-layer/economic-coordination/escrow-and-settlement).
## Governance parameters
Pricing is set by on-chain parameters, so it changes by proposal rather than by announcement.
| Parameter | Controls |
| - | - |
| Base action fee | The fixed component of a service fee |
| Fee per KB | The size-dependent component |
| `registration_fee_share_bps` | Share of the fee routed to the Everlight pool |
| Minimum gas price | The floor for transaction fees |
Query current values before quoting a cost to anyone.
```bash theme={null}
lumerad query action params
```
## Estimating cost in an application
Two practical notes for anyone building a cost model.
Round up. The per-KB component applies to whole kilobytes, so many small objects cost more in aggregate than the same bytes batched into fewer, larger ones. Applications writing frequent small records usually batch before anchoring.
Budget for gas separately. It is small relative to the service fee for large files and proportionally significant for very small ones.
What happens to the fee after registration.
How pricing parameters change.
The asset fees are denominated in.
The action a Cascade fee pays for.
# Identity
Source: https://docs.lumera.io/intelligence-layer/identity
How the Protocol represents participants, delegates authority, and records verifiable claims about them.
Identity is the root of the Intelligence Layer. Every other capability consumes it, and it consumes nothing above it.
The reason is mechanical rather than philosophical. Authorization needs a subject. Escrow needs an account to draw against. A receipt needs a signer. A reputation record needs something stable to attach to. Without a durable notion of who is acting, none of the other capabilities can be expressed.
## What protocol identity is
A participant on Lumera is represented by a **LumeraID**, an on-chain identity controlled by its holder's keys.
The set of things that can hold an identity is deliberately wide. People, applications, autonomous agents, datasets, and service providers are all participants in the same sense. An agent is not a secondary object owned by a user account. It is a principal that can be granted authority, hold a balance, sign work, and accumulate a record of its own.
Identity here is not a user profile. A profile describes someone. A LumeraID is what permissions attach to, what receipts are signed against, and what the Protocol checks before releasing a private record or a payment.
## Claims
State is added to an identity through **claims**, which are on-chain transactions that assert something verifiable.
| Claim type | Purpose |
| - | - |
| Profile | Establishes and updates the identity's own published record |
| Key rotation | Replaces keys without abandoning the identity or its history |
| Delegation | Transfers scoped authority from one identity to another |
| Capability grant | Defines which operations a holder is permitted to perform |
| Application and asset identity | Extends identity to software and to data |
| Attestation | Records a verifiable statement made by one participant about another |
| Revocation | Withdraws a grant or invalidates a prior claim |
Because claims are transactions, they inherit the properties of the Control Plane. They are ordered, public in structure, attributable to a signer, and permanent. An authorization decision made against them can be replayed and audited later.
Two consequences matter in practice. Key rotation means a compromised key does not destroy an accumulated record, which is what makes long-lived agent identity viable. Revocation means authority can be withdrawn as explicitly as it was granted.
## Delegated authority
Delegation is what makes autonomous operation safe to permit.
An owner does not hand an agent unrestricted control. An owner grants a scoped capability, on chain, to a specific identity. The grant states what may be done and within what limits. It can be narrowed, and it can be revoked.
Grants compose. If an enterprise delegates authority to an agent, and that agent grants a narrower capability to a specialist it engages, the chain holds a verifiable path from the original authority to the final actor. Any party can inspect that path and see where the authority came from and where it stops.
This is why authority is a Control Plane concern. Delegation must be strongly consistent, because two conflicting views of what an agent is allowed to do is not a degraded experience, it is a security failure.
## What identity does not own
The boundary is as important as the function.
Identity owns identity, delegation, capability, attestation, and revocation. It does not own application data, working state, model selection, or memory. Those belong to other capabilities and other planes.
Keeping the boundary strict is what keeps the dependency graph acyclic. If identity began storing mutable application state, everything that depends on identity would inherit that state's failure modes.
## Attestation
An attestation is a claim by one participant about another, recorded on chain and attributable to its author.
It is a general mechanism rather than a single feature. Content authenticity results are one kind of attestation. An operator vouching for a peer, a verifier confirming that an execution conformed to its declared shape, or an enterprise certifying that an agent passed review are all the same shape of record.
Attestations are the raw material the [Trust](/intelligence-layer/trust) record is compiled from. Identity produces the statements. Trust interprets them.
## How it fits the rest of the Protocol
Identity is not a new subsystem bolted alongside the chain. It is a Protocol Module holding identity records and claims in the Control Plane, checked by other modules during the Action Lifecycle.
When a private artifact is retrieved from [Cascade](/cascade/how-cascade-works), the request must carry a signature that resolves to an identity permitted to read it. That is identity doing its job inside an existing flow, not a parallel access system.
How claims and receipts compile into reputation.
How authority is checked when work is performed.
# LumeraID
Source: https://docs.lumera.io/intelligence-layer/identity/lumeraid
How identity works on Lumera today: addresses as identities, ADR-036 signatures, and how access to private objects is enforced.
LumeraID is the Protocol's identity primitive. This page covers how it behaves today and what you need to do to work with it. For why identity is the root of the Intelligence Layer and where the claim model is heading, see [Identity](/intelligence-layer/identity).
## Your address is your identity
There is no separate identity registration and no username. A Lumera address is a LumeraID, and the private key that controls the address controls the identity.
Identity records are anchored to addresses by `x/lumeraid`. Nothing needs to be created before you can act. If you can sign for an address, you have an identity the Protocol recognizes.
The practical consequence is that identity and key custody are the same problem. Losing the key does not lock you out of an account you can recover through support. It removes your ability to act as that identity at all.
## What identity is used for today
Identity is checked wherever the Protocol needs to know that a request comes from a party entitled to make it.
| Operation | What identity proves |
| - | - |
| Registering an action | The transaction signer is the creator and pays the fee |
| Uploading to Cascade | The uploader controls the account that registered the action |
| Retrieving a private object | The requester is permitted to read it |
The first is ordinary transaction signing. The second and third are not, because they happen over HTTP against a SuperNode rather than as a transaction on chain. Those use ADR-036.
## ADR-036 signatures
ADR-036 is the Cosmos standard for signing arbitrary data. It produces a signature that proves control of a key without submitting a transaction.
Cascade uses it because upload and download are direct calls to a SuperNode. A SuperNode needs to verify you control the registering account before it accepts bytes or serves a private object, and requiring a transaction for that would add a block of latency and a gas cost to every data operation.
What gets signed differs by operation.
| Operation | Signed payload |
| - | - |
| Upload | The `data_hash` of the file |
| Download | The `action_id` of the stored object |
The signature travels with the request. The SuperNode recovers the signer and checks it against the on-chain action record.
### Producing one
Two details trip people up, and both come from the standard rather than from Lumera.
**The `chain_id` must be empty.** ADR-036 signs data that is not bound to a chain, so the sign document carries an empty string. Passing your real chain ID produces a signature the SuperNode will reject.
**CosmJS does not expose `signArbitrary` on a direct wallet.** Protobuf signing and amino signing are separate paths, and ADR-036 requires amino. The usual approach is to hold both a direct wallet for transactions and an amino wallet for arbitrary signing, and expose both through one signer object.
The sign document takes a fixed shape.
```ts theme={null}
const signDoc = makeAminoSignDoc(
[{
type: "sign/MsgSignData",
value: {
signer: signerAddress,
data: Buffer.from(payload).toString("base64"),
},
}],
{ gas: "0", amount: [] }, // no fee, nothing is broadcast
"", // chain_id must be empty for ADR-036
"", // memo
0, // account number
0 // sequence
);
```
Browser wallets such as Keplr implement `signArbitrary` directly, so in a browser context you call it and skip the wrapper. The [Quickstart](/quickstart) shows the server-side construction, and [Build a browser app](/cascade/guides/browser-app) shows the wallet path.
## Public and private objects
Every Cascade action carries a visibility flag set at registration.
A **private** object requires a valid ADR-036 signature resolving to a permitted identity before a SuperNode will serve it. This is why private data can live on a public network. The encoded symbols are distributed across independent operators, but retrieval is gated on identity.
A **public** object is served without a signature. Use it when open availability is the point.
Visibility is not encryption. A private object is access-controlled, not unreadable. For data whose contents must never be visible to operators, encrypt client-side before upload. The network then provably retains bytes it cannot read. See [Store encrypted files on Cascade](/cascade/guides/encrypted-storage).
## Signing agents and applications
Because identity is just an address with a key, non-human participants use the same mechanism. An agent with its own keypair has its own LumeraID, distinct from its operator's, and its actions are attributable to it rather than to whoever deployed it.
That separation is worth setting up deliberately even now. Actions registered under a shared operator key are indistinguishable from each other in the on-chain record, and the record is what later capabilities read. Giving each agent its own identity today costs nothing and preserves attribution you cannot reconstruct afterward.
Scoped delegation, which lets an owner grant an agent bounded authority rather than a copy of a key, is described in [Identity](/intelligence-layer/identity) as part of the claim model.
## Key handling
A few consequences follow from identity being key-controlled.
* **Never hardcode a mnemonic.** Load it from an environment variable or a secrets manager. The Quickstart says this in a warning for a reason.
* **Use separate keys for separate roles.** A validator operator key, a SuperNode account key, and an application signing key should not be the same key.
* **Treat key loss as identity loss.** There is no recovery path outside your own backups.
Why identity is the root of the architecture.
A working ADR-036 signer in context.
Signing with Keplr.
What `x/lumeraid` stores.
# What Is the Intelligence Layer?
Source: https://docs.lumera.io/intelligence-layer/overview
The part of the Protocol concerned with autonomous software, and the gap in existing infrastructure it addresses.
The Intelligence Layer is the part of the Protocol concerned with autonomous software. It defines how a participant is identified, how it remembers, how its work is executed and evidenced, how that work is paid for, and how its record accumulates into something others can rely on.
It is not a separate network, a sidechain, or a parallel stack. Every capability in this section is expressed through the primitives documented in [Protocol Architecture](/architecture). The chain remains the Control Plane. SuperNodes remain the Execution Plane. Work is still requested as an Action and settles through the Action Lifecycle.
## Why it exists
Software that runs without a person watching it needs guarantees that human-facing software does not.
A developer using an API holds context in their head, reads the output and judges it, and resolves a billing dispute by email. An autonomous process does none of that. It needs state that survives the end of a session, an identity that means something to a counterparty it has never met, evidence that the work it paid for actually happened, and a way to settle without a commercial relationship with every provider it touches.
Most of that does not exist yet. Agents today call centralized endpoints, authenticate with API keys, and pay with a card attached to those keys. There is no record of which model ran or what data it consumed. If a provider changes behavior, raises prices, or withholds a response, the calling application has no recourse and no proof of what it was promised.
Every one of those gaps is a coordination problem. Coordination problems are what protocols are for.
## What it is not
Being precise about the boundary prevents a common misreading.
The Intelligence Layer is not a model host. Running inference is one kind of work among many, and it is the kind that commoditizes fastest. The Protocol coordinates, evidences, and settles work regardless of where the computation happens.
It is not an agent framework. It does not compete with the libraries developers use to build agents. It is the layer underneath them, providing the identity, memory, evidence, and settlement those frameworks currently have to assume or fake.
It is not a claim that the Protocol produces intelligence. Reasoning happens off chain. What lands on chain is the commitment, the receipt, and the outcome.
## Relationship to Protocol Services
Lumera already exposes work as Protocol Services. A service is priced by the resource it consumes, requested as an Action, escrowed on registration, and settled on confirmed completion.
The Intelligence Layer does not replace that model. It extends the range of work the model can carry, and it adds the capabilities autonomous callers need in order to use it without supervision. Decentralized Intelligent Services are Protocol Services consumed by software rather than by people.
This is the consistent pattern across the section. Each capability extends existing machinery rather than introducing a new subsystem beside it.
## Where to go next
The design rules every capability follows.
What the Protocol provides, and what is available today.
# Architectural Principles
Source: https://docs.lumera.io/intelligence-layer/principles
The design rules that govern how capabilities are added to the Protocol, and what each one means in practice.
The Intelligence Layer is governed by a small number of design rules. They are worth reading before the capability pages, because each capability is an application of them rather than an independent design.
Each principle below states the rule and what follows from it for someone building on the Protocol.
## The chain commits, SuperNodes execute
The Control Plane owns what must be strongly consistent. That means identity, permissions, budgets, prices, escrows, commitments, receipts, and dispute outcomes. The Execution Plane performs work. The memory plane holds artifacts and state.
Nothing in the Execution Plane or the memory plane ever becomes the source of truth for anything the Control Plane owns.
**What this means for you.** Authorization and settlement answers come from the chain and are consistent. Data movement does not pass through consensus, so throughput is not bounded by block time. When those two facts appear to conflict, the chain is authoritative.
## Reason off-chain, verify on-chain
Computation that cannot be made deterministic happens off chain. What is committed on chain is the evidence that it happened.
**What this means for you.** A receipt is a commitment to an event, not a copy of its content. You can prove work occurred without publishing what was in it, which is what keeps verification and confidentiality from being mutually exclusive.
## Composition over reinvention
New capabilities extend existing machinery. They are new Protocol Modules and new Action types, not new consensus, not a second settlement path, and not a parallel identity system.
**What this means for you.** What you learn about the Action Lifecycle transfers. A new capability arrives as a new kind of Action moving through a lifecycle you already understand, priced and settled the way everything else is.
## Authority is graded
A fluent answer is not a proof, and confidence is not a permission. The Protocol grades what a result entitles a caller to do, from advisory output through independently verified results and reproducible computation, up to narrowly scoped protocol-native effects.
**What this means for you.** Irreversible effects stay in your application. The Protocol returns signed results and structured plans. Your application revalidates and executes. That ordering is deliberate.
## Objective faults are penalized, subjective quality is priced
Faults that can be demonstrated carry consequences. Timeouts, missing or invalid receipts, wrong versions, unavailable artifacts, deterministic mismatches, and policy violations are all provable.
Whether an answer was good is not provable, so it is not penalized. It accumulates in the trust record instead, where a history of outcomes prices it over time.
**What this means for you.** Slashing is predictable and scoped to things an operator controls. Quality signals come from reputation, not from consensus.
## Boundaries are part of the design
Each capability has a defined scope and an explicit list of what it must never own. Identity does not hold application data. Metered settlement does not fund long-lived obligations. The composition layer does not take custody of irreversible effects.
**What this means for you.** The dependency graph stays acyclic, so a failure in one capability does not propagate into the ones it depends on.
## Dependency order is not adoption order
The capabilities form a dependency graph with identity at the root. That is the order the architecture is built in. It is not the order a developer meets them in, which follows practical usefulness.
**What this means for you.** Start with the capability that solves a problem you have now. The [capability page](/intelligence-layer/capabilities) sets out both orderings and what is available today.
# Trust
Source: https://docs.lumera.io/intelligence-layer/trust
How the Protocol turns identity, receipts, and settlement history into a verifiable record participants can use to evaluate each other.
Trust is the capability that lets a participant evaluate a counterparty it has no relationship with.
It is the last of the five capabilities to become useful, because it is derived rather than designed. Identity provides the subject. Execution and settlement provide the evidence. Trust is what accumulates when those records are read together over time.
## Why trust becomes a protocol concern
When software is operated by people, trust is a human judgment. A developer picks a provider based on its reputation, reads the output, and forms a view.
Autonomous operation removes the person from that loop. An agent selecting another agent for a subtask, consuming data from a source it did not vet, or committing a budget based on an analysis it cannot review has no way to make that judgment. Brand does not help. Neither does a self-reported rating.
What is needed instead is a record of behavior that cannot be authored by its subject. That requirement is what makes trust a protocol capability rather than an application feature. Only the Protocol sees the whole event stream, and only the Protocol can anchor it to identity in a way that cannot be forged.
## Compiled from protocol events
The trust record is not submitted. It is compiled from things that already happen.
| Source | What it evidences |
| - | - |
| Identity claims and delegations | Who a participant is and whose authority it holds |
| Attestations | Verifiable statements other participants made about it |
| Signed receipts | Work performed, attributable to the identity that performed it |
| Settlements | Obligations that were met and paid |
| Disputes and penalties | Obligations that were not |
| Retention proofs | Data still held, demonstrated repeatedly over time |
Each of these is already produced by another capability. Trust adds interpretation, not new instrumentation. That is why it is composition rather than a new subsystem.
The properties that follow are structural. A record of ten thousand acknowledged Actions cannot be fabricated, because producing it requires spending real LUME and performing real work that others independently confirmed. A retention history cannot be back-dated, because the proofs are on chain. Behavior is expensive to fake in a way that self-reported reputation is not.
## Reputation is a vector, not a score
Collapsing a participant's history into one number would discard the information that makes it useful.
An agent may have an extensive, clean record for research work and no record at all for handling capital. Those are different questions, and the distinction is exactly what a caller needs. The trust record is therefore multi-dimensional and context-dependent. It is also time-weighted, because recent behavior is more predictive than old behavior.
The practical consequence is that trust is an input to routing and selection rather than a badge. A caller asks whether this participant has a record in the specific context at hand.
## Trust composes through delegation
Because authority is granted on chain, trust follows the same paths authority does.
If an enterprise delegates to an agent, and that agent grants a narrower capability to a specialist, there is a verifiable chain from the original authority to the final actor. The record can weigh that path. Trust extends past direct relationships without anyone having to assert it.
Reputation is also portable. It attaches to a LumeraID rather than to a venue, so a record earned in one context carries into another. Key rotation preserves it, which is what allows a long-lived identity to accumulate history without becoming brittle.
## Commitments, not content
The trust record is built from commitments, metadata, and outcomes. It is not built from stored inputs and outputs.
A receipt commits to what happened. It does not publish what was said. Content stays off chain and reachable only under identity-gated authorization. This is a deliberate boundary. A trust record that required publishing the work it evaluated would make the record and the privacy of its subject mutually exclusive, and the Protocol declines that trade.
## Where trust begins
Trust does not have to wait for an execution layer to exist, because storage already produces the right kind of evidence.
Every participant storing data on the network generates behavioral records from its first write. Retention challenges passed and failed, settlements, and retrieval patterns are all anchored to identity and all objectively checkable. Those are the first edges of the record.
This is the practical reason Durable Memory is the capability to build against first. It is useful on its own, and using it produces the evidence every later capability reads.
The subject that trust attaches to.
The capability that seeds the record today.
# Trust Graph
Source: https://docs.lumera.io/intelligence-layer/trust/trust-graph
The reputation mechanics running on Lumera today, the evidence they are compiled from, and how scores decay and drive enforcement.
The trust graph is often described as something a protocol builds later, once it has history to draw on. On Lumera the first subgraph already operates, and it decides which operators receive work and get paid.
This page covers those mechanics. For why trust belongs to the Protocol and how it relates to identity, see [Trust](/intelligence-layer/trust).
## Reputation already governs who gets work
`x/supernode` maintains a ranked list of SuperNodes, and `x/action` routes work to the top of it. Rank is a function of stake and performance, and performance comes from the epoch reports collected by `x/audit`.
That loop is a complete reputation system. Behavior is observed, compiled into a position, and the position determines income. An operator does not choose its rank and cannot assert it.
What makes it credible is the cost of manipulation. Rank cannot be bought without bonding real stake under LEP-3, and it cannot be earned without performing work that other nodes verify.
## Evidence the graph is compiled from today
Every input below is produced by a capability that already runs.
| Evidence | Source | What it demonstrates |
| - | - | - |
| Storage challenge outcomes | Deterministic challenger and observer quorum over the SuperNode mesh | Whether a node holds what it claims |
| Retained volume | LEP-4 epoch metrics, `cascade_kademlia_db_bytes` | Scale of obligation carried |
| Report freshness | Epoch report submission | Liveness as a service participant |
| Action finalization history | `x/action` state transitions | Work completed against work abandoned |
| Bond status | Validator delegation under LEP-3 | Capital committed and currently at risk |
None of it is self-attested in the way a review or an endorsement is. Retained volume is self-reported, which is why it passes through smoothing, a growth cap, and a ramp-up before it becomes payout weight, and why challenges exist to test the claim independently.
## Three scores, deliberately separate
LEP-6, Storage-Truth Enforcement and Ticket-Driven Self-Healing, is approved and specifies the scoring model explicitly. The scores are computed and applied only while the `x/audit` parameter `storage_truth_enforcement_mode` is above `UNSPECIFIED`. The design decision worth understanding is the separation.
| Score | Question it answers | Used for |
| - | - | - |
| **Node suspicion** | Is this operator failing its obligations? | Audit enforcement against the node |
| **Ticket deterioration** | Does this piece of data need repair? | Scheduling self-healing |
| **Reporter reliability** | Can I trust what this node says about others? | Weighting a node's reports and its eligibility to challenge |
Collapsing these into one number would break all three. Data can deteriorate because several holders churned, with no single operator at fault. An operator can fail while the data survives elsewhere. And a node can be a reliable storer while being an unreliable witness.
Keeping them apart means an enforcement decision and a repair decision never contaminate each other.
## Reputation decays by construction
Each score decays as epochs pass without new evidence, applied lazily on update.
```text theme={null}
score_new = floor(score_old × decay^epochs_since_last_update) + delta
```
Node suspicion decays at 0.92 per epoch. Reporter reliability and ticket deterioration decay at 0.90.
This is not leniency. It is what makes the score a **current assessment** rather than a permanent record. An operator that failed twenty epochs ago and has performed cleanly since is a different counterparty than one failing now, and a system that could not express that difference would be useless for routing decisions.
Recovery from an enforcement band requires both decay below the threshold and a run of clean passes. Time alone does not clear a fault.
## Scores map to graduated consequence
Node suspicion drives action in bands rather than at a single cliff.
| Band | Consequence |
| - | - |
| Low | Baseline challenge rate, no action |
| Elevated | Storage watch, increased challenge frequency |
| High | Probation |
| Severe | Postpone candidate, then strong postpone |
Escalation also considers pattern rather than magnitude alone. Repeated failures on distinct data, or failures across both recently assigned and long-held data, escalate faster than the same score reached by isolated events. Failing at both ends is a different signal than failing once.
## Scoring the witness, not just the subject
Reporter reliability is the least obvious of the three and the most interesting.
A node that reports failures which independent rechecks overturn accumulates a penalty. So does a node whose negative reports run persistently above the network median without being confirmed. The consequence is that its future reports carry less weight, and at the extreme it becomes temporarily ineligible to act as a challenger.
This closes an attack that most reputation systems leave open. If reporting badly about a competitor were free, it would be rational. Here the report is itself evidence, and being wrong about others is a fault attributable to you.
Provisional suspicion contributed by a low-reliability reporter is scaled down until an independent recheck confirms it. Accusation and finding are separate states.
## What the graph does not yet cover
Stated plainly, because the boundary matters when evaluating the architecture.
* **Operators only.** Today's reputation is about SuperNodes serving work. Requesters and agents accumulate history in the on-chain record, but no score is compiled from it.
* **Storage-weighted.** The strongest signals concern retention, because storage is the mature service. Reputation for other work types arrives with those work types.
* **Not yet delegation-aware.** Trust composing along delegation chains is described in [Trust](/intelligence-layer/trust) as architecture and depends on the claim model.
* **Not a public score API.** Rank is observable. A general-purpose reputation query surface is not part of the current interface.
## Inspecting it
Rank and node state are readable on chain and through the explorer, so you can see which operators are serving your actions and what state they are in.
```bash theme={null}
lumerad query supernode list-super-nodes
```
Track the specification in the [LEP repository](https://github.com/LumeraProtocol/lumera/tree/master/docs/leps).
Why reputation is a Protocol capability.
What happens to an operator when scores rise.
How retention converts into payouts.
The service most evidence is produced by.
# Verifiable Execution
Source: https://docs.lumera.io/intelligence-layer/verifiable-execution
How work is requested, performed by SuperNodes, evidenced by receipts, and settled only on confirmed completion.
Verifiable Execution is the capability that lets one participant rely on work performed by another without trusting the party that performed it.
It is the existing Action framework applied to a wider range of work. The lifecycle does not change. A request is registered on chain, a fee is escrowed, SuperNodes in the Execution Plane perform the work, and settlement follows confirmed completion. Storage was the first kind of work to move through that lifecycle. It is not the only kind it can carry.
## Actions and Agents
Two terms do distinct jobs, and the distinction is the basis of the whole model.
An **Action** is a single unit of work with an on-chain record and a defined lifecycle. It is stateless and atomic. It has a requester, a fee, a set of SuperNodes that serve it, and a terminal state.
An **Agent** is a process that pursues a goal by composing Actions. It retrieves state, requests work, evaluates results, and acts on them, without a person driving each step.
The Protocol commits Actions. Agents are the reason Actions need to be composable, evidenced, and settleable without human supervision. Agents are not a privileged category inside the Protocol. They are participants, with identities, that use the same interfaces anything else uses.
## Why execution is verifiable
The Protocol does not ask a requester to trust the operator that served them. It makes completion demonstrable.
Three properties do the work.
**Escrow before execution.** The fee is committed on chain when the Action is registered, before any work begins. The requester's funds are locked and the operator's compensation is contingent. Neither side is exposed to the other's good behavior.
**A receipt for each step.** Work produces signed evidence attributable to the identity that performed it. A receipt is a commitment to what happened, not a copy of the content. It records that a step occurred, by whom, over what inputs, with what result shape.
**Settlement gated on acknowledgment.** Payment releases when completion is confirmed against the on-chain record. Actions that expire before finalization refund the escrow. The requester is not paying for a promise, and the operator is not serving on credit.
Together these mean a requester can verify that work happened without having watched it happen, and without a relationship with the operator.
## Verification and its limits
Verification is deliberately scoped to what can be checked objectively.
Some work is reproducible. A deterministic computation with declared inputs and outputs can be re-executed by an independent SuperNode and compared. Disagreement is a provable fault.
Some work is not. A model producing an answer is not reproducible in that sense, and no amount of re-execution settles whether the answer was good.
The Protocol handles the two cases differently rather than pretending they are the same. What is objectively checkable is verified and can carry penalties. That includes timeouts, missing or invalid receipts, wrong versions, unavailable artifacts, deterministic mismatches, and policy violations. What is subjective is not slashed. It flows to the [Trust](/intelligence-layer/trust) record, where a history of outcomes prices it over time.
## Authority is graded
An answer from a model is not a permission. The architecture makes that explicit by grading what a result entitles a caller to do.
| Result | Authority it carries |
| - | - |
| Advisory result | An answer with a trace and a receipt. No automatic effect. |
| Verified advisory result | An independent check confirmed structure, citations, and policy compliance. Higher confidence, still advisory. |
| Deterministic result | Reproducible by independent nodes. Objective authority. |
| Action plan | A structured recommendation with preconditions and limits. The calling application decides. |
| Protocol-native effect | Reserved for tightly scoped operations, and only where identity, escrow, audit, and rollback semantics are established. |
The practical consequence for a developer is that irreversible effects stay in the application. The Protocol returns signed results and plans. The application revalidates and executes. That ordering is intentional, and it is what makes the model safe to build on before a trust record has depth.
## Composition
Work that no single Action completes is expressed as composition rather than as a new subsystem.
A composed workflow is published as a content-addressed, versioned bundle. The chain registers its hash and metadata. The payload is stored in [Cascade](/cascade/how-cascade-works). Invoking it means invoking a specific version by hash, which makes an execution reproducible and auditable rather than incidental.
Execution follows a runner and verifier pattern. One SuperNode executes. An independent SuperNode checks what is objectively checkable, including structure, schema conformance, declared citations, policy compliance, and any deterministic steps by replay. Payment depends on lifecycle completion, never on a judgment about answer quality.
A composed result is trustworthy because each component is individually evidenced, individually settled, and individually auditable. It is not trustworthy because the arrangement is elaborate.
How escrow and settlement are funded.
How authority is granted and checked.
# Actions and the Action Lifecycle
Source: https://docs.lumera.io/intelligence-layer/verifiable-execution/actions
How work is requested on Lumera, the states an action moves through, and why the lifecycle is the Protocol's extension point.
An Action is the unit of work on Lumera. Every Protocol Service request becomes an action, which is an on-chain record with an identifier, a creator, a state machine, and an escrowed fee.
Actions are handled by `x/action`. The module is deliberately service-agnostic. It knows how to register work, hold a fee, route the request to operators, verify completion, and settle. It does not know what the work is.
## Action types
Two types exist today, and both run through the same lifecycle.
| Type | Work performed | Priced by |
| - | - | - |
| **Cascade** | Erasure-code a file and store it permanently across the SuperNode mesh | Size of the data |
| **Sense** | Evaluate media for near duplicates and record a verdict | Size of the data and the analysis performed |
The difference between them is what SuperNodes do in the execution phase. Registration, escrow, verification, and settlement are identical. That is what makes adding a service type a smaller change than it appears, and it is the clearest example of the Protocol growing by composition rather than reinvention.
## The record on chain
Each action is stored keyed by `action_id` and carries its type, creator, current state, and escrowed fee. You can read any of it directly.
```bash theme={null}
lumerad query action action
```
The `action_id` is returned in an `action_registered` event when the registering transaction is included, and it remains the permanent on-chain handle for the work.
## States
| State | Meaning |
| - | - |
| `UNSPECIFIED` | Zero value. Also the sentinel a handler returns for no state change |
| `PENDING` | Registered on chain and the fee is escrowed. No operator has begun work |
| `PROCESSING` | One or more SuperNodes are executing the request |
| `DONE` | Completion was verified on chain. The escrowed fee is released |
| `APPROVED` | Used by cross-chain flows that require an explicit approval step before settlement |
| `REJECTED` | Reserved in the enum. No chain logic sets it |
| `FAILED` | Finalization was rejected. The escrow is neither released nor refunded |
| `EXPIRED` | The expiration passed while the action was still `PENDING` or `PROCESSING`. The escrow returns to the creator |
Wire values carry the `ACTION_STATE_` prefix, as in `ACTION_STATE_DONE`. The terminal states are what a caller should build against. `DONE` means the chain verified completion, not that an operator claimed it.
## The lifecycle
The creator submits `MsgRequestAction` with the action type, the service metadata, an expiration, and the fee. The chain validates the message, escrows the fee, and emits the `action_id`.
For Cascade, registration also carries the content hash and an availability commitment. See [How Cascade works](/cascade/how-cascade-works).
`x/action` routes the request to the ranked top set of SuperNodes maintained by `x/supernode`. Rank is a function of stake and performance, so assignment is a consequence of on-chain state rather than a negotiation.
Selected SuperNodes perform the work in the Execution Plane. Data moves directly between the client and the operators and does not pass through consensus, which is why throughput here is not bounded by block time.
An operator submits the finalization message with whatever evidence the action type requires. For Cascade that includes Merkle proofs for the chunks committed at registration, which the chain verifies before accepting the finalization.
On verified finalization the state becomes `DONE` and the escrow releases to the participating SuperNodes. If finalization never arrives before the expiration, the state becomes `EXPIRED` and the escrow returns. A finalization the chain rejects settles nothing. See [Escrow and settlement](/intelligence-layer/economic-coordination/escrow-and-settlement).
## Expiration
Every action carries an expiration supplied at registration. It is a deadline for operators to complete the work, and it exists so escrowed funds cannot be held indefinitely by inaction.
An expired action refunds. A caller therefore never has capital stranded by an unresponsive operator, which is what makes it safe to register work with a party you have no relationship with.
## Why the lifecycle matters architecturally
The lifecycle is the Protocol's extension point. A new capability arrives as a new action type moving through these same states, priced the same way, escrowed the same way, and settled on the same conditions.
That has a practical consequence worth internalizing. What you learn from integrating one service transfers to the next. The verification evidence differs by type, and everything around it does not.
What happens to the fee at each state transition.
How the fee is calculated.
The lifecycle applied to permanent storage.
`x/action` state and messages.
# Networks and Endpoints
Source: https://docs.lumera.io/networks
Network configuration, public endpoints, genesis files, and connection information for Lumera Mainnet and Testnet.
Lumera operates two public networks: **Mainnet**, the production network for applications and protocol services, and **Testnet**, a development environment for testing integrations, deploying smart contracts, and onboarding validators and SuperNode operators.
Both networks use the same `lumerad` binary and protocol architecture. Network-specific configuration—including chain IDs, genesis files, seed nodes, and public endpoints—is provided below.
## Public Endpoints
Public endpoints allow applications and developers to query network state without running a node. These endpoints may be rate-limited and should not be treated as guaranteed production infrastructure. For high-volume or production workloads, run your own node or use a dedicated provider.
| Service | Mainnet | Testnet |
| - | - | - |
| **gRPC** | `grpc.lumera.io:443` | `grpc.testnet.lumera.io:443` |
| **RPC** | `https://rpc.lumera.io` | `https://lumera-testnet-rpc.polkachu.com` |
| **LCD (REST)** | `https://lcd.lumera.io` | `https://lumera-testnet-api.polkachu.com` |
| **SN-API** | `https://snapi.lumera.io` | `https://snapi.testnet.lumera.io` |
Community infrastructure providers such as [Polkachu](https://www.polkachu.com/networks/lumera) also operate public Lumera endpoints. Validator snapshots and additional connection resources are available in the [validator setup guides](/validators/mainnet/node-setup).
## Network Configuration
Select a network below to view its chain parameters, genesis file, seed nodes, and node configuration.
Mainnet is Lumera's production network. LUME carries economic value, validators and SuperNodes earn protocol rewards, and data stored through Cascade is retained under the network's permanent-storage model. Protect all Mainnet keys using production-grade security practices.
### Network Parameters
| Parameter | Value |
| - | - |
| **Chain ID** | `lumera-mainnet-1` |
| **Native denom** | `ulume` |
| **Minimum gas price** | `0.025ulume` |
| **Explorer** | [Lumera Portal](https://portal.lumera.io/lumera-mainnet-1) |
### Genesis File
Download the network genesis file from the official `lumera-networks` repository:
```bash theme={null}
curl -o genesis.json https://raw.githubusercontent.com/LumeraProtocol/lumera-networks/master/mainnet/genesis.json
```
Move the file into the node configuration directory:
```bash theme={null}
cp genesis.json ~/.lumera/config/genesis.json
```
The claims file loaded into chain state at genesis is published next to it: [mainnet/claims.csv](https://raw.githubusercontent.com/LumeraProtocol/lumera-networks/master/mainnet/claims.csv).
### Seed Nodes
Add the network seed node to the `[p2p]` section of `~/.lumera/config/config.toml`:
```toml theme={null}
seeds = "10ed1e176d874c8bb3c7c065685d2da6a4b86475@seed-lumera.ibs.team:16656"
```
### Minimum Gas Price
Set the minimum accepted gas price in `~/.lumera/config/app.toml`:
```toml theme={null}
minimum-gas-prices = "0.025ulume"
```
### Quick Configuration
```bash theme={null}
# Initialize node (replace with your node name)
lumerad init --chain-id lumera-mainnet-1
# Download genesis
curl -o ~/.lumera/config/genesis.json \
https://raw.githubusercontent.com/LumeraProtocol/lumera-networks/master/mainnet/genesis.json
# Set seeds in config.toml
sed -i 's/seeds = ""/seeds = "10ed1e176d874c8bb3c7c065685d2da6a4b86475@seed-lumera.ibs.team:16656"/' \
~/.lumera/config/config.toml
# Set gas price in app.toml
sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025ulume"/' \
~/.lumera/config/app.toml
# Start the node
lumerad start
```
Never share your validator key at `~/.lumera/config/priv_validator_key.json` or your operator key mnemonic. Back both up securely before starting the node.
Testnet is where new validators onboard, developers test integrations, and SuperNode operators rehearse before staking on mainnet. Testnet LUME has no value. Request some from the [faucet](/faucet).
### Network Parameters
| Parameter | Value |
| - | - |
| **Chain ID** | `lumera-testnet-2` |
| **Native denom** | `ulume` |
| **Minimum gas price** | `0.025ulume` |
| **Explorer** | [Lumera Portal](https://portal.testnet.lumera.io/lumera-testnet-2) |
| **Faucet** | [Portal faucet](https://portal.testnet.lumera.io/lumera-testnet-2/faucet) |
### Genesis File
Download the network genesis file from the official `lumera-networks` repository:
```bash theme={null}
curl -o genesis.json https://raw.githubusercontent.com/LumeraProtocol/lumera-networks/master/testnet-2/genesis.json
```
Move the file into the node configuration directory:
```bash theme={null}
cp genesis.json ~/.lumera/config/genesis.json
```
The claims file loaded into chain state at genesis is published next to it: [testnet-2/claims.csv](https://raw.githubusercontent.com/LumeraProtocol/lumera-networks/master/testnet-2/claims.csv).
### Peers
Testnet has no seed node. Download the address book that Lumera's snapshot service keeps current; your node dials peers from it:
```bash theme={null}
curl -fsSL -o ~/.lumera/config/addrbook.json \
https://snapshots.lumera.io/testnet/addrbook.json
```
### Minimum Gas Price
Set the minimum accepted gas price in `~/.lumera/config/app.toml`:
```toml theme={null}
minimum-gas-prices = "0.025ulume"
```
### Quick Configuration
```bash theme={null}
# Initialize node (replace with your node name)
lumerad init --chain-id lumera-testnet-2
# Download genesis
curl -o ~/.lumera/config/genesis.json \
https://raw.githubusercontent.com/LumeraProtocol/lumera-networks/master/testnet-2/genesis.json
# Download the address book (testnet has no seed node)
curl -fsSL -o ~/.lumera/config/addrbook.json \
https://snapshots.lumera.io/testnet/addrbook.json
# Set gas price in app.toml
sed -i 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025ulume"/' \
~/.lumera/config/app.toml
# Start the node
lumerad start
```
Use the [Lumera Portal](https://portal.testnet.lumera.io/lumera-testnet-2) to watch your validator status, inspect transactions, and confirm your node produces blocks.
## Node Requirements
The following specifications are the minimum recommended baseline for a production validator. SuperNode operators should provision additional CPU, memory, storage, and network capacity based on the protocol services they intend to support.
| Component | Minimum |
| - | - |
| **CPU** | 8+ cores |
| **Memory** | 64 GB RAM |
| **Storage** | 2 TB+ SSD |
| **Network** | High bandwidth with low latency |
Underpowered validators may miss blocks, become jailed, or incur slashing penalties. SuperNodes that fail performance or availability requirements may fall out of the active service set and lose protocol-service rewards.
Note: Chain state and protocol-service storage requirements grow over time. Maintain sufficient capacity headroom and continuously monitor disk usage, memory utilization, network performance, and service availability.
## EVM Access
Beginning with `v1.20.0`, Lumera nodes enable the EVM JSON-RPC server and OpenRPC discovery by default, allowing a node to support Ethereum-compatible tooling such as Hardhat, Foundry, and standard wallet integrations.
Mainnet and Testnet may upgrade on different schedules. Confirm the software version currently deployed on a network before relying on EVM functionality. See the [Smart Contracts overview](/smart-contracts/overview) for configuration and development details.
## Resources and Support
Join our growing community and use the following resources for technical support, source code, protocol announcements, and network upgrade information.
Get help with Validator, SuperNode, and developer questions.
Read the source for `lumerad`, the `supernode` daemon, and the SDKs.
Follow protocol announcements and upgrade notices.
## Next Steps
Connect to Lumera and store your first file through Cascade.
Request Testnet LUME for development and testing.
Configure a node and prepare to join the validator set.
# Network status and upgrades
Source: https://docs.lumera.io/networks-status
Current mainnet and testnet software versions, the next scheduled upgrade, and the applied upgrade history.
Chain versions and upgrade heights change, and any value written into a page goes stale. This page records the current state with a timestamp, and every figure here is one command away from being re-checked.
## Check it yourself
These queries are the source of truth. Run them before relying on anything below.
```bash Mainnet theme={null}
# running version and height
curl -s https://rpc.lumera.io/abci_info | jq -r '.result.response | "\(.version) at height \(.last_block_height)"'
# scheduled upgrade, null when none is on chain
curl -s https://lcd.lumera.io/cosmos/upgrade/v1beta1/current_plan | jq .
```
```bash Testnet theme={null}
curl -s https://lumera-testnet-rpc.polkachu.com/abci_info | jq -r '.result.response | "\(.version) at height \(.last_block_height)"'
curl -s https://lumera-testnet-api.polkachu.com/cosmos/upgrade/v1beta1/current_plan | jq .
```
From a synced node, `lumerad status` and `lumerad query upgrade plan` return the same information.
## Current versions
| | Mainnet (`lumera-mainnet-1`) | Testnet (`lumera-testnet-2`) |
| - | - | - |
| `lumerad` | `v1.12.0` | `v1.20.2-rc1` |
| Height when checked | 7,184,647 | 6,900,051 |
| Scheduled upgrade | None on chain | None on chain |
| SuperNode release in widest use | `v2.5.2` | `v2.6.x-testnet` |
Checked 2 October 2026. SuperNode versions vary across operators; the live distribution is in the [version matrix](https://lumescope.lumera.io/v1/version/matrix).
Mainnet runs the `v1.12.0` line and testnet runs the `v1.20.x` line, so testnet carries features mainnet does not yet have, including the EVM. Check a feature's availability on your target network before building against it.
## Next upgrade
Neither network has an upgrade plan on chain. `current_plan` returns `null` on both.
The mainnet EVM upgrade (Aurora Convergence, the `v1.20.x` line) was planned for block `6,715,000` and did not execute; mainnet passed that height on `v1.12.0`. It has been rescheduled and the new date is to be announced. See [the EVM upgrade](/upgrades/evm-upgrade).
## Applied upgrades
Heights at which each upgrade handler executed, read from `applied_plan`. A dash means the upgrade was never applied on that network.
| Upgrade | Mainnet | Testnet |
| - | - | - |
| `v1.6.1` | 434,000 | — |
| `v1.8.0` | — | 1,740,000 |
| `v1.8.4` | 2,270,000 | 1,922,000 |
| `v1.9.0` | 2,964,000 | 2,630,000 |
| `v1.10.1` | 3,770,000 | 3,410,000 |
| `v1.11.0` | — | 3,763,000 |
| `v1.11.1` | 4,218,000 | — |
| `v1.12.0` | 5,193,000 | 4,833,000 |
| `v1.20.0` | — | 5,547,000 |
| `v1.20.1` | — | 5,615,000 |
| `v1.20.2` | — | 6,149,000 |
Query any one of them directly:
```bash theme={null}
curl -s https://lcd.lumera.io/cosmos/upgrade/v1beta1/applied_plan/v1.12.0 | jq .
```
Build your node from a tag that carries every handler your target network has already applied. A binary missing a handler halts at that upgrade height and cannot sync past it. Testnet has applied `v1.20.2`, so a testnet node needs `v1.20.2` or newer.
## Next steps
Chain IDs, endpoints, and denominations.
What Aurora Convergence changes and what operators must do.
# Protocol Economics
Source: https://docs.lumera.io/protocol-revenue
How issuance, transaction fees, protocol-service revenue, and long-term sustainability work across Lumera.
Lumera's economic model coordinates four distinct functions: securing consensus, pricing block space, compensating protocol-service operators, and funding long-lived network obligations.
Newly issued LUME rewards validators and delegators for securing the network. Transaction fees compensate block producers while permanently removing a portion of supply. Protocol-service fees are paid by applications requesting work and earned by the SuperNodes that perform it. Everlight supports obligations—such as permanent data retention—that continue long after an individual service request has settled.
| Economic flow | Source | Primary recipients | Purpose |
| - | - | - | - |
| Block Rewards | Newly issued LUME | Validators and Delegators | Secure consensus and maintain a target bonded ratio |
| Transaction Fees | Gas paid on transactions | Validators and Delegators, with a portion burned | Price block space, deter spam, and offset issuance |
| Protocol-Service fees | Payments for executing Actions | SuperNodes | Compensate operators that provide decentralized services |
| Sustainability Funding | Protocol reward flows, fee shares, and endowments | Eligible service operators | Fund long-lived protocol obligations |
## Block Rewards
Newly issued LUME is distributed each block to validators and their delegators in exchange for securing consensus. Issuance is adaptive rather than fixed: it increases when the bonded share of supply falls below the protocol's target and decreases when staking participation exceeds it. See [LUME supply mechanics](/token).
| Allocation | Share |
| - | - |
| Validators and delegators | 98% |
| Community pool | 2% |
The community pool supports protocol development, ecosystem initiatives, and other governance-approved expenditures.
## Transaction Fees
The burn creates a direct link between network activity and LUME supply. As transaction volume increases, more LUME is permanently removed from circulation, partially offsetting the dilution created by security issuance.
| Allocation | Share |
| - | - |
| Validators and delegators | 80% |
| Burned | 20% |
The burn is the network's deflationary link to usage: the more the chain is used, the more LUME is permanently destroyed. This is not a fixed emission schedule running independently of activity. It responds to it.
## Protocol-Service Fees
Protocol-service fees are Lumera's demand-driven economic flow. They are paid by applications requesting decentralized services and earned by the SuperNodes that perform and verifiably complete the work.
**Who pays.** Users, developers, smart contracts, and applications that request protocol Actions. Fees are established when an Action is registered, escrowed on-chain, and released according to the service's completion and settlement rules.
**Who earns.** SuperNodes earn fees for the protocol services they successfully provide. Service revenue is distinct from staking rewards: operating a SuperNode is an independent economic activity requiring infrastructure, availability, and service-specific capabilities.
**How fees are calculated.** Each protocol service prices the resources and obligations it consumes. Pricing may reflect data size, execution requirements, verification costs, duration, or other service-specific factors established by protocol parameters.
### Example: Cascade Pricing
Cascade uses a base registration fee plus a size-based component reflecting the amount of data placed into permanent storage.
| Component | Basis |
| - | - |
| **Base fee** | Charged when the storage Action is registered |
| **Size-based fee** | Calculated from the amount of data stored |
| **Long-term retention** | Supported through Everlight rather than recurring charges to the uploader |
Current parameters are queryable on-chain and adjustable through governance. See [How Cascade Works](/cascade/how-cascade-works) for current pricing and lifecycle details.
### Keeping Service Pricing Competitive
Protocol-service pricing must account for two competing requirements: services should remain commercially competitive for users, while operator compensation must remain sufficient to support reliable infrastructure.
Where enabled by a service's pricing model, a **dynamic adjustment factor** can reduce the effect of changes in LUME's market value on real-world service pricing. Without such an adjustment, services denominated entirely in an appreciating native asset could become increasingly expensive relative to centralized alternatives.
A separate **base-cost adjustment** can account for changes in the real-world expenses operators bear, including hardware, storage, bandwidth, and power. Together, these mechanisms are intended to preserve affordable service pricing without undermining the economics required to retain capable operators.
## Sustainability and Everlight
Some protocol services create obligations that outlive the transaction that created them. Permanent storage is the clearest example: a single upfront payment must support data retention long after the original Action has settled.
Per-use service fees and long-term sustainability funding therefore serve different economic purposes. Service fees compensate operators for completing immediate work. **Everlight** supports persistent obligations by directing protocol reward flows, registration-fee shares, and endowment yield toward eligible operators over time.
SuperNodes must continue proving that required data remains available to remain eligible for retention compensation. Permanence is therefore supported by ongoing economic incentives and cryptographic evidence rather than by a contractual promise from a centralized company.
Everlight provides the model for funding other long-lived protocol obligations as Lumera expands, including persistent artifacts, service availability, and additional infrastructure that cannot be sustained through one-time execution fees alone. See [Everlight](/cascade/everlight/overview).
## How the Economic Flows Interact
Lumera's economic flows are designed to reinforce one another while serving distinct purposes.
Security issuance rewards validators and delegators for maintaining consensus, but increases circulating supply. Transaction-fee burns offset a portion of that issuance in proportion to network activity. Protocol-service fees compensate the SuperNodes that provide capacity, allowing infrastructure supply to grow in response to application demand rather than relying solely on higher token issuance.
Everlight closes the remaining gap by funding obligations that persist after an individual Action settles. This prevents permanent services from accumulating unfunded liabilities and allows one-time service payments to support long-lived outcomes.
The resulting loop connects usage directly to network capacity and economic security: applications consume services, fees compensate operators, increased demand attracts additional capacity, and transaction activity removes a portion of LUME from circulation.
### Illustrative Cascade Transaction
At current mainnet parameters, a Cascade Action includes a base fee of `30,000 ulume` plus `10 ulume` per KB. That places a 1 MB upload at approximately `0.04 LUME`. Testnet uses a `10,000 ulume` base, so the same upload costs about `0.02 LUME` there.
The service fee is escrowed when the Action is registered and released according to the Cascade settlement lifecycle. The registration transaction also incurs gas, of which 80% is distributed to validators and delegators and 20% is burned. Everlight subsequently supports the network's ongoing retention obligation.
These figures are governance-controlled protocol parameters and may change. Query the network or refer the [Cascade documentation](/cascade/how-cascade-works) for current values.
Understand token utility, supply mechanics, staking, and burns.
Provide protocol services and earn service fees.
Learn how economic parameters and fee structures are governed.
# Governance Proposals
Source: https://docs.lumera.io/protocol/governance
Submitting proposals, voting, software upgrades, and protocol parameter changes on Lumera.
The Governance overview explains how protocol governance works and what it controls. This page focuses on participating in governance: submitting proposals, voting, software upgrades, and the operational mechanics of the governance module.
Lumera uses the Cosmos SDK governance module (`x/gov`). Protocol proposals are submitted and voted on entirely on-chain, allowing the network to evolve through transparent community coordination rather than centralized administration.
## Proposal lifecycle
Every governance proposal follows the same lifecycle from submission to execution.
A proposal is submitted with an initial deposit. Once the required minimum deposit is reached, the proposal enters the voting period.
During the voting period, holders of staked LUME cast votes. Validators vote with their delegated stake by default, while delegators may override their validator's vote for their own stake.
At the end of the voting period, votes are tallied according to the protocol's governance rules, including quorum, threshold, and veto requirements.
Approved proposals execute according to their proposal type. Parameter changes take effect automatically, while software upgrade proposals schedule coordinated network upgrades.
Supported vote options are:
* `yes`
* `no`
* `abstain`
* `no_with_veto`
A `no_with_veto` vote signals that a proposal is considered harmful or malicious rather than simply undesirable.
***
## Voting with lumerad
Query all governance proposals currently on-chain.
```bash theme={null}
lumerad query gov proposals
```
Vote on an active proposal.
```bash theme={null}
lumerad tx gov vote yes \
--from \
--chain-id lumera-mainnet-1 \
--fees 10000ulume
```
Replace `yes` with `no`, `abstain`, or `no_with_veto` as appropriate.
For Testnet, replace the chain ID with:
```text theme={null}
lumera-testnet-2
```
***
## Browsing proposals
Governance activity can also be viewed through the Lumera Portal.
* **Mainnet:** [https://portal.lumera.io](https://portal.lumera.io)
* **Testnet:** [https://portal.testnet.lumera.io/lumera-testnet-2](https://portal.testnet.lumera.io/lumera-testnet-2)
The Portal displays proposal status, voting windows, deposits, validator voting records, and final vote tallies.
***
## Software upgrades
Software upgrades are coordinated through on-chain governance.
Upgrade proposals specify the target software version together with an upgrade height. Once the blockchain reaches that height, block production pauses until validators install the approved binary and resume consensus.
Operators are encouraged to automate this process using **Cosmovisor**. See the [Validator Operations](/validators/mainnet/operations) guide for recommended upgrade procedures.
***
## Lumera Enhancement Proposals (LEPs)
Lumera Enhancement Proposals (LEPs) are the protocol's design documents. They describe architectural changes, protocol improvements, and new capabilities before those changes are proposed through on-chain governance.
LEPs are maintained in the Lumera repository:
[https://github.com/LumeraProtocol/lumera/tree/master/docs/leps](https://github.com/LumeraProtocol/lumera/tree/master/docs/leps)
A LEP documents the technical design and rationale behind a proposal. When implementation requires protocol changes, the corresponding governance proposal becomes the mechanism through which the network formally adopts that design.
***
## Governable protocol parameters
Most protocol behavior is configurable through governance rather than fixed in software.
Examples include:
* Protocol-service pricing
* Security issuance parameters
* Transaction fee distribution
* Everlight funding allocations
* SuperNode operating parameters
* Governance thresholds and voting periods
* Community Pool expenditures
Current parameter values can be queried directly from the network and may change through approved governance proposals.
***
## Related documentation
Learn how governance fits into Lumera's protocol architecture.
Configure automated software upgrades using Cosmovisor.
Explore the modules and parameters governed on-chain.
# Lumera module reference
Source: https://docs.lumera.io/protocol/modules
A guided map of the custom, EVM, and standard modules on the Lumera chain.
Lumera Protocol runs on the Cosmos SDK with five custom modules that implement the protocol logic, an EVM module family added by the v1.20.0 upgrade, and the standard Cosmos modules. This page maps each module, the state it holds, its key messages, and where the deeper documentation lives. For how the layers fit together, read the [architecture overview](/architecture).
| Module | Role | Deeper docs |
| - | - | - |
| `x/action` | Cascade and Sense action lifecycle | [How Cascade works](/cascade/how-cascade-works) |
| `x/supernode` | SuperNode registry, ranking, rewards, Everlight pool | [SuperNode overview](/supernodes/overview) |
| `x/lumeraid` | Identity anchored to Lumera addresses | [Architecture](/architecture) |
| `x/claim` | Legacy Pastel token claims | Not documented |
| `x/audit` | Epoch metric reports from SuperNodes | [Everlight for operators](/cascade/everlight/supernode-operators) |
## x/action
`x/action` records every Cascade and Sense request as an action, an on-chain record with a state machine. It escrows the service fee at registration and refunds it when an action expires unfinalized. States run `PENDING`, `PROCESSING`, then `DONE`, `FAILED`, or `EXPIRED`. Cross-chain flows can add `APPROVED` after an explicit approval.
The module stores action records keyed by `action_id`, each with its type, creator, current state, and escrowed fee. Its key messages are `MsgRequestAction` to register an action and `MsgApproveAction` for flows that require approval. Query any record with `lumerad query action action `.
Deeper docs live at [how Cascade works](/cascade/how-cascade-works) for storage flows, [Sense](/protocol/sense) for authentication flows, and the [cross-chain overview](/cross-chain/overview) for the approval path.
## x/supernode
`x/supernode` is the source of truth for SuperNodes. It registers nodes, maintains the ranked top list that `x/action` routes work to, tracks operational state and metrics, and pays rewards. A SuperNode must stay tied to an active validator to remain on the list.
The module stores each node's account, endpoint, stake delegation, and state. One state worth knowing is `STORAGE_FULL`, entered when a node's disk usage passes its configured limit. A `STORAGE_FULL` node is excluded from new storage assignments but stays eligible for compute work like Sense and keeps earning Everlight payouts for the data it already holds.
The module also hosts the Everlight SN Pool, a named module account that compensates SuperNodes for retaining Cascade data. Every payment period, measured in block height, the pool pays eligible nodes in proportion to the Cascade data they retain. The [Everlight overview](/cascade/everlight/overview) explains the funding model.
Registration happens on chain with `lumerad tx supernode register-supernode VALOPER SN_ENDPOINT SN_ACCOUNT`, signed by the validator operator. The stake requirement combines validator self delegation and SuperNode account delegation. Check the current minimum with `lumerad query supernode params`. The [registration guide](/supernodes/register) walks the full flow.
## x/lumeraid
`x/lumeraid` anchors identity records to Lumera addresses. There is no separate username. Your address is your identity and your private key controls the record. Signatures follow ADR-036, the Cosmos standard for signing arbitrary data, and authenticate Cascade uploads and downloads. The [architecture page](/architecture) covers how identity threads through the protocol.
## x/claim
`x/claim` handles token claims from the legacy Pastel Network, which Lumera evolved from. The claim flow is not part of the developer surface and is not documented here.
## x/audit
`x/audit` collects epoch reports from SuperNodes. Each epoch, nodes report service metrics defined by LEP-4, including `cascade_kademlia_db_bytes`, the amount of Cascade data a node holds. These reports feed SuperNode ranking and drive Everlight payouts. See [Everlight for SuperNode operators](/cascade/everlight/supernode-operators).
## EVM modules
The v1.20.0 upgrade added Cosmos EVM support through five modules. The EVM is live on testnet today. Mainnet runs v1.12.0 and gains the EVM with its upgrade.
| Module | Role |
| - | - |
| `x/vm` | Executes Solidity contracts on the Cosmos EVM |
| `x/feemarket` | EIP-1559 dynamic base fee for EVM transactions |
| `x/precisebank` | Bridges the 6 decimal `ulume` denom to the 18 decimal `alume` EVM denom |
| `x/erc20` | Maps Cosmos tokens to ERC-20 interfaces |
| `x/evmigration` | Migrates legacy accounts to EVM compatible keys, atomically and fee free |
Start building with the [smart contracts overview](/smart-contracts/overview). The [EVM upgrade page](/upgrades/evm-upgrade) covers what changed at the chain level and the migration path.
## Standard Cosmos modules
Lumera also ships the standard Cosmos SDK modules, which behave as they do on any Cosmos chain. `x/bank` handles transfers, `x/staking` runs delegated proof of stake, `x/gov` runs proposals and voting, `x/distribution` pays staking rewards, and `x/slashing` penalizes misbehaving validators. Governance behavior specific to Lumera is covered on the [governance page](/protocol/governance).
## Next steps
Content authentication built on x/action.
How proposals change module parameters.
The v1.20.0 upgrade that added the EVM module family.
# Sense content authentication
Source: https://docs.lumera.io/protocol/sense
Submit a file for AI powered near-duplicate analysis and record the result permanently on chain.
Sense is Lumera Protocol's AI powered content authentication service. Submit a file and top ranked SuperNodes analyze it with perceptual fingerprinting. A perceptual fingerprint is a feature vector that captures what the content looks like rather than its exact bytes. The network compares it against every previously registered asset and records a verifiable similarity result on chain through the `x/action` module. Slightly altered copies still match, which plain hash comparison cannot do.
This page covers what Sense is for, how an analysis runs, and how to submit and query Sense actions with `lumerad`.
## Use cases
Prove that a digital artwork is not a near duplicate of a registered asset before minting.
Verify the originality of visual or audio content before publishing or licensing it.
Establish a timestamped on-chain record that a file existed in a unique form at a specific block height.
Keep near duplicates from flooding marketplaces and diluting the value of originals.
## How Sense works
You submit a registration transaction carrying the hash of your file. The `x/action` module escrows the fee and assigns a unique `action_id`. The action starts in the `PENDING` state.
Top ranked SuperNodes, selected through `x/supernode`, pick up the action and process the file. Each one computes a perceptual fingerprint of the content. The state is `PROCESSING`.
The SuperNodes compare the fingerprint against the network's registered assets. They compute a similarity score and collect any duplicate candidates that exceed the near-duplicate threshold.
The aggregated result is committed and the action moves to `DONE`. The similarity score, duplicate candidates, and fingerprint hash become part of the permanent record, queryable by anyone.
An action still unfinalized at its `expirationTime` becomes `EXPIRED` and the escrowed fee is refunded. The [architecture page](/architecture) walks the full action state machine that Sense shares with Cascade.
## Submitting a Sense action
Register a Sense action with the `request-action` command. It takes four positional arguments, the action type, a metadata JSON string, the service price you offer, and an expiration timestamp.
```shell Register a Sense action theme={null}
lumerad tx action request-action SENSE '' \
--from \
--chain-id lumera-mainnet-1 \
--fees 10000ulume
```
```shell Register a Sense action theme={null}
lumerad tx action request-action SENSE '' \
--from \
--chain-id lumera-testnet-2 \
--fees 10000ulume
```
The metadata JSON carries the hash of the file you want analyzed in its `data_hash` field. The price is denominated in `ulume` and the expiration time is a Unix timestamp for when the action expires. After broadcasting, note the `action_id` returned in the transaction response.
## Querying Sense results
Once the action reaches `DONE`, the result lives in the on-chain action record.
```shell Query a Sense action theme={null}
lumerad query action action
```
The response includes the action metadata, the current state, and the encoded Sense result payload. For a decoded, human readable view over REST, use the [LumeScope API](/api/lumescope).
## Result fields
| Field | Meaning |
| - | - |
| Similarity score | A value between 0 and 1. Near 0 means highly unique, near 1 means a close match to a registered asset. |
| Duplicate candidates | Action IDs of previously registered files that exceeded the near-duplicate threshold. |
| Fingerprint hash | Hash of the perceptual fingerprint vector, usable to verify the result independently. |
| Block height | The block where the result was recorded, a tamper-proof timestamp. |
Sense results are permanent. Anyone can query the record, check the similarity score, and audit the duplicate candidates, which gives your assets a transparent provenance trail.
## Pricing
A Sense action pays two fees.
* A standard transaction fee in `ulume` for the registration message.
* A service fee escrowed by `x/action` and paid to the SuperNodes that run the analysis.
Sense costs less than Cascade because only the analysis result is stored, never the file itself. Governance can change fee parameters at any time.
## Next steps
How x/action and x/supernode coordinate Sense under the hood.
Decoded action results and network state over REST.
Permanent file storage, the sibling service to Sense.
# Get started with Lumera
Source: https://docs.lumera.io/quickstart
Connect to a Lumera network, fund an address, and pick the path for what you are building.
Lumera Protocol is a Cosmos SDK Layer 1 blockchain. The chain coordinates requests, payments, and proofs, while economically bonded SuperNodes perform storage, verification, and retrieval. You reach all of it through SDKs, smart contracts, or plain REST and gRPC.
This page gets you connected to a network and points you at the right section for what you are building. It takes a few minutes and needs nothing but `curl`.
This page is for application developers. Node operators can start with the [validator quickstart](/quickstart-validator) or the [SuperNode quickstart](/quickstart-supernode).
## What you can build
Lumera exposes four surfaces. Most applications use more than one.
| Surface | What it gives you | Start here |
| - | - | - |
| Protocol services | Permanent storage, identity, and verifiable execution through Actions | [Cascade](/cascade/overview) |
| Smart contracts | Solidity through Cosmos EVM and Rust through CosmWasm | [Smart contracts](/smart-contracts/overview) |
| Read APIs | Chain state, SuperNode metrics, and Action metadata | [API reference](/api/overview) |
| Network operation | Run a validator or a SuperNode | [Operate the network](/validators/overview) |
## Choose a network
Build on testnet first. Testnet tokens are free and the EVM is available there today.
| | Testnet | Mainnet |
| - | - | - |
| Chain ID | `lumera-testnet-2` | `lumera-mainnet-1` |
| RPC | `https://lumera-testnet-rpc.polkachu.com` | `https://rpc.lumera.io` |
| LCD (REST) | `https://lumera-testnet-api.polkachu.com` | `https://lcd.lumera.io` |
| gRPC | `grpc.testnet.lumera.io:443` | `grpc.lumera.io:443` |
| SN-API | `https://snapi.testnet.lumera.io` | `https://snapi.lumera.io` |
| Tokens | Free from the [faucet](/faucet) | Real LUME |
| Cosmos EVM | Available | Not yet available |
Public endpoints are rate limited. Run your own node or use a dedicated provider for production traffic. See [Networks and endpoints](/networks) for genesis files and seed nodes.
## Connect to the network
Query the RPC endpoint and check which network answers.
```bash theme={null}
curl -s https://lumera-testnet-rpc.polkachu.com/status | jq -r '.result.node_info.network'
```
You should see `lumera-testnet-2`. If you get nothing back, the endpoint is rate limiting you or is down. Try another provider from [Networks and endpoints](/networks).
The REST endpoint reports the application version the network is on.
```bash theme={null}
curl -s https://lumera-testnet-api.polkachu.com/cosmos/base/tendermint/v1beta1/node_info \
| jq -r '.application_version.version'
```
Match this version when you install `lumerad` or pin an SDK. Mainnet and testnet run different versions.
Lumera uses bech32 addresses with the `lumera` prefix. Get one from [Lumera Hub](/hub/wallet), from a Cosmos wallet such as Keplr, or from the CLI.
```bash theme={null}
lumerad keys add my-key
```
Save the mnemonic somewhere safe. See the [`lumerad` CLI reference](/sdk/lumerad-cli) for keyring backends.
Most operations cost gas, and protocol services escrow a service fee on top. Request testnet LUME from the [faucet](/faucet), then confirm the balance arrived.
```bash theme={null}
curl -s https://lumera-testnet-api.polkachu.com/cosmos/bank/v1beta1/balances/ | jq
```
Balances are in `ulume`, where 1 LUME is 1,000,000 `ulume`.
You are connected. Pick the path that matches what you are building.
## Choose your path
Use Cascade to store files that stay retrievable. Storage is paid once at upload, and reads are not metered by the protocol.
Deploy Solidity through the Cosmos EVM with MetaMask and Remix, or Rust through CosmWasm.
JavaScript for browsers and Node.js, Go for backends with direct SuperNode access, and Rust for system integrations.
Read chain state, SuperNode metrics, and Action metadata over REST, gRPC, and JSON-RPC.
## Next steps
How consensus, SuperNodes, and protocol services fit together.
Set up the JavaScript or Go SDK and configure your bundler.
The capabilities Lumera is building for autonomous software.
# Run a SuperNode
Source: https://docs.lumera.io/quickstart-supernode
What it takes to extend your validator with a SuperNode, condensed to one page.
A SuperNode extends a Lumera Protocol validator with Cascade storage, Sense verification, and AI inference. It earns additional rewards for that work on top of staking income.
This page is the condensed orientation. The full runbook lives in the [SuperNode section](/supernodes/overview).
## Before you start
You need four things.
* **A validator in a bonded state.** If you do not have one yet, start with [Run a validator](/quickstart-validator).
* **A separate server.** The SuperNode must never share a host with the validator. It keeps its own account, keys, and stake delegation, and talks to the chain over gRPC.
* **Access to the validator operator key.** It signs the registration transaction.
* **SuperNode hardware.** Plan for at least 8 CPU cores, 16 GB RAM, a 1 TB NVMe SSD, and a 1 Gbps connection. Ports 4444 (gRPC) and 4445 (P2P) must be reachable.
The pairing itself is two on-chain records. A registration signed by the validator operator binds the SuperNode endpoint and account to the validator, and a stake delegation from the SuperNode account backs it.
## The path
Four stages take you from a bonded validator to a registered SuperNode.
* **Provision a second server.** Ubuntu 22.04 LTS or later on x86\_64 hardware that meets the specs above.
* **Install with `sn-manager`.** The manager downloads the `supernode` binary, applies updates automatically, and supervises the service. Configuration lives in `~/.supernode/config.yml`.
* **Fund the SuperNode account and delegate.** LEP-3, a Lumera Enhancement Proposal, defines a dual stake rule. Your validator self-delegation plus the delegation from the SuperNode account must together meet the minimum, 10,000 LUME on testnet and 25,000 LUME on mainnet. Governance can change these values.
* **Register from the validator host.** The validator operator key signs the transaction that binds the SuperNode endpoint and account to your validator.
Two commands anchor the flow. The runbook walks through everything around them.
```bash theme={null}
# Check the current combined stake minimum
lumerad query supernode params
# Bind the SuperNode to your validator, signed by the operator key
lumerad tx supernode register-supernode
```
Verify the stake minimum before you register. The `minimum_stake_for_sn` parameter is set by governance and can change.
## Earning for retention
SuperNodes that keep Cascade data available receive ongoing payouts from the Everlight SN Pool, proportional to the data they retain. [Everlight for SuperNode operators](/cascade/everlight/supernode-operators) explains eligibility and payout mechanics.
## Next steps
Architecture, staking requirements, and hardware in full.
Install and configure the SuperNode with `sn-manager`.
How retention compensation works for operators.
# Run a Validator
Source: https://docs.lumera.io/quickstart-validator
What it takes to run a Lumera validator, condensed to one page.
Lumera Protocol is a Cosmos SDK Layer 1 blockchain secured by CometBFT. Validators produce blocks, vote on governance proposals, and keep the network honest. In return they earn staking rewards plus commission from their delegators.
This page is the condensed orientation. The full runbooks with every command live in the validator sections linked at the end.
## What you need
A dedicated x86\_64 server and some LUME. The table shows the published minimums for each network. The overview pages list the recommended specs.
| Component | Testnet | Mainnet |
| - | - | - |
| CPU | 4 vCPU (x86\_64) | 8 cores (x86\_64) |
| RAM | 8 GB | 32 GB |
| Storage | 250 GB NVMe SSD | 2 TB NVMe SSD |
| Network | 1 Gbps | 1 Gbps dedicated |
| OS | Ubuntu 22.04 LTS or later | Ubuntu 22.04 LTS or later |
Use NVMe SSDs. HDDs cannot sustain a live CometBFT node. ARM (including Apple Silicon) is not supported for production nodes.
On testnet you get free LUME from the [faucet](/faucet). On mainnet you need at least 1 LUME for the minimum self-delegation and fees, and far more than that to enter the active set.
## The path
Five stages take you from an empty server to a bonded validator.
* **Provision a server.** Ubuntu 22.04 LTS or later on x86\_64 hardware that meets the table above.
* **Install `lumerad` from the prebuilt release.** Each release ships a linux/amd64 binary, the CosmWasm shared library, and an installer, so you do not need Go or a compiler.
* **Sync from a snapshot.** Polkachu publishes snapshots for both networks. This is much faster than replaying every block from genesis.
* **Create and fund a key.** This wallet pays transaction fees and holds your self-delegation.
* **Submit the create-validator transaction.** Set your moniker, commission, and self-delegation, then confirm the validator appears on the Lumera Portal.
Match the binary version to the network before you install. For example, this returns the version mainnet currently runs.
```bash theme={null}
curl -fsSL https://lumera-rpc.polkachu.com/abci_info | jq -r .result.response.version
```
## Choose your network
Practice on testnet first. Tokens are free, mistakes cost nothing, and the process is identical. When your setup is solid, repeat it on mainnet with real stake.
* [Testnet validator guide](/validators/overview) covers `lumera-testnet-2`, currently on the `v1.20.2` line with the EVM live.
* [Mainnet validator guide](/validators/overview) covers `lumera-mainnet-1`, currently on `v1.12.0`.
The active set is capped at 50 validators ranked by total stake. Joining it means attracting more bonded stake than the smallest active validator, which your validator then displaces.
## Next steps
Rehearse the full setup with tokens that have no value.
Go to production with real stake on `lumera-mainnet-1`.
Extend your validator with storage and compute services.
# Go SDK
Source: https://docs.lumera.io/sdk/go
Build server-side applications and CLI tools that upload to Cascade and query the Lumera chain.
The [Go SDK](https://pkg.go.dev/github.com/LumeraProtocol/sdk-go) is the official SDK for backend services, CLI tools, and infrastructure that talk to Lumera Protocol. It puts chain queries, transactions, and Cascade file storage behind one client. The current release is v1.2.0 and it needs Go 1.26.
The SDK unifies three interfaces.
| Layer | Access via | Purpose |
| - | - | - |
| Lumera API (gRPC) | `client.Blockchain` | Chain queries and transactions for actions, SuperNodes, and fees |
| SuperNode SDK | `client.Cascade` | File upload, download, and event subscriptions |
| SnApi (gRPC) | Wrapped by `client.Cascade` | Direct SuperNode communication |
Unlike the [JavaScript SDK](/sdk/javascript), which routes file transfers through a REST gateway, the Go SDK talks to SuperNodes directly over gRPC on port 4444. Your machine needs network access to that port.
## Install
```bash theme={null}
go mod init your-project
go get github.com/LumeraProtocol/sdk-go
```
## Create a client
`lumerasdk.New` takes a config, a Cosmos SDK keyring, and options. Close the client when you are done.
```go main.go theme={null}
package main
import (
"context"
"log"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
lumerasdk "github.com/LumeraProtocol/sdk-go/client"
"go.uber.org/zap"
)
func main() {
ctx := context.Background()
kr, err := keyring.New("lumera", "test", "/tmp", nil)
if err != nil {
log.Fatal(err)
}
client, err := lumerasdk.New(ctx, lumerasdk.Config{
ChainID: "lumera-testnet-2",
GRPCEndpoint: "grpc.testnet.lumera.io:443",
RPCEndpoint: "https://rpc.testnet.lumera.io",
Address: "lumera1...",
KeyName: "my-key",
}, kr, lumerasdk.WithLogger(zap.NewExample()))
if err != nil {
log.Fatal(err)
}
defer client.Close()
}
```
The `test` keyring backend stores keys unencrypted. Use the `os` backend and a funded key for anything beyond local experiments. Never commit mnemonic files.
## Upload a file
`Upload` registers the action on chain, waits for inclusion, and streams the file to one of the selected SuperNodes. The result carries the `ActionID`, the permanent on-chain reference to your file.
```go theme={null}
res, err := client.Cascade.Upload(ctx, addr, nil, "./my-document.pdf")
if err != nil {
log.Fatal(err)
}
log.Println("Stored with action ID:", res.ActionID)
```
The block height of the registration transaction seeds the deterministic selection of processing SuperNodes. The SDK tries each selected node in order and uploads to the first that responds.
## Download a file
`Download` fetches the file by action ID into a local directory.
```go theme={null}
_, err := client.Cascade.Download(ctx, actionID, "./downloads/")
if err != nil {
log.Fatal(err)
}
```
## Query actions
`client.Blockchain` exposes the chain modules. Action queries return the on-chain state of a storage request.
```go theme={null}
action, err := client.Blockchain.Action.GetAction(ctx, "action-123")
if err != nil {
log.Fatal(err)
}
log.Printf("Action state: %s", action.State)
```
The state moves from `PENDING` to `PROCESSING` to `DONE`. An upload still unfinalized at its `expirationTime` ends at `EXPIRED` and the escrowed fee is refunded. Subscribe to task events with `client.Cascade.SubscribeToEvents` when you need progress callbacks instead of polling.
## Multiple accounts
The factory pattern reuses one configuration and one set of connections across several signers.
```go theme={null}
factory, _ := lumerasdk.NewFactory(lumerasdk.Config{
ChainID: "lumera-testnet-2",
GRPCEndpoint: "grpc.testnet.lumera.io:443",
RPCEndpoint: "https://rpc.testnet.lumera.io",
}, kr)
alice, _ := factory.WithSigner(ctx, aliceAddr, "alice")
bob, _ := factory.WithSigner(ctx, bobAddr, "bob")
defer alice.Close()
defer bob.Close()
```
## EVM and cross-chain support
The SDK is EVM ready. The keyring supports both `secp256k1` (Cosmos, coin type 118) and `eth_secp256k1` (EVM, coin type 60) key types. The repo ships examples for EVM balance reads, Ethereum format transfers, ERC20 conversion, and calling Lumera precompiles from the EVM. See the [smart contracts overview](/smart-contracts/overview) for what the precompiles expose.
Full ICS-27 interchain account support lets an app on another Cosmos chain drive Cascade through IBC. The [interchain accounts concept page](/cascade/concepts/interchain-accounts) explains the flow.
## Next steps
Config fields, client surfaces, and crypto helpers.
The storage flow behind Upload and Download.
Drive Cascade from another Cosmos chain.
# Go SDK reference
Source: https://docs.lumera.io/sdk/go-reference
Config fields, client surfaces, ICA helpers, and crypto utilities in the Lumera Go SDK.
This page lists the public surface of `github.com/LumeraProtocol/sdk-go`. For the generated API docs see [pkg.go.dev](https://pkg.go.dev/github.com/LumeraProtocol/sdk-go). For a guided start see the [Go SDK page](/sdk/go).
## Client factory
```go theme={null}
import lumerasdk "github.com/LumeraProtocol/sdk-go/client"
client, err := lumerasdk.New(ctx, cfg, kr, opts...)
defer client.Close()
```
### Config fields
| Field | Type | Description |
| - | - | - |
| `ChainID` | `string` | `lumera-testnet-2` or `lumera-mainnet-1` |
| `GRPCEndpoint` | `string` | Chain gRPC endpoint, for example `grpc.testnet.lumera.io:443` |
| `RPCEndpoint` | `string` | CometBFT RPC endpoint |
| `Address` | `string` | Your bech32 sender address |
| `KeyName` | `string` | Key name inside the keyring |
Options include `lumerasdk.WithLogger(*zap.Logger)`. The keyring argument is a standard Cosmos SDK `keyring.Keyring`.
### Factory for multiple signers
```go theme={null}
factory, err := lumerasdk.NewFactory(cfg, kr)
alice, err := factory.WithSigner(ctx, aliceAddr, "alice")
```
`NewFactory` shares one configuration and one set of transports. `WithSigner` returns a per account client. Close each client when done.
## Blockchain surface
`client.Blockchain` wraps the Lumera gRPC API with per module clients.
| Client | Example methods |
| - | - |
| `Blockchain.Action` | `GetAction(ctx, actionID)` returns the action record with its state |
| `Blockchain.SuperNode` | SuperNode registration state and listings |
Action states are `PENDING`, `PROCESSING`, `DONE`, `FAILED`, and `EXPIRED`. Approved cross-chain actions can also reach `APPROVED` after `MsgApproveAction`.
## Cascade surface
`client.Cascade` wraps the SuperNode SDK for file operations.
| Method | Signature shape | Purpose |
| - | - | - |
| `Upload` | `Upload(ctx, addr, nil, filePath, opts...)` | Register the action and stream the file to a SuperNode |
| `Download` | `Download(ctx, actionID, destDir, opts...)` | Retrieve a file by action ID into a directory |
| `SubscribeToEvents` | `SubscribeToEvents(ctx, ...)` | Receive task lifecycle events instead of polling |
| `CreateRequestActionMessage` | `CreateRequestActionMessage(ctx, addr, filePath, opts)` | Build a `MsgRequestAction` without broadcasting it |
| `CreateApproveActionMessage` | `CreateApproveActionMessage(ctx, actionID, opts...)` | Build a `MsgApproveAction` for cross-chain flows |
Upload options for interchain flows include `cascade.WithICACreatorAddress`, `cascade.WithAppPubkey`, and `cascade.WithICASendFunc`. Download accepts `cascade.WithDownloadSignerAddress` to sign with a controller chain key.
### Standalone Cascade client
For interchain account flows you can build the Cascade client directly with owner settings.
```go theme={null}
import "github.com/LumeraProtocol/sdk-go/cascade"
cascadeClient, err := cascade.New(ctx, cascade.Config{
ChainID: chainID,
GRPCAddr: grpcAddr,
Address: hostAddr,
KeyName: hostKeyName,
ICAOwnerKeyName: controllerKeyName,
ICAOwnerHRP: "cosmos",
Timeout: 30 * time.Second,
}, kr)
```
## ICA helpers
The `ica` package assembles ICS-27 packets and decodes acknowledgements.
| Function | Purpose |
| - | - |
| `PackRequestForICA` | Pack a `MsgRequestAction` into protobuf `Any` bytes |
| `BuildICAPacketData` | Wrap messages into `InterchainAccountPacketData` for `EXECUTE_TX` |
| `BuildMsgSendTx` | Build the controller side `MsgSendTx` |
| `ExtractRequestActionIDsFromAck` | Pull action IDs out of an IBC acknowledgement |
| `ExtractRequestActionIDsFromTxMsgData` | Same, from decoded `TxMsgData` |
| `ParseTxHashJSON`, `ExtractPacketInfoFromTxJSON`, `DecodePacketAcknowledgementJSON` | CLI friendly helpers for tx hash, packet info, and acks |
The [interchain accounts concept page](/cascade/concepts/interchain-accounts) walks through the full flow these helpers support.
## Crypto helpers
The `pkg/crypto` package covers keyring setup and signing.
| Helper | Purpose |
| - | - |
| `DefaultKeyringParams`, `NewKeyring` | Consistent keyring creation |
| `KeyType` (`KeyTypeCosmos`, `KeyTypeEVM`) | Select `secp256k1` coin type 118 or `eth_secp256k1` coin type 60 |
| `LoadKeyring(keyName, mnemonicFile, keyType)` | Create a test keyring from a mnemonic file |
| `ImportKey(kr, keyName, mnemonicFile, hrp, keyType)` | Import a mnemonic into an existing keyring |
| `AddressFromKey(kr, keyName, hrp)` | Derive an address for any bech32 prefix |
| `NewDefaultTxConfig`, `SignTxWithKeyring` | Sign transactions with Cosmos SDK builders |
One keyring can hold both key types under different names. The ICA controller config accepts a separate `HostKeyName` when the controller and host chains use different key types.
```go theme={null}
kr, _ := sdkcrypto.NewKeyring(sdkcrypto.DefaultKeyringParams())
sdkcrypto.ImportKey(kr, "controller-key", "mnemonic.txt", "lumera", sdkcrypto.KeyTypeCosmos)
sdkcrypto.ImportKey(kr, "host-key", "mnemonic.txt", "inj", sdkcrypto.KeyTypeEVM)
```
## Examples
The [examples](https://pkg.go.dev/github.com/LumeraProtocol/sdk-go/examples) in the module have runnable samples for cascade upload and download, action queries, multi account factories, EVM balance reads, Ethereum format transfers, ERC20 conversion, precompile calls, and ICA request and approve flows.
## Next steps
Install the SDK and run your first upload.
What the EVM examples build on.
# JavaScript SDK
Source: https://docs.lumera.io/sdk/javascript
Install the Lumera JavaScript SDK and store your first file on Cascade.
`@lumera-protocol/sdk-js` is the official JavaScript and TypeScript SDK for Lumera Protocol. It puts chain queries, transaction signing, and Cascade storage behind a single client. Cascade is the Lumera permanent storage network, paid once at upload.
The SDK runs in Node.js 18 or later and in modern browsers with the Keplr wallet. File transfers go through SN-API, a REST gateway in front of SuperNodes. The current release is 0.3.0. This page takes you from install to your first upload and download. Every method is listed in the [JavaScript SDK reference](/sdk/javascript-reference).
## Install the SDK
Install the package together with the CosmJS peer dependencies for signing and chain queries.
```bash npm theme={null}
npm install @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
```
```bash yarn theme={null}
yarn add @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
```
```bash pnpm theme={null}
pnpm install @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate
```
## Create a client
`createLumeraClient` is the entry point for everything. The `testnet` preset fills in the chain ID `lumera-testnet-2` and the public testnet endpoints. Use `mainnet` for `lumera-mainnet-1`. To target your own node instead, pass custom endpoints as shown in the [JavaScript SDK reference](/sdk/javascript-reference).
```ts theme={null}
import { createLumeraClient } from "@lumera-protocol/sdk-js";
const client = await createLumeraClient({
preset: "testnet", // or "mainnet"
signer, // Keplr or a wrapped HD wallet
address: account.address, // sender bech32 address
gasPrice: "0.025ulume",
});
```
The signer must provide three methods. `signDirect` and `signAmino` sign transactions. `signArbitrary` implements ADR-036, the Cosmos standard for arbitrary message signing, which Cascade uses to authenticate with SuperNodes. Keplr covers all three in the browser. In Node.js you build a compatible signer from a mnemonic, as shown below.
## Wallet options
Browser wallets must know the Lumera chain before they can sign for it. Register it once with `experimentalSuggestChain` using this config. The config uses coin type 60 with the Ethereum key features because testnet derives keys the Ethereum way since the `v1.20.0` upgrade.
```ts chain-config.ts theme={null}
export const CHAIN_ID = "lumera-testnet-2";
export const LUMERA_CHAIN_INFO = {
chainId: CHAIN_ID,
chainName: "Lumera Testnet",
rpc: "https://rpc.testnet.lumera.io",
rest: "https://lcd.testnet.lumera.io",
bip44: { coinType: 60 },
bech32Config: {
bech32PrefixAccAddr: "lumera",
bech32PrefixAccPub: "lumerapub",
bech32PrefixValAddr: "lumeravaloper",
bech32PrefixValPub: "lumeravaloperpub",
bech32PrefixConsAddr: "lumeravalcons",
bech32PrefixConsPub: "lumeravalconspub",
},
currencies: [
{ coinDenom: "LUME", coinMinimalDenom: "ulume", coinDecimals: 6 },
],
feeCurrencies: [
{
coinDenom: "LUME",
coinMinimalDenom: "ulume",
coinDecimals: 6,
gasPriceStep: { low: 0.025, average: 0.03, high: 0.04 },
},
],
stakeCurrency: {
coinDenom: "LUME",
coinMinimalDenom: "ulume",
coinDecimals: 6,
},
features: ["stargate", "ibc-transfer", "eth-address-gen", "eth-key-sign"],
};
```
`getKeplrSigner` returns a signer with `signDirect`, `signAmino`, and `signArbitrary` already wired up.
```ts keplr-client.ts theme={null}
import {
createLumeraClient,
getKeplrSigner,
isKeplrAvailable,
} from "@lumera-protocol/sdk-js";
import { CHAIN_ID, LUMERA_CHAIN_INFO } from "./chain-config";
if (!isKeplrAvailable()) {
throw new Error("Please install the Keplr extension");
}
// Register the Lumera chain, then request wallet access
await window.keplr.experimentalSuggestChain(LUMERA_CHAIN_INFO);
const signer = await getKeplrSigner(CHAIN_ID);
const [account] = await signer.getAccounts();
const client = await createLumeraClient({
preset: "testnet",
signer,
address: account.address,
gasPrice: "0.025ulume",
});
```
Also install `@cosmjs/amino`. A plain `DirectSecp256k1HdWallet` signs transactions but not arbitrary data, so wrap it together with an Amino wallet to add ADR-036 support.
```ts node-client.ts theme={null}
import { createLumeraClient } from "@lumera-protocol/sdk-js";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import {
Secp256k1HdWallet,
makeSignDoc as makeAminoSignDoc,
} from "@cosmjs/amino";
const mnemonic = process.env.LUMERA_MNEMONIC!;
const directWallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: "lumera",
});
const aminoWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: "lumera",
});
const [account] = await directWallet.getAccounts();
const signer = {
getAccounts: () => directWallet.getAccounts(),
signDirect: (addr: string, doc: any) => directWallet.signDirect(addr, doc),
signAmino: (addr: string, doc: any) => aminoWallet.signAmino(addr, doc),
async signArbitrary(chainId: string, signerAddress: string, data: string) {
const signDoc = makeAminoSignDoc(
[
{
type: "sign/MsgSignData",
value: {
signer: signerAddress,
data: Buffer.from(data).toString("base64"),
},
},
],
{ gas: "0", amount: [] },
"",
"",
0,
0
);
const { signature } = await aminoWallet.signAmino(signerAddress, signDoc);
return {
signed: data,
signature: signature.signature,
pub_key: signature.pub_key,
};
},
};
const client = await createLumeraClient({
preset: "testnet",
signer: signer as any,
address: account.address,
gasPrice: "0.025ulume",
});
```
Never hardcode a mnemonic. Load it from an environment variable or a secrets manager.
## Upload a file
An upload runs in three steps. `prepareFile` hashes the file. `registerAction` registers the action on chain and escrows the storage fee. `sendFileToSupernodes` sends the file to a SuperNode and polls until processing completes. An action is the on-chain record of a storage request. The fee is paid in LUME, so fund your testnet account from the [faucet](/faucet) first.
```ts upload.ts theme={null}
const data = new TextEncoder().encode("Hello, Lumera!");
// Expiration, 25 hours from now as a Unix timestamp
const expirationTime = String(Math.floor(Date.now() / 1000) + 25 * 60 * 60);
const uploader = client.Cascade.uploader;
const prepared = await uploader.prepareFile(data);
const { actionId, authSignature } = await uploader.registerAction(prepared, {
fileName: "hello.txt",
isPublic: true,
expirationTime,
});
await uploader.sendFileToSupernodes(actionId, authSignature, prepared.fileBytes);
console.log("Stored with action ID:", actionId);
```
`actionId` is the permanent on-chain reference to your file. Save it. You need it for every download. `uploadFile` runs the same three steps in one call, but it resolves to the SN-API task, not the action ID.
## Download a file
`download` signs the action ID with ADR-036 to authenticate, then returns a `ReadableStream` of the file bytes.
```ts download.ts theme={null}
// actionId comes from registerAction in the upload step
const stream = await client.Cascade.downloader.download(actionId);
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) chunks.push(value);
}
const size = chunks.reduce((total, chunk) => total + chunk.length, 0);
const bytes = new Uint8Array(size);
let offset = 0;
for (const chunk of chunks) {
bytes.set(chunk, offset);
offset += chunk.length;
}
console.log(new TextDecoder().decode(bytes));
```
## Check action status
Query the action to follow its progress on chain.
```ts status.ts theme={null}
const action = await client.Blockchain.Action.getAction(actionId);
console.log("State:", action.state);
```
The state starts at `PENDING` after registration. It moves to `PROCESSING` while SuperNodes encode and distribute the file, then ends at `DONE`. An upload still unfinalized at its `expirationTime` ends at `EXPIRED` and the escrowed fee is refunded.
## Next steps
Every method, option, and status value in the SDK.
Wire the SDK into a browser upload flow.
What happens between PENDING and DONE.
# JavaScript SDK reference
Source: https://docs.lumera.io/sdk/javascript-reference
Every method, option, and status value in the Lumera JavaScript SDK.
This page lists the public surface of [`@lumera-protocol/sdk-js`](https://www.npmjs.com/package/@lumera-protocol/sdk-js). For a guided start see the [JavaScript SDK page](/sdk/javascript).
## createLumeraClient
The factory function that builds a configured client.
```ts theme={null}
import { createLumeraClient } from "@lumera-protocol/sdk-js";
const client = await createLumeraClient({
preset: "testnet",
signer: wallet,
address: "lumera1...",
gasPrice: "0.025ulume",
});
```
| Field | Type | Required | Description |
| - | - | - | - |
| `preset` | `"testnet"` or `"mainnet"` | No | Fills in chain ID and endpoints for the chosen network |
| `rpcUrl` | `string` | No | Custom CometBFT RPC endpoint, overrides the preset |
| `lcdUrl` | `string` | No | Custom LCD or REST endpoint, overrides the preset |
| `chainId` | `string` | No | Custom chain ID, overrides the preset |
| `snapiUrl` | `string` | No | Custom SN-API gateway endpoint, overrides the preset |
| `signer` | `OfflineSigner` | Yes | CosmJS offline signer used for transaction signing |
| `address` | `string` | Yes | Bech32 address of the signing account |
| `gasPrice` | `string` | No | Gas price string. Defaults to `"0.025ulume"` |
| `http.timeout` | `number` | No | Request timeout in milliseconds. Defaults to `30000` |
| `http.maxRetries` | `number` | No | Maximum retry attempts. Defaults to `3` |
All four endpoint fields sit at the top level of the config. Use a preset for the standard networks, or set them individually to point at your own infrastructure.
```ts theme={null}
const client = await createLumeraClient({
chainId: "lumera-testnet-2",
rpcUrl: "https://rpc.testnet.lumera.io",
lcdUrl: "https://lcd.testnet.lumera.io",
snapiUrl: "https://snapi.testnet.lumera.io",
signer: wallet,
address: "lumera1...",
});
```
Returns `Promise`. The signer needs `signDirect`, `signAmino`, and `signArbitrary` for the full feature set. See [wallet options](/sdk/javascript#wallet-options).
## Cascade uploader
### uploadFile
Runs the whole upload in one call. Prepare, register on chain, and send to SuperNodes.
```ts theme={null}
const result = await client.Cascade.uploader.uploadFile(fileBytes, {
fileName: "document.pdf",
isPublic: true,
expirationTime: "1742553600",
taskOptions: { pollInterval: 2000, timeout: 300000 },
});
```
| Option | Type | Description |
| - | - | - |
| `fileName` | `string` | Filename metadata |
| `isPublic` | `boolean` | Whether the file downloads without the owner signature |
| `expirationTime` | `string` | Unix timestamp. Unfinalized uploads past this time refund the fee |
| `taskOptions.pollInterval` | `number` | Milliseconds between status polls, default 2000 |
| `taskOptions.timeout` | `number` | Milliseconds before timeout, default 300000 |
Resolves to the completed SN-API `Task`, not the action ID. The `Task` type declares only `taskId`, `status`, and `progress`. When you need the action ID, run the lower level steps below. `registerAction` returns it as `actionId`.
### Lower level upload steps
Use these when you need control over individual phases.
| Method | Purpose |
| - | - |
| `prepareFile(file)` | Convert `Uint8Array`, `File`, or `Blob` to bytes and compute the BLAKE3 hash |
| `registerAction({ fileBytes, dataHash }, options)` | Generate the layout, sign it, and broadcast the registration transaction. Returns `{ actionId, authSignature }` |
| `makeAuthSignature(actionId, dataHash)` | Create an auth signature for an existing action |
| `sendFileToSupernodes(actionId, authSignature, fileBytes, options)` | Send the file to the SN-API gateway after registration |
`sendFileToSupernodes` retries up to 5 times with 3 second delays to absorb SN-API indexing lag.
## Cascade downloader
| Method | Purpose |
| - | - |
| `download(actionId, taskOptions?)` | Download by action ID. Returns `Promise>` |
| `downloadFile({ actionId, taskOptions })` | Same download with an explicit options object |
| `downloadPrivate(actionId)` | Download with the owner ADR-036 signature always applied |
Every download authenticates with an ADR-036 signature over the action ID. The [download lifecycle page](/cascade/concepts/download-lifecycle) shows how to consume the stream.
## Blockchain queries
| Method | Returns |
| - | - |
| `Blockchain.getChainId()` | The connected chain ID |
| `Blockchain.Action.getParams()` | Current action parameters such as `max_raptor_q_symbols` |
| `Blockchain.Action.getAction(actionId)` | The action record with `creator`, `actionID`, `actionType`, `metadata`, and `state` |
| `Blockchain.Action.getActionFee(dataSizeKb)` | The storage fee in `ulume` for that size in KB, rounded up from bytes |
| `Blockchain.Supernode.listSupernodes()` | All registered SuperNodes |
| `Blockchain.Tx.simulate(address, msgs)` | Gas estimate for a set of messages |
| `Blockchain.Tx.broadcast(signedTx)` | Broadcast result for a signed transaction |
```ts theme={null}
const dataSizeKb = Math.ceil(fileSizeBytes / 1024);
const { amount } = await client.Blockchain.Action.getActionFee(dataSizeKb);
```
Chain parameter queries are cached for 5 minutes.
## Wallet helpers
| Helper | Purpose |
| - | - |
| `getKeplrSigner(chainId)` | Signer from the Keplr extension with `signDirect`, `signAmino`, and `signArbitrary` |
| `isKeplrAvailable()` | Whether the Keplr extension is present |
Keplr is the only browser wallet with an exported helper.
## LEP-1 helpers
For deterministic layout and ID work that must match on-chain validation.
```ts theme={null}
import {
createSingleBlockLayout,
generateIds,
buildIndexFile,
} from "@lumera-protocol/sdk-js";
const layoutBytes = await createSingleBlockLayout(fileBytes);
const layoutIds = await generateIds(layoutFileB64, layoutSignatureB64, rq_ids_ic, rq_ids_max);
const index = buildIndexFile(layoutIds, layoutSignatureB64);
```
The [erasure coding page](/cascade/concepts/erasure-coding) explains the derivation scheme these helpers implement.
## Task status values
The SDK reports task progress with these statuses.
| Status | Category | Meaning |
| - | - | - |
| `sdk:completed` | Success | Operation completed |
| `sdk:upload_completed` | Success | Upload confirmed |
| `sdk:download_completed` | Success | Download ready |
| `sdk:failed` | Failure | Generic failure |
| `sdk:supernodes_unavailable` | Failure | No SuperNode accepted the task |
| `sdk:registration_failure` | Failure | On-chain registration failed |
| `sdk:upload_failed` | Failure | File transfer failed |
| `sdk:processing_failed` | Failure | SN-API processing failed |
| `sdk:processing_timeout` | Failure | Processing exceeded the timeout |
| `sdk:download_failure` | Failure | Download reconstruction failed |
On-chain action states are separate. They move from `PENDING` to `PROCESSING` to `DONE`, `FAILED`, or `EXPIRED`. See [error handling](/cascade/error-handling) for recovery patterns.
## Next steps
Install, connect a wallet, and run your first upload.
Handle every failure mode with working code.
# lumerad CLI reference
Source: https://docs.lumera.io/sdk/lumerad-cli
Use the lumerad command line tool to query chain state, submit transactions, manage keys, and work with Lumera modules.
`lumerad` is the Lumera blockchain node binary and the primary command-line tool for interacting with the Lumera Protocol. You use it to run a full node, query any on-chain state, submit transactions, manage cryptographic keys, and work with Lumera-specific modules such as actions (Cascade/Sense), claims, and the SuperNode registry. It is built on the Cosmos SDK and follows the same command conventions as other Cosmos chains. Existing Cosmos familiarity transfers directly.
The current release is `v1.20.1-hotfix` (July 14, 2026). Version `v1.20.0` was the Aurora Convergence EVM upgrade.
***
## Installation
### Build from source
```shell theme={null}
git clone https://github.com/LumeraProtocol/lumera
cd lumera
```
```shell theme={null}
make build
```
The compiled binary is placed at `build/lumerad`. You can also run `make build-debug` to include debug symbols.
```shell theme={null}
sudo mv build/lumerad /usr/local/bin/
```
```shell theme={null}
lumerad version
```
**Prerequisites.** You need Go 1.26+ (see `go.mod` for the exact version) and `make`. The `libwasmvm` shared library is built automatically during `make build`. You can also source it from the [CosmWasm releases page](https://github.com/CosmWasm/wasmvm/releases).
***
## Key management
Before you can sign transactions, you need at least one key in your local keyring.
```shell theme={null}
lumerad keys add # Generate a new key and display its mnemonic
lumerad keys list # List all keys in the keyring
lumerad keys show # Show a key's details, address, and public key
lumerad keys import # Import a key from a mnemonic phrase
lumerad keys delete # Permanently delete a key from the keyring
```
When you run `lumerad keys add`, the mnemonic is displayed **once** and never stored. Write it down and keep it in a safe place. It is the only way to recover your key if you lose access to the keyring files.
***
## Query commands
Query commands read on-chain state without submitting any transactions. They are safe to run at any time and do not require a signing key.
```shell theme={null}
# List all active validators
lumerad query staking validators
# Show delegations for a specific address
lumerad query staking delegations
# Show token balances for a specific address
lumerad query bank balances
```
```shell theme={null}
# List all Cascade/Sense actions
lumerad query action list-actions
# Fetch a specific action by ID
lumerad query action action
```
```shell theme={null}
# Show the claim record for a legacy address
lumerad query claim claim-record
# List claimed records for a vested term (0 if not vested)
lumerad query claim list-claimed
```
```shell theme={null}
# Query the connected node's sync status and block height
lumerad status
```
***
## Transaction commands
Transaction commands broadcast signed messages to the chain. Every transaction command requires `--from` (the signing key) and `--chain-id`. Fees are charged in `ulume` (1 LUME = 1,000,000 ulume).
```shell theme={null}
# Delegate tokens to a validator
lumerad tx staking delegate ulume \
--from \
--chain-id lumera-mainnet-1
# Unbond tokens from a validator (subject to unbonding period)
lumerad tx staking unbond ulume \
--from \
--chain-id lumera-mainnet-1
```
```shell theme={null}
# Vote on an active governance proposal
lumerad tx gov vote yes \
--from \
--chain-id lumera-mainnet-1
# Valid vote options: yes | no | abstain | no_with_veto
```
```shell theme={null}
# Claim LUME to a new address, signed with the legacy key
lumerad tx claim claim \
--from \
--chain-id lumera-mainnet-1
# Same claim into a vesting tier
lumerad tx claim delayed-claim \
--from \
--chain-id lumera-mainnet-1
```
***
## Global flags
These flags apply to every `lumerad` command. You can set frequently-used flags as environment variables (e.g., `LUMERA_CHAIN_ID`) or in `~/.lumera/config/client.toml`.
| Flag | Description |
| - | - |
| `--chain-id` | Chain identifier, `lumera-mainnet-1` for mainnet or `lumera-testnet-2` for testnet |
| `--from` | Key name or bech32 address used to sign the transaction |
| `--fees` | Transaction fee in `ulume` (e.g., `5000ulume`) |
| `--gas` | Gas limit as an integer, or `"auto"` to estimate automatically |
| `--gas-adjustment` | Multiplier applied to the estimated gas when using `--gas auto` (default `1.3`) |
| `--node` | Tendermint RPC endpoint (default: `tcp://localhost:26657`) |
| `--output` | Output format: `text` (default) or `json` |
| `--broadcast-mode` | How to wait for the result: `sync`, `async`, or `block` |
| `--keyring-backend` | Keyring backend: `os`, `file`, or `test` |
Add `--output json` to any query command and pipe the result to `jq` for easy scripting.
```shell theme={null}
lumerad query bank balances lumera1abc... --output json | jq '.balances'
```
For transactions, combine `--broadcast-mode block` with `--output json` to get the full transaction result in a single call. The result includes events and the transaction hash.
***
## Common workflows
```shell theme={null}
# 1. Find your address
lumerad keys show mykey --bech acc
# 2. Check your balance
lumerad query bank balances $(lumerad keys show mykey -a)
# 3. Pick a validator
lumerad query staking validators --output json | jq '.validators[] | {moniker: .description.moniker, address: .operator_address}'
# 4. Delegate
lumerad tx staking delegate lumeravaloper1xyz... 1000000ulume \
--from mykey \
--chain-id lumera-mainnet-1 \
--fees 5000ulume \
--gas auto \
--gas-adjustment 1.4
```
```shell theme={null}
lumerad query action action --output json | jq '.action.state'
```
A finalized Cascade action has `state: "ACTION_STATE_DONE"`.
```shell theme={null}
lumerad status --node tcp://rpc.mainnet.lumera.io:26657
lumerad query bank balances lumera1abc... --node tcp://rpc.mainnet.lumera.io:26657
```
Set `--node` once in your client config to avoid repeating it.
```shell theme={null}
lumerad config node tcp://rpc.mainnet.lumera.io:26657
```
***
## Additional build targets
The Makefile exposes several targets useful during development.
```shell theme={null}
make build-debug # Build with debug symbols
make build-proto # Regenerate protobuf files from .proto sources
make lint # Run golangci-lint
make unit-tests # Run unit test suite
make integration-tests # Run integration tests
```
***
## Further reading
The upstream documentation for the SDK that powers `lumerad`.
Consensus engine and RPC reference.
Cross-chain messaging used by Lumera.
Genesis files, seeds, and peer lists for mainnet and testnet.
# rq-go RaptorQ library
Source: https://docs.lumera.io/sdk/raptorq
Use the rq-go library to encode files into fault-tolerant RaptorQ symbols and reconstruct them even with missing data.
`rq-go` is a Go library that wraps the RaptorQ Forward Error Correction (FEC) algorithm. Lumera's Cascade service uses it to split files into redundant, independently-retrievable symbols before distributing them across the SuperNode P2P network. Because RaptorQ is an erasure code, the original file can be reconstructed even if a subset of the symbols is lost or unavailable. This makes Cascade storage resilient to node churn and partial network failures.
Most developers interact with Cascade through the [SuperNode gRPC API](/api/grpc/cascade-service) or the `lumerad` CLI rather than calling `rq-go` directly. This page documents the library for developers who need low-level encoding control or who are building tooling on top of Cascade's storage layer.
***
## Platform support
`rq-go` ships pre-built static libraries for the following targets. No external shared libraries or C toolchains are required at runtime. The final binary is fully self-contained.
| Platform | Architecture |
| - | - |
| Linux | amd64 |
| Linux | arm64 (including Raspberry Pi) |
| macOS | amd64 (Intel) |
| macOS | arm64 (Apple Silicon) |
***
## Installation
Add `rq-go` to your Go module.
```shell theme={null}
go get lumera.build/rq-go@v0.3.0
```
The module path is `lumera.build/rq-go` from v0.3.0 on. Earlier versions were published as `github.com/LumeraProtocol/rq-go`; switch the import path when you upgrade.
Then import it in your source files.
```go theme={null}
import "lumera.build/rq-go"
```
***
## Quick start
The example below shows the complete encode and decode round trip using the library's default configuration.
```go raptorq_example.go theme={null}
package main
import (
"fmt"
"os"
raptorq "lumera.build/rq-go"
)
func main() {
// Create a processor with default settings.
processor, err := raptorq.NewDefaultRaptorQProcessor()
if err != nil {
panic(err)
}
defer processor.Free() // Always free resources when done.
// Choose an optimal block size for the file being encoded.
fileInfo, err := os.Stat("large_file.dat")
if err != nil {
panic(err)
}
blockSize := processor.GetRecommendedBlockSize(uint64(fileInfo.Size()))
// Encode the file into RaptorQ symbols.
result, err := processor.EncodeFile("large_file.dat", "symbols/", blockSize)
if err != nil {
panic(err)
}
fmt.Printf("Encoded into %d symbols\n", result.TotalSymbolsCount)
fmt.Printf("Layout file: %s\n", result.LayoutFilePath)
// Decode symbols back to the original file.
err = processor.DecodeSymbols(
"symbols/",
"recovered.dat",
"symbols/_raptorq_layout.json",
)
if err != nil {
panic(err)
}
fmt.Println("File recovered successfully!")
}
```
Always call `processor.Free()` (or defer it immediately after creation) to release the memory held by the underlying C library. Forgetting to do so will cause a memory leak, especially in long-running services.
***
## Custom configuration
Use `NewRaptorQProcessor` when you need to tune symbol size, redundancy, memory limits, or concurrency.
```go theme={null}
// NewRaptorQProcessor(symbolSize, redundancyFactor, maxMemoryMB, concurrencyLimit)
processor, err := raptorq.NewRaptorQProcessor(65535, 4, 4096, 2)
if err != nil {
panic(err)
}
defer processor.Free()
```
### Configuration parameters
| Parameter | Default | Description |
| - | - | - |
| `symbolSize` | `65535` (64 KB − 1 B) | Size of each encoded symbol in bytes. Smaller symbols increase parallelism but raise overhead. |
| `redundancyFactor` | `4` | Repair symbols generated per source symbol. Higher values improve loss tolerance at the cost of storage. |
| `maxMemoryMB` | `16384` (16 GB) | Hard cap on memory the processor may allocate, in MB. |
| `concurrencyLimit` | `4` | Maximum number of blocks encoded or decoded concurrently. |
***
## API reference
### `NewDefaultRaptorQProcessor() (*RaptorQProcessor, error)`
Creates a processor with default settings (see table above). Use this for most applications.
### `NewRaptorQProcessor(symbolSize, redundancyFactor, maxMemoryMB, concurrencyLimit uint32) (*RaptorQProcessor, error)`
Creates a processor with explicit parameters.
### `processor.GetRecommendedBlockSize(fileSize uint64) uint64`
Returns the recommended block size (in bytes) for the given file, taking into account the processor's memory limit and efficiency heuristics. Using this value avoids excessive memory consumption for large files.
### `processor.EncodeFile(inputPath, outputDir string, blockSize uint64) (*EncodeResult, error)`
Encodes the file at `inputPath` into RaptorQ symbols written to `outputDir`. The layout metadata file (`_raptorq_layout.json`) is also written to `outputDir`. Returns an `EncodeResult` with two fields.
* `TotalSymbolsCount` is the total number of symbols generated across all blocks.
* `LayoutFilePath` is the absolute path to the generated layout file.
### `processor.DecodeSymbols(symbolsDir, outputPath, layoutPath string) error`
Reconstructs the original file from the symbols in `symbolsDir`, guided by the layout file at `layoutPath`, and writes the output to `outputPath`. Reconstruction succeeds even when some symbols are missing, as long as enough symbols survive for each block.
### `processor.CreateMetadata(inputPath, layoutPath string, blockSize uint64) (*EncodeResult, error)`
Generates the layout file without writing any symbol files. Use this to plan storage requirements or generate symbol identifiers ahead of time.
### `processor.Free()`
Releases all memory held by the processor. Must be called when the processor is no longer needed.
***
## Block processing and memory management
`rq-go` processes files in blocks to bound peak memory usage.
The file is divided into blocks of at most `blockSize` bytes.
Each block is encoded separately. Only one block's working memory is live at a time, so peak usage is proportional to `blockSize` rather than to the total file size.
Each block produces a set of source symbols plus repair symbols (determined by `redundancyFactor`). Symbols are written to the output directory as individual files named by their content hash.
A single `_raptorq_layout.json` file records the encoder parameters, block boundaries, symbol identifiers, and block hashes needed for decoding.
Use `GetRecommendedBlockSize` to let the library select a block size that balances memory efficiency against encoding overhead.
***
## Metadata file format
The `_raptorq_layout.json` file produced by `EncodeFile` is required for decoding. Keep it alongside the symbols (or store it separately and pass its path to `DecodeSymbols`).
```json theme={null}
{
"blocks": [
{
"block_id": 0,
"encoder_parameters": [0, 0, 25, 240, 160, 0, 195, 80, 1, 0, 1, 8],
"original_offset": 0,
"size": 1700000,
"symbols": [
"9yCaAXSexMsaWDP6pzK4wZ4w9Hqrr6QPjJZ86wJMGoq9",
"3Q4MtczkeZzWbECcA8eUeMaQ14cGHF4PpgeYo33cMtYD",
"G6okoLMA1wGtVZwieSykR9bvLSw49iwYZux2byJDrbDF",
"AJ9fp8Ydqo1aaVHzjHowajJA4ELwfetpQAMUT47GZays"
],
"hash": "9gD64LFuoQPYJoWBQmnG2TdPErWwni7Bhrpn6ae74rk7"
},
{
"block_id": 1,
"encoder_parameters": [0, 0, 25, 240, 160, 0, 195, 80, 1, 0, 1, 8],
"original_offset": 1700000,
"size": 1700000,
"symbols": [
"CxFNCbQhtWLzXwpGCE8L1m67WEV85zuTpTtYyRm6nDQF",
"8NVZfQzFDsXwQgEbuNxzyo9D18da9qEHfDpou7mCzg72",
"2hsAk5xWZCJ6d3xnnEaPu6TXsVwN6vfVhLDbKo7fsGVd",
"96KaGntmMeqzL9uPxpKce2PMV9BiUXovMiDUBw1t3Mhz",
"3kMQUazSrgfpxxkw3yY9zfh8amHXCeD7ZsmaDCu2szdB"
],
"hash": "9LvSmppDKePbZnY6PLX8hyEY8gcskVJKwzmgn7zyPNyC"
}
]
}
```
| Field | Type | Description |
| - | - | - |
| `block_id` | integer | Zero-based block index |
| `encoder_parameters` | array of integers | 12-byte RaptorQ encoder parameter block needed for decoding |
| `original_offset` | integer | Byte offset of this block within the original file |
| `size` | integer | Size of this block in bytes |
| `symbols` | array of strings | Base58-encoded content hashes that identify each symbol file |
| `hash` | string | Base58-encoded hash of the block data, used for integrity verification |
If the file fits within a single block, the layout contains exactly one entry with `block_id: 0`. The decoder handles single-block and multi-block layouts identically.
***
## Usage in Lumera
`rq-go` is used internally by the Lumera SuperNode's Cascade service. When you call [`CascadeService/Register`](/api/grpc/cascade-service), the SuperNode encodes your file with this library, distributes the resulting symbols across the P2P network, and records the symbol IDs on-chain. You do not need to use `rq-go` directly unless you are building custom tooling or integrating at a lower level than the gRPC API.
For most integration work, prefer the [SuperNode gRPC API](/api/grpc/cascade-service) or the `lumerad` CLI over calling `rq-go` directly. The SuperNode handles encoding parameters, P2P distribution, and on-chain finalization for you.
# Rust SDK
Source: https://docs.lumera.io/sdk/rust
Build server-side Rust applications and CLI tools that store and retrieve files on Cascade.
The Rust SDK is the official SDK for server-side applications, CLI tools, and system-level integrations with Lumera Protocol. It talks to the chain over gRPC and routes file transfers through SN-API, the REST gateway in front of SuperNodes.
Because file transfers travel over HTTPS to SN-API, the SDK works from any machine with ordinary outbound internet access. The [Go SDK](/sdk/go) instead streams files to SuperNodes directly over gRPC on port 4444, which requires direct network reachability to that port.
## Install
```bash theme={null}
# Initialize a project if you don't have one yet
cargo init .
# Add the SDK and async runtime
cargo add lumera-sdk-rs
cargo add tokio --features full
```
The SDK needs stable Rust with edition 2021 or later.
## Architecture
The SDK is organized into three core modules.
| Module | Purpose |
| - | - |
| `chain` | On-chain operations. Action parameters, fee lookups, transaction build, sign, and broadcast, gas simulation. |
| `snapi` | SN-API interaction. Upload, download, status polling, file retrieval. |
| `cascade` | High-level orchestration. Deterministic payload and ID generation, ticket registration, upload and download workflows. |
## Create a client
Configure the SDK with explicit values or with environment variables.
```rust theme={null}
use lumera_sdk_rs::chain::ChainConfig;
use lumera_sdk_rs::{CascadeConfig, CascadeSdk};
let chain_cfg = ChainConfig::new(
"lumera-testnet-2",
"https://grpc.testnet.lumera.io",
"https://rpc.testnet.lumera.io",
"https://lcd.testnet.lumera.io",
"0.025ulume",
);
let cfg = CascadeConfig::new(chain_cfg, "https://snapi.testnet.lumera.io");
let sdk = CascadeSdk::new(cfg);
```
Or load the configuration from the environment.
```rust theme={null}
use lumera_sdk_rs::{CascadeSdk, SdkSettings};
// Load .env file if present
let _ = dotenvy::dotenv();
let settings = SdkSettings::from_env();
let sdk = CascadeSdk::new(settings.to_cascade_config());
```
### Environment variables
The built-in defaults assume a local devnet. For testnet, override them in a `.env` file or your shell.
| Variable | Testnet value | Purpose |
| - | - | - |
| `LUMERA_CHAIN_ID` | `lumera-testnet-2` | Chain identifier |
| `LUMERA_GRPC` | `https://grpc.testnet.lumera.io` | gRPC endpoint |
| `LUMERA_RPC` | `https://rpc.testnet.lumera.io` | CometBFT RPC endpoint |
| `LUMERA_REST` | `https://lcd.testnet.lumera.io` | REST LCD endpoint |
| `SNAPI_BASE` | `https://snapi.testnet.lumera.io` | SN-API base URL |
| `LUMERA_GAS_PRICE` | `0.025ulume` | Gas price |
## Derive signing keys
Upload and download both need a `SigningIdentity` derived from your mnemonic. It carries the chain signing key for transactions and the arbitrary signing key for ADR-036 authentication with SN-API.
```rust theme={null}
use lumera_sdk_rs::SigningIdentity;
let identity = SigningIdentity::from_mnemonic(
&mnemonic,
"lumera",
"m/44'/118'/0'/0/0",
)?;
```
Load the mnemonic from an environment variable or a secrets manager. Never hardcode it or commit it to version control.
## Upload a file
Uploads happen in two steps. Register the action on chain, then stream the file to SN-API.
```rust theme={null}
use lumera_sdk_rs::RegisterTicketRequest;
use std::path::PathBuf;
let file_path = PathBuf::from("./my-document.pdf");
// 1. Register the on-chain action
let registered = sdk.register_ticket(
&identity.chain_signing_key,
&identity.arbitrary_signing_key,
&identity.address,
&file_path,
RegisterTicketRequest {
file_name: "my-document.pdf".to_string(),
is_public: true,
expiration_time: expiration.to_string(), // Unix timestamp
},
).await?;
// 2. Upload the file to SN-API
let task = sdk.upload_via_snapi(
®istered.action_id,
®istered.auth_signature,
&file_path,
).await?;
// 3. Poll the upload status
let status = sdk.snapi.upload_status(&task).await?;
println!("Stored with action ID: {}", registered.action_id);
```
`register_ticket` escrows the storage fee and returns the `action_id`, the permanent on-chain reference to your file. SuperNodes then encode the data and distribute it across the network. [How Cascade works](/cascade/how-cascade-works) explains what happens behind each step.
## Download a file
```rust theme={null}
// 1. Request a download task with your arbitrary signing key
let down_task = sdk.request_download(
®istered.action_id,
&identity.arbitrary_signing_key,
).await?;
// 2. Stream the file bytes from SN-API
let bytes = sdk.snapi.download_file(&down_task).await?;
std::fs::write("./downloaded-file.pdf", &bytes)?;
```
## Key dependencies
| Crate | Role |
| - | - |
| `cosmrs` | Cosmos SDK signing and gRPC |
| `reqwest` | HTTP client with rustls |
| `tokio` | Async runtime |
| `blake3` | File integrity verification |
| `prost` | Protobuf encoding |
## Next steps
The storage flow behind register, upload, and download.
The REST endpoints the SDK calls under the hood.
The gRPC alternative for backends with direct SuperNode access.
# Rust SDK reference
Source: https://docs.lumera.io/sdk/rust-reference
Modules, settings, client surfaces, and helpers in the Lumera Rust SDK.
This page lists the public surface of the Rust SDK, `lumera-sdk-rs`. For a guided start see the [Rust SDK page](/sdk/rust).
The crate is [`lumera-sdk-rs`](https://crates.io/crates/lumera-sdk-rs) on crates.io and the library name is `lumera_sdk_rs`. Add it with `cargo add lumera-sdk-rs`. Release notes and source tarballs are in [Lumera releases](https://github.com/LumeraProtocol/releases/releases?q=sdk-rs).
The crate is pre-1.0. Pin a version (for example `lumera-sdk-rs = "=0.1.0"`) if you need a stable surface.
## Crate layout
The crate exposes seven modules. Three of them carry the client surfaces you will use most.
| Module | Purpose |
| - | - |
| `chain` | Action parameters, fee lookups, transaction build, sign, broadcast, and gas simulation |
| `snapi` | SN-API calls for upload, download, status polling, and file retrieval |
| `cascade` | High-level orchestration over `chain` and `snapi` |
| `config` | Settings loading from environment, `.env`, TOML, and JSON |
| `keys` | Mnemonic derivation and address validation |
| `crypto` | Raw message signing and ADR-036 sign bytes |
| `error` | The single `SdkError` type |
These items are re-exported at the crate root.
```rust theme={null}
pub use cascade::{CascadeConfig, CascadeSdk, RegisterTicketRequest};
pub use config::SdkSettings;
pub use keys::SigningIdentity;
```
## Settings
`SdkSettings` holds the six values every client needs.
| Field | Type | Default |
| - | - | - |
| `chain_id` | `String` | `lumera-devnet` |
| `grpc_endpoint` | `String` | `http://127.0.0.1:9090` |
| `rpc_endpoint` | `String` | `http://127.0.0.1:26657` |
| `rest_endpoint` | `String` | `http://127.0.0.1:1317` |
| `gas_price` | `String` | `0.025ulume` |
| `snapi_base` | `String` | `http://127.0.0.1:8080` |
The defaults point at a local devnet. Override them for testnet or mainnet.
### Loading settings
| Method | Behavior |
| - | - |
| `SdkSettings::default()` | Local devnet values |
| `SdkSettings::from_env()` | Defaults with environment overrides applied |
| `SdkSettings::from_env_file(path)` | Loads a `.env` file, then applies environment overrides |
| `SdkSettings::from_file(path)` | Reads `.toml` or `.json`, then applies environment overrides |
| `settings.to_cascade_config()` | Converts settings into a `CascadeConfig` |
Environment variables win over file values, which makes deployment-time overrides straightforward. Any other file extension returns `SdkError::InvalidInput`.
### Environment variables
| Variable | Field |
| - | - |
| `LUMERA_CHAIN_ID` | `chain_id` |
| `LUMERA_GRPC` | `grpc_endpoint` |
| `LUMERA_RPC` | `rpc_endpoint` |
| `LUMERA_REST` | `rest_endpoint` |
| `LUMERA_GAS_PRICE` | `gas_price` |
| `SNAPI_BASE` | `snapi_base` |
## Cascade surface
`CascadeSdk` owns a `ChainClient` and an `SnApiClient` as public fields, so you can drop to either layer directly.
```rust theme={null}
use lumera_sdk_rs::{CascadeConfig, CascadeSdk};
let sdk = CascadeSdk::new(CascadeConfig::new(chain_cfg, "https://snapi.testnet.lumera.io"));
```
### Configuration
`CascadeConfig` carries a `ChainConfig` plus the SN-API base URL.
| Item | Signature |
| - | - |
| `CascadeConfig::new` | `(chain: ChainConfig, snapi_base: impl Into) -> Self` |
| `CascadeConfig::with_snapi_base` | `(self, snapi_base: impl Into) -> Self` |
### Workflow methods
| Method | Signature |
| - | - |
| `register_ticket` | `(&self, chain_signing_key, arbitrary_signing_key, creator_addr: &str, file_path: &Path, req: RegisterTicketRequest) -> Result` |
| `upload_via_snapi` | `(&self, action_id: &str, signature: &str, file_path: &Path) -> Result` |
| `request_download` | `(&self, action_id: &str, signing_key: &k256::ecdsa::SigningKey) -> Result` |
`register_ticket` does the full registration in one call. It reads the file, hashes it, builds the RaptorQ layout, signs it, computes layout IDs, looks up the fee, and submits the action. Both upload and download return a task ID you poll through `snapi`.
### Deterministic helpers
These are associated functions, so call them on the type rather than an instance.
| Function | Signature |
| - | - |
| `compute_data_hash_b64` | `(file_bytes: &[u8]) -> String` |
| `create_layout_b64` | `(file_path: &Path) -> Result` |
| `generate_ids` | `(base: &str, ic: u32, max: u32) -> Result, SdkError>` |
| `build_index_file` | `(layout_ids: Vec, layout_signature: String) -> IndexFile` |
| `canonical_json_bytes` | `(v: &T) -> Result, SdkError>` |
| `random_ic` | `(max: u32) -> u32` |
`compute_data_hash_b64` is BLAKE3 over the file bytes, base64 encoded. `generate_ids` compresses with zstd level 3, hashes with BLAKE3, and encodes with base58, which is what keeps IDs byte-compatible with the SuperNode implementation.
### Cascade types
| Type | Fields |
| - | - |
| `RegisterTicketRequest` | `file_name: String`, `is_public: bool`, `expiration_time: String` |
| `RegisteredTicket` | `action_id: String`, `auth_signature: String`, `data_hash_b64: String`, `metadata_json: String` |
| `IndexFile` | `layout_ids: Vec`, `layout_signature: String`, `version: i32` |
## Chain surface
`ChainClient::new(cfg: ChainConfig)` builds the client. `ChainConfig` has the same five chain fields as `SdkSettings` minus `snapi_base`, plus a builder for each one (`with_chain_id`, `with_grpc_endpoint`, `with_rpc_endpoint`, `with_rest_endpoint`, `with_gas_price`).
### Queries
| Method | Signature |
| - | - |
| `get_action_params` | `(&self) -> Result` |
| `get_action_fee_amount` | `(&self, file_size_kbs: u64) -> Result` |
| `get_account_info` | `(&self, address: &str) -> Result` |
| `get_tx` | `(&self, tx_hash: &str) -> Result