# Steledger — full documentation corpus # Source: https://api.steledger.com/llms.txt # About Steledger ## What is this? **Steledger gives an AI agent a durable identity and a place to anchor what it knows.** Instead of every AI vendor owning your agent's identity, the agent writes its identity and the hashes of its work to a public chain that no single company owns or can switch off — so the record outlives the vendor, the session and the model. An agent can: 1. **Claim an identity** rooted in a GitHub account — the record `ai:gh:`. 2. **Store memory** — content hashes of research, artifacts, and decisions as `ai:gh::mem:` records (the body lives off-chain; the chain holds the verifiable fingerprint). 3. **Prove who it is** later by signing a challenge with its address key. The gateway is a thin authenticated HTTP API in front of an Emercoin node. It does not expose raw wallet RPC; it enforces GitHub-rooted login, short-lived session JWTs, and per-tier rate limits. ## The substrate: Emercoin Steledger does not run a chain of its own. Records live in the Name-Value Storage (NVS) of **Emercoin**, an open-source public blockchain that has been running since 2013. NVS is a key-value store written directly on-chain: each record has a name, a value, an owner address and an expiry, is created and updated by signed transactions, and has a publicly verifiable history. Naming the chain is the point rather than a footnote: it means **a record here can be checked without trusting this service.** Read it from the API, or look the same transaction up in a public block explorer and compare. The same NVS primitive also backs EmerDNS (decentralized DNS), EmerSSL (certificate-based authentication) and EmerSSH — it is long-standing infrastructure, not something built for this. Agents need no coin (**EMC**) to use it: the gateway pays for every record, and for handing records over. Only an agent that takes its names onto its own address *and then wants to change them* needs its own node and pays that node's fees — see [transfer](https://api.steledger.com/docs/nvs.md#transfer). ## Why a separate site? A different audience, not a duplicate. `emercoin.com` is the chain's own human-facing site; `api.steledger.com` is **agent-first**: human-readable, but primarily designed to be discovered and used by AI agents (Claude, GPT-class models, and others) — via a machine-readable API ([OpenAPI](https://api.steledger.com/openapi.json)), an [MCP server](https://api.steledger.com/docs/mcp.md), and this documentation corpus indexed at [/llms.txt](https://api.steledger.com/llms.txt). ## A note on hostnames The service moved to `api.steledger.com` on 2026-09-22. `ai.emercoin.com` is the previous address; it still answers reads and the REST API, so links published before the move keep working. Signing in, however, happens only on the new host: the OAuth metadata names one issuer, and it is `api.steledger.com`. If an MCP client was configured against the old address, point it at the new one and sign in again. ## The bigger idea AI agents increasingly need a portable, verifiable identity and durable memory that outlive any one platform. A neutral blockchain — credible without a central custodian — is a natural substrate. GitHub is only the first identity root; the namespace is designed to grow (`ai:dns:`, `ai:did::`, …). Next: [Quickstart](https://api.steledger.com/docs/quickstart.md) · [NVS data model](https://api.steledger.com/docs/nvs.md) --- # Quickstart **MCP-capable agent?** Skip everything below — add `https://api.steledger.com/mcp` as a connector and your client signs you in with GitHub automatically (OAuth, no token to copy). See the [MCP guide](https://api.steledger.com/docs/mcp.md). The steps here are the raw HTTP path: use them for scripting, a sandbox, or any client without MCP/OAuth support. Base URL: `https://api.steledger.com`. All chain-writing endpoints require a session JWT in the `Authorization: Bearer ` header. Reads are open. Check the node is healthy and synced: ```bash curl https://api.steledger.com/status # {"version":"v0.8.5emc","blocks":...,"synced":true,...} ``` ## 1. Authenticate ### Option A — browser (humans) Open , click **Continue with GitHub**, and copy the session token shown on the result page. ### Option B — device flow (headless agents) No browser on the agent side. Start the flow, show the user a short code, then poll. ```bash # Start — returns user_code, verification_uri, session_id, interval, expires_in curl -X POST https://api.steledger.com/auth/github/device/start # The user opens verification_uri (https://github.com/login/device) and enters user_code. # Poll until authorized — 202 while pending, 200 + access_token once done curl -X POST https://api.steledger.com/auth/github/device/poll \ -H 'Content-Type: application/json' \ -d '{"session_id":""}' # 200 -> {"access_token":"","github_id":...,"github_login":"...","tariff":"free"} ``` Save the `access_token`; it is your session JWT (short-lived). ## 2. Confirm your identity ```bash TOKEN= curl https://api.steledger.com/me -H "Authorization: Bearer $TOKEN" # {"github_id":...,"github_login":"...","tariff":"free"} ``` ## 3. Write a memory record on-chain Store the hash of an artifact (the body stays off-chain; the chain holds the proof): ```bash curl -X POST https://api.steledger.com/nvs/mem \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"content_hash":"","metadata":{"note":"research result"}}' # {"name":"ai:gh::mem:","result":""} ``` ## 4. Read it back ```bash curl "https://api.steledger.com/nvs/ai:gh::mem:" # {"status":"pending",...} immediately (in the mempool), then # {"status":"confirmed",...} once it lands in a block (~10 min) ``` ## (Optional) Register your identity record Bind an Emercoin address to your GitHub identity so you can later prove control by signature (machine-speed agent login without a GitHub round-trip): ```bash curl -X POST https://api.steledger.com/nvs/identity \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"address":"","metadata":{}}' # {"name":"ai:gh:","result":""} ``` ## (Optional) Take records onto your own address Irreversible — read [Transfer](https://api.steledger.com/docs/nvs.md#transfer) first. The records must be confirmed: ```bash curl -X POST https://api.steledger.com/nvs/transfer \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"to_address":"","irreversible":true,"names":["ai:gh::mem:"]}' # {"txid":"","count":1,"names":[...],"to_address":"...","after":"The transfer is final ...","fee_grant":null,...} ``` Holding the key and no EMC to pay fees with later? Add `"fee_grant":true`: the gateway also sends 0.01 EMC to the same address, once per account — see [Network-fee funds](https://api.steledger.com/docs/nvs.md#network-fee-funds). Full machine-readable contract: [OpenAPI](https://api.steledger.com/openapi.json) · prefer MCP? see the [MCP guide](https://api.steledger.com/docs/mcp.md) · naming and limits: [NVS data model](https://api.steledger.com/docs/nvs.md) --- # NVS data model Everything this gateway writes is an Emercoin **NVS record**: a `name` → `value` pair, owned by an address, with an expiry. Names use a namespace per identity to avoid collisions. ## Names | Name | Purpose | Written by | |------|---------|------------| | `ai:gh:` | **Identity** record — binds a GitHub id to an Emercoin address (+ metadata) | `POST /nvs/identity` | | `ai:gh::mem:` | **Memory** record — a content hash of an artifact (+ metadata) | `POST /nvs/mem` | `` is the numeric GitHub user id carried in your session JWT — you can only write under your own namespace. `` is a content hash you choose (e.g. SHA-256 of the artifact body, which you store off-chain in IPFS or elsewhere). It must look like a digest — 32–128 characters of letters, digits, `_` or `-` (hex of any common algorithm, or an IPFS CID); anything else is refused with `invalid_hash` before any quota is spent. ## Record value The value is a JSON object. For a memory record: ```json { "github_id": 3772563, "content_hash": "", "metadata": { "note": "..." } } ``` For an identity record it carries `github_id`, `github_login`, `address`, `metadata`. ## Ownership The node wallet is shared and internal, so on-chain **all records are owned by the gateway hot-wallet address**. Agent ownership is asserted *inside the value* (`github_id`, and — once you register an identity — your address), anchored to GitHub and recorded on-chain. Control of the bound address can be proven later via the signature login (`POST /auth/challenge` → sign the nonce → `POST /auth/agent-login`). Say it plainly: while the gateway holds the names, **it could technically change a record**. It cannot do so unseen — every version of every record stays in the chain's public history (`GET /history/`, or any block explorer), and the gateway's code is open — so tampering would be detectable, not impossible. An agent that needs a record nobody else can touch can take the name onto an address of its own: see Transfer below. ## Transfer `transfer_records` over MCP, `POST /nvs/transfer` over HTTP, moves your records from the gateway's wallet to an address you choose, in one transaction. **It is irreversible**: once the block confirms, the gateway can no longer change, renew or return those names. The call therefore requires `irreversible: true`. ```json {"to_address": "E…", "irreversible": true, "names": ["ai:gh::mem:"]} ``` or `"everything": true` instead of `names` for your identity and every live memory (at most 100 per call). Only names under your own `ai:gh:`, and only once confirmed; each counts as one write against the limits. - Values move **byte for byte**, unchanged. - Each name gets **36 500 days** (about a century) added to its term at the moment of transfer, since the gateway will not be able to renew it afterwards. - **Any valid address is accepted.** With an address whose key you hold, the records are yours: you can prove control by signing, and coins sent to the name (wallets can pay a name's holder) reach you instead of the gateway. Changing a record later takes your own Emercoin node and its fees. With an address no one holds a key to, the records are **sealed**: provably unchangeable by anyone until the term ends. - If you only need proof that something existed at a given time, **do not transfer** — a record held by the gateway is already dated. After a transfer, `register_identity` or re-storing a moved hash answers `not_held`; new memories are held by the gateway again and can be transferred later. ### Network-fee funds Changing or renewing a record you hold is a transaction your own node pays for, in EMC: a value update cost 0.0002 EMC when measured (September 2026); extending a term costs more, growing with the square root of the days added. If the destination has no EMC, add `"fee_grant": true` to the transfer. The gateway then sends **0.01 EMC** — about fifty value updates — to the same address, in a separate transaction right after the transfer: - **once per GitHub account**, and only together with a transfer, to the address that receives the names; - within a daily budget across all accounts; - refused **before anything moves** if it is not available — `fee_grant_used` (this account already had it) or `fee_grant_unavailable` (budget spent, or not offered) — so you can decide to transfer without it. The result's `fee_grant` holds the amount and the paying transaction's id. Do not ask for it with an address no one holds a key to: nobody could spend it. ## Expiry Records are written with a default term of **1825 days** (reported as `days_added` on read). The chain buys a term in days and charges a flat 175 blocks each, so that is 319 375 blocks; at the rate the chain has been producing lately — about 171 blocks a day, 8.4 min each, measured over the 103 days to 2026-09-22 — it works out to roughly **five years**. That rate drifts, so `expires_in` in blocks against the current height is the only answer worth trusting; `node_status` reports the height. Re-writing a record adds to the remaining term rather than replacing it, so an agent that keeps working never approaches expiry. The term matters for one that stops: an expired name is released and **can be registered by anyone**, so let a lapse happen and the name is no longer evidence of who you are. ## Reading `GET /nvs/` returns: - `{"status":"pending", ...}` — the write is in the mempool, not yet in a block. - `{"status":"confirmed", ...}` — mined; includes `value`, `address`, `days_added`, and (if a newer update is queued) a `pending_update` flag. To find records without knowing their hashes, `GET /records/` lists everything under that id — the identity record and every memory, newest first, with each memory's hash and metadata (`?limit=`, `?offset=`; confirmed records only, cached for a minute). Over MCP this is `list_records`. Also useful: `GET /history/` (full value history) and `GET /addresses/
/names` (all names an address owns). A read's `address` tells you who holds a name now: the gateway, or the address it was transferred to. ## Errors Every refusal has the same shape — in the response's `detail` over HTTP, and as the JSON text of the tool error over MCP: ```json {"error": "daily_limit", "message": "...", "how_to_fix": "...", "retry_after": 3600} ``` `error` is a stable code: `authentication_required`, `account_too_new`, `rate_limited`, `daily_limit`, `service_capacity`, `invalid_hash`, `record_pending`, `value_too_large`, `not_found`, `not_held`, `service_funds`, `busy`, `node_unavailable` (the node is restarting — retry), `method_not_allowed` (a GET on a route that takes POST), `internal_error` (a bug on our side, logged), or `node_error` (the node's own message, passed through). Transfer adds `confirmation_required`, `invalid_selection`, `not_your_record`, `too_many_names`, `invalid_address`, `not_active`, `nothing_to_transfer`, `fee_grant_used` and `fee_grant_unavailable`. `retry_after` is in seconds and appears only when waiting helps. Every refusal also carries `feedback`: if it looks like our fault, or you are stuck, say so with `send_feedback` over MCP or `POST /feedback` (`{"message": "...", "error_code": "...", "tool": "..."}`, up to 1000 characters, no sign-in, a few per day). A person reads these; there is no automatic reply. Refusals are counted by code — never by caller — at https://api.steledger.com/stats. ## Rate limits / tiers Every write is paid for by the gateway, so the **free tier** limits them per `github_id`, in sliding windows (a batch of N records counts as N): - **10 writes per minute**, and - **100 writes per trailing 24 hours**. Writing also needs a **GitHub account at least 30 days old**; a younger one can sign in and read, and the refusal says the date writing opens. The service as a whole has a daily write ceiling too: if it is ever reached, writes answer `503` until the oldest of the last 24 hours' writes age out — reads are unaffected. Every write returns `quota` — `writes_left_this_minute` and `writes_left_today` — and `whoami` (or `GET /me` over HTTP) shows the same without writing, so an agent can plan its batches. Batch many memory records atomically in one transaction with `POST /nvs/mem/batch`. See the [Quickstart](https://api.steledger.com/docs/quickstart.md) for end-to-end examples and the [OpenAPI spec](https://api.steledger.com/openapi.json) for exact request/response schemas. --- # Using the gateway over MCP For agents that speak the **Model Context Protocol** (e.g. Claude Desktop / Claude Code), the `steledger` MCP server wraps this gateway's HTTP API as tools, so the agent never has to craft raw HTTP requests. ## Remote endpoint (hosted — no install) Connect directly to the **hosted** server over Streamable HTTP — nothing to install. - **URL:** `https://api.steledger.com/mcp` ### Getting started 1. **Add the server as a connector** — point your MCP client at the URL above and nothing else. Sign-in happens automatically: on first use of a write tool your client runs the OAuth flow (dynamic client registration + authorization code + PKCE) and redirects you to GitHub to authorize; the edge issues an access token good for the session plus a refresh token good for 30 days, so you stay signed in across reconnects. The **read tools** (`node_status`, `read_record`, `whoami`) work **immediately, with no sign-in at all**. ```jsonc // Claude Code / Desktop MCP config (HTTP transport) — no token needed, OAuth handles it { "mcpServers": { "steledger": { "url": "https://api.steledger.com/mcp" } } } ``` 2. **No OAuth in your client?** Fall back to a manual token: open , sign in with GitHub, copy the token shown, and put it in the `Authorization: Bearer ` header. It's the same session JWT the OAuth flow issues, so both paths are fully interchangeable — but a manual token is short-lived and isn't refreshed for you, so OAuth is the path to prefer whenever your client supports it. ```jsonc // Manual-token fallback { "mcpServers": { "steledger": { "url": "https://api.steledger.com/mcp", "headers": { "Authorization": "Bearer " } } } } ``` Prefer to run it yourself? Use the local stdio server below. ## Tools — remote (hosted, OAuth) | Tool | Auth | What it does | |------|------|--------------| | `node_status` | open | node sync/height (`GET /status`) | | `read_record` | open | read any NVS record | | `list_records` | open | list every record under a GitHub id, newest first — how a new session finds what it anchored before | | `send_feedback` | open | tell the operator what went wrong or is unclear — up to 1000 characters; a person reads it, no automatic reply | | `whoami` | open | current session identity (`{authenticated: false}` with a sign-in hint until you're signed in) | | `register_identity` | sign-in required | register the `ai:gh:` identity record | | `store_memory` | sign-in required | write one memory record (`ai:gh::mem:`); writing the same hash again renews it | | `store_memory_batch` | sign-in required | write up to 100 memory records in one transaction (each counts against the limits) | | `transfer_records` | sign-in required | **irreversible**: move your records to an address you choose, in one transaction — see [Transfer](https://api.steledger.com/docs/nvs.md#transfer) | ## Tools — local (stdio) The local server swaps OAuth (no browser redirect to catch outside a browser) for device-flow / manual-token login, and adds an atomic batch-write tool: | Tool | What it does | |------|--------------| | `node_status` | node sync/height (`GET /status`) | | `login` | start GitHub device-flow login → returns a user code + URL | | `login_poll` | poll the device-flow until authorized → returns the session JWT | | `login_with_token` | dev fallback: exchange a raw GitHub token for a JWT | | `register_identity` | register the `ai:gh:` identity record | | `store_memory` | write one memory record (`ai:gh::mem:`) | | `store_memory_batch` | write many memory records atomically | | `read_record` | read any NVS record | ## Connect The server is a small stdio MCP server (Python) in the [steledger-gateway repo](https://github.com/steledger/steledger-gateway) under `mcp_server/`. Point it at the public gateway with the `GATEWAY_URL` environment variable: ```bash # from a checkout of the repo GATEWAY_URL=https://api.steledger.com # register with Claude Code (stdio, local scope) claude mcp add steledger -- \ uv run --directory /path/to/steledger-gateway/mcp_server python server.py ``` (Set `GATEWAY_URL=https://api.steledger.com` in the server's environment; it defaults to `http://localhost:8000` for local development.) ## Typical flow **Remote (hosted, OAuth):** 1. `node_status` — confirm the chain is synced. 2. `whoami` — check whether you're already signed in; if not, your client's OAuth flow runs on the first write call. 3. `register_identity` (once) and `store_memory` (ongoing). 4. `read_record` — verify what's on-chain. 5. Only if you want the records on an address of your own, or sealed: `transfer_records` (irreversible; hosted server only). **Local (stdio):** 1. `node_status` — confirm the chain is synced. 2. `login` — get a device code; the human authorizes it once at github.com/login/device. 3. `login_poll` — receive the session JWT (held by the server for subsequent calls). 4. `register_identity` (once) and `store_memory` / `store_memory_batch` (ongoing). 5. `read_record` — verify what's on-chain. Prefer raw HTTP? Everything above is also available directly — see the [Quickstart](https://api.steledger.com/docs/quickstart.md) and the [OpenAPI spec](https://api.steledger.com/openapi.json). ---