Skip to content
kascov
explore tokens playground API guide

the covenant explorer for Kaspa L1

Kaspa coins just learned to carry rules.

like a token explorer, but for coins that carry their own rules: kascov watches every smart coin on the Kaspa testnet — born, moving, retiring — and saves the history ordinary nodes prune after roughly 30 hours.

explore live open the galaxy

the last 24 hours

how to read this

  1. Borna coin gets an identity and rules
  2. Movesit changes hands or state — identity travels with it
  3. Retiredits story ends, recorded forever here

new here? take the 30-second tour →

wait — what is a smart coin, really?

Normal KAS is like cash: interchangeable bills with no memory. Spend some, and brand-new bills come out the other side — nothing connects them to the old ones.

Since the Toccata upgrade, a transaction can take ordinary KAS from any wallet and wrap it into something new: an output stamped with a permanent identity (a covenant id) and, usually, a program. That moment is a birth — ordinary money becoming a named coin that carries rules. The id is a fingerprint of that exact moment, so it can't be faked.

When its owner spends it and the transaction stamps the same identity onto new outputs, that's a move: same coin, new state — maybe a new owner, maybe split into pieces, maybe merged back. The name travels with it, however many hands it passes through.

And when it's spent without passing the identity on, the KAS inside flows back out as ordinary cash and the identity ends — retired. Ordinary nodes prune the story after roughly 30 hours. kascov doesn't.

Why bother? Because the program travels with the identity: a coin can refuse to move more than 10% a week, demand two signatures, wait for a delivery confirmation, or verify a zero-knowledge proof — the coin itself enforces its rules, no middleman.

the latest stories

    open the explorer →

    the galaxy

    every app at once, live — one zoomable map of the whole network. each dot is a smart coin, each link a shared transaction.

    script decoder

    paste any Kaspa script, read its post-Toccata opcodes — covenant ops highlighted. runs in your browser, nothing leaves the page.

    JSON API

    everything kascov sees, as plain JSON with CORS on. build a bot, a chart, a better explorer.

    make a smart coin

    build a covenant without writing code and deploy it on testnet in one click — it reveals itself here, named forever.

    built with covenants

    what builders are shipping on Kaspa’s smart coins — real projects, their contracts verifiable right here.

    new here? how to read this →

    your coins — the ones you starred

    life on the testnet

    in the mempool

    covenant transactions the node has seen but no block has confirmed yet, each clears the moment it lands on chain.

    record holders

    what’s running here

    apps · coins that move together

    a single transaction moving several smart coins is a multi-contract flow — a covenant app.

    based-app activity · by namespace

    smart coins whose transactions carry an app payload, grouped by its namespace — the first per-namespace view of Kaspa's based apps.

    inscriptions · what’s being written on-chain

    the JSON payloads, decoded straight from the bytes — each covenant’s protocol, operation and token ticker, exactly as written. no registry, no labels we invented.

    the galaxy · the whole network

    every app at once — each dot is a smart coin, each link a shared transaction. scroll to zoom, drag to pan, click a dot to open the coin.pinch to zoom · tap a dot

    ← explorer overview
    Search centers one coin in the map and exposes a precise link here.

    how long do smart coins live?

    the lifespan of every retired coin — from birth to burn.

    analytics · the network at a glance

    charts

    the same feeds behind this page, drawn as charts — births against retirements over the whole history, the population by contract type, and how long coins survive.

    births vs retirements over time

    survival curve

    coins by contract type

    recent reorgs

    latest stories

      all smart coins

      📄 read a script ✎ write a covenant 🛫 check a transaction

      read a script

      paste any Kaspa script as hex and read it as opcodes — including the post-Toccata set: transaction introspection (KIP-17), covenant ops (KIP-20), zk verification (KIP-16). recognizing, disassembling & linting run in your browser; simulate, verify-proof & compile call the kascov node. recognized contracts can be re-made with your own parameters.

      try an example:
      what the colors mean
      • push data pushed onto the stack
      • standard the classic script opcodes (checks, math, stack)
      • introspection KIP-17 — the script reads its own transaction
      • covenant KIP-20 — smart-coin identity & state rules
      • zk KIP-16 — zero-knowledge proof verification
      • unknown not in the post-Toccata opcode table
      📄 read a script ✎ write a covenant 🛫 check a transaction

      the lab

      Make your own smart coin.

      Deploy a real covenant on Kaspa testnet-10 and watch it run itself — no Solidity, no VM bytecode, just Kaspa script. One command births a contract and spends it under its own rules, and it reveals itself right here on kascov.

      cargo run -p kascov-lab -- escrow-demo click to copy

      build one without writing code

      Pick a covenant, fill in the parties, amounts and timelocks in plain terms, and kascov assembles byte-perfect Kaspa script in your browser — the exact bytes the silverc compiler emits, live as you type. Then deploy it in one click on testnet-10 — no local toolchain — or download a ready-to-run deploy.sh.

      write & compile SilverScript

      Write a covenant in SilverScript and compile it to real Kaspa script right here — powered by the actual silverc compiler. Edit the source, set the constructor args, and get deployable hex.

      start from a template:

      the whole loop

      1. 1
        Decoderead any covenant on-chain as SilverScript — its rules, its parameters, labeled.
      2. 2
        Editchange the constructor arguments in your browser; verified byte-for-byte on every keystroke.
      3. 3
        Deployone command births it on testnet — a real coin, its rules committed as a P2SH state.
      4. 4
        Spendsatisfy the contract's own rules; the transaction reveals the program on-chain.
      5. 5
        Revealedkascov names the coin its contract, forever — for everyone, hash-verified.
      ⌘ prefer the command line? run the whole loop from the CLI

      start here — one command

      You need the repo and a faucet-funded key. Then a single command runs the entire loop and prints a kascov link so you can watch it happen.

      escrow-demo

      An escrow is deployed with you as the arbiter, then you settle it — the contract itself forces the payout to the buyer. A real dispute resolution, on-chain.

      cargo run -p kascov-lab -- escrow-demo

      contract-demo

      A Mecenas is deployed reclaimable by your key, then reclaimed — it reveals itself as SilverScript · Mecenas on kascov, with every argument labeled.

      cargo run -p kascov-lab -- contract-demo

      examples

      Prints every copy-paste recipe — setup, the demos, and the manual decode → deploy → spend flow with your own parameters. Needs no key or network.

      cargo run -p kascov-lab -- examples

      the contracts you can make

      Three canonical SilverScript covenants ship with kascov. Open one in the builder above, put your own keys and amounts in, and it compiles to deployable hex in your browser.

      do it yourself, step by step

      01

      Get a key, fund it

      Make a throwaway testnet key and fund the address it prints at the faucet.

      cargo run -p kascov-lab -- keygen
      02

      Generate your contract

      In the builder above, pick Mecenas / Escrow / LastWill, edit the fields (use the pubkey and blake2b that keygen printed), and copy the compiled hex — or download the ready-to-run deploy.sh.

      03

      Deploy it

      Births the coin as a hidden P2SH commitment. It appears on kascov within a minute.

      cargo run -p kascov-lab -- deploy --program-hex <hex> --value 1000000000
      04

      Reveal it — spend it

      Satisfy the contract to reveal its program on-chain, so kascov names it your contract forever.

      cargo run -p kascov-lab -- spend --program-hex <hex> --entrypoint reclaim

      Escrow instead? settle-escrow --program-hex <hex> --release-to buyer

      Full walkthrough in docs/Covenant Lab.md. Every command has --help.

      📄 read a script ✎ write a covenant 🛫 check a transaction

      will this transaction pass?

      paste a transaction as JSON (the SDK / RPC shape) and find out before you broadcast: every input’s compute budget checked against the classic “script units exceeded” trap, masses measured against the block limit, an honest fee estimate — and for inputs that carry both their witness and their UTXO’s script, a real run through Kaspa’s script engine.

      try an example:

      your transaction goes into the preflight call and nowhere else — the worker computes the verdict and forgets it (no-store). nothing is broadcast, nothing is kept.

      reference

      The kascov API.

      Every covenant on Kaspa, indexed live and kept long after nodes prune it — as plain JSON. No keys, no rate cards, CORS open to everyone. Build a bot, a chart, a better explorer.

      Start
      Overview Networks Client libraries
      Feeds
      Live feed Pending mempool Full snapshot Event feed Event stream Price Atom feed
      Coins
      One coin Coins by id Transaction Address
      Analytics
      24h digest Templates Activity Apps Galaxy Based-app lanes One lane Inscriptions Tokens One token Lifespans Reorgs Consistency
      Find
      Search Spend debugger
      Share
      Share cards
      Write
      Simulate a spend Preflight a tx Compile Publish source Verified source Webhooks One-click deploy

      Overview

      The indexer follows Kaspa's chain tip live, classifies every covenant event (born / moved / retired), decodes spend-time script reveals, and serves same-origin JSON through the production Caddy proxy. Reads are cheap and cacheable — poll as often as you like.

      Base URLhttps://kascov.io
      FormatJSON · application/json
      AuthNone — public
      CORSCORS enabled incl. preflight — call any route, GET or POST, straight from a browser page
      Cachingresponses carry explicit Cache-Control, tip_daa + tip_at_ms; the live stream is never buffered or cached

      Networks

      Every path takes a network segment. Swap testnet-10 for mainnet anywhere. Mainnet's first smart coins arrived July 2, 2026; their full history starts there.

      Client libraries

      Two tiny zero-dependency clients ship in the repo — clients/js/kascov.mjs (Node 18+ and browsers) and clients/py/kascov.py. Each is a single file wrapping the endpoints below. The event stream works through the normal same-origin base — new Kascov('testnet-10', 'https://kascov.io') — because the production Caddy proxy streams SSE without buffering.

      GET/data/{network}-live.json

      The small, fast feed — network stats, the chain tip, and the ~150 newest life events. This is the one to poll (a few KB, refreshes every few seconds).

      Request
      curl -s https://kascov.io/data/testnet-10-live.json
      Response · 200
      {
        "network": "testnet-10",
        "stats": { "covenants": 192852, "active": 14190, "burned": 178662,
                   "events": 876383, "live_value": 307204412838957,
                   "last_activity_daa": 513875477 },
        "tip_daa": 513875491,
        "tip_at_ms": 1783746272524,
        "processed_daa": 513875490,
        "generated_at_ms": 1783746272524,
        "recent_events": [
          { "covenant_id": "2ab7dcd2…35115",
            "kind": "transition", "txid": "f9bbca…f2ba",
            "accepting_daa": 513875477, "seq": 5, "tx_index": 50 }
        ]
      }
      FieldTypeNotes
      stats.covenantsintdistinct smart coins ever seen
      stats.activeintcoins with a live UTXO right now
      tip_daa · processed_daaintchain tip vs. last block applied — the gap is the honest sync lag
      recent_events[].kindstringgenesis · transition · burn
      recent_events[].seqintthe event's 0-based position in its own coin's timeline
      recent_events[].tx_indexint?0-based index in the accepting block's accepted-tx list (consensus acceptance order); omitted on rows indexed before capture
      GET/data/{network}/pending

      The node’s current unconfirmed covenant transactions. Reconcile this snapshot after opening or reconnecting the event stream: stream messages are fast hints; this response is the authoritative current set.

      Request
      curl -s https://kascov.io/data/testnet-10/pending
      Response · 200
      {
        "status": "live",
        "last_poll_ms": 1784875200123,
        "generated_at_ms": 1784875200200,
        "revision": 421,
        "pending": [{
          "txid": "f9bbca…f2ba",
          "covenant_id": "2ab7dcd2…35115",
          "tx_kind": "transition",
          "age_ms": 77,
          "events": [
            { "covenant_id": "2ab7dcd2…35115",
              "tx_kind": "transition" }
          ]
        }]
      }
      FieldTypeNotes
      statusstringstarting · live · stale · reconnecting · disabled
      last_poll_msint?unix ms of the latest successful node mempool poll; remains visible while reconnecting
      revisionintchanges whenever feed state or tracked rows change; useful for observability, not as a durable cursor
      pending[].eventsarrayevery covenant touched by the transaction; the scalar covenant fields remain as a backward-compatible preview
      GET/data/{network}.json?after_daa=&after_id=&limit=

      The grid — stats plus one summary row per covenant, ordered by last activity, newest first. A bare request is a first page capped at 20,000 rows; when more remain the response carries next_after_daa + next_after_id — pass them back to keep walking.

      QueryTypeNotes
      after_daaintexclusive cursor — resume below this last_activity_daa
      after_idhextie-breaker within the boundary DAA (the last row's covenant_id)
      limitintpage size, default 5000, capped at 20000; invalid values degrade to the bare first page
      Response · 200
      {
        "stats": { … },
        "covenants": [
          { "covenant_id": "e55322e7…60697", "name": "sleepy-olive-crane",
            "status": "active", "born_value": 460831908462, "live_value": 230415954231,
            "event_count": 2, "live_utxos": 1, "lineage_complete": true,
            "genesis_daa": 513863444, "last_activity_daa": 513870198,
            "template": "p2pk state" }
        ],
        "next_after_daa": 513870178,
        "next_after_id": "a348a978…f3a29"
      }
      FieldTypeNotes
      namestringthe coin's friendly name — derived deterministically from the id, the same name everywhere kascov shows it
      statusstringactive or burned
      born_value · live_valueintsompi held at birth / right now
      last_activity_daaintthe DAA of the coin's newest life event — the sort key of this feed
      templatestring?recognized contract, or a state shape like p2sh commitment
      next_after_daa · next_after_idint? · hex?present only when another page exists — feed them to ?after_daa=&after_id=
      GET/data/{network}/events?after_daa=&after_seq=&limit=

      The chain-wide event feed — every life event on the network, oldest first, in the canonical deterministic order: accepting_daa, then tx_index, then txid. Every event-shaped surface (this feed, the live feed, the stream, webhooks) serves the same object, so you write one parser. Walk history from any point by passing the response's cursor pair back.

      QueryTypeNotes
      after_daaintresume at this DAA — 0 starts from the beginning of history
      after_seqinthow many events of that boundary DAA were already taken — pass the returned pair straight back; a stable cursor because order inside a DAA is total and history only appends at higher DAAs
      limitintpage size, clamped server-side
      Response · 200
      {
        "events": [
          { "covenant_id": "2ab7dcd2…35115", "seq": 5, "kind": "transition",
            "txid": "f9bbca…f2ba", "accepting_daa": 513875477,
            "accepting_block": "58ef8d…c44f0", "tx_index": 50,
            "payload_len": 137 }
        ],
        "next_after_daa": 513875477,
        "next_after_seq": 12
      }
      FieldTypeNotes
      events[].seqint0-based position in the event's own coin's timeline
      events[].tx_indexint?consensus acceptance order within the accepting block; omitted (not null) on rows indexed before capture
      events[].payload_lenint?byte length of the transaction's v1 payload; omitted when it carried none
      next_after_daa · next_after_seqintthe resume cursor — feed to ?after_daa=&after_seq= to keep walking
      GET/data/{network}/stream?covenant=

      Server-sent events — one JSON message the moment the indexer sees each confirmed life event or mempool change. The stream opens with a : connected hello and sends a : ka keep-alive comment every 25s. Treat messages as hints and reconcile the matching snapshot after every connection.

      QueryTypeNotes
      covenanthexoptional — exactly 64 hex characters narrows the stream to one coin's events; anything else is a 400, never a silent wildcard
      Request
      curl -N 'https://kascov.io/data/testnet-10/stream'
      The stream
      : connected
      
      data: {"covenant_id":"2ab7dcd2…","kind":"transition",
             "txid":"f9bbca…","accepting_daa":513875477,
             "tx_index":50}
      
      data: {"kind":"pending","tx_kind":"transition",
             "txid":"a15d4e…","covenant_id":"8c91d2…",
             "events":[…],"revision":421}
      FieldTypeNotes
      kindstringgenesis · transition · burn · pending · pending_resolved — plus reorg notices {"kind":"reorg","rolled_back":N} (filtered streams never see them)
      tx_kindstring?the predicted covenant event kind on pending messages
      resolutionstring?confirmed or dropped on pending_resolved
      GET/data/price.json

      The KAS→USD rate behind the site's ≈ $ hints — one tiny object, no network segment. Answers 503 while no fresh rate is available; treat any non-200 as "no price right now".

      Request
      curl -s https://kascov.io/data/price.json
      Response · 200
      {
        "kas_usd": 0.1132,
        "updated_at_ms": 1751799000000,
        "source": "median of spot venues"
      }
      FieldTypeNotes
      kas_usdnumberUS dollars per 1 KAS
      updated_at_msintwhen the rate was last refreshed (unix ms)
      sourcestringwhere the rate came from
      GET/feed.xml

      What’s new, as an Atom feed — the same milestones as the changelog, one entry each with a stable id, newest first. Lives on the site root next to /sitemap.xml (no network segment), cached for an hour. Point any feed reader at it. Served by newer workers; older deploys answer 404.

      Request
      curl -s https://kascov.io/feed.xml
      GET/data/{network}/c/{covenant_id}.json

      One coin's whole life — the complete event timeline and every UTXO with decoded scripts, recognized templates, and spend-time reveals. 404 if the id is unknown.

      Response · 200
      {
        "covenant_id": "a348a978…f3a29", "name": "brave-jade-osprey",
        "status": "burned", "event_count": 3, "lineage_complete": true,
        "genesis_txid": "82aabd…25dcb", "genesis_daa": 513863030,
        "last_activity_daa": 513870178,
        "events": [
          { "seq": 0, "kind": "genesis", "txid": "82aabd…25dcb",
            "accepting_daa": 513863030, "accepting_block": "bc5aaf…8daac",
            "tx_index": 56 },
          { "seq": 2, "kind": "burn", "txid": "8e7040…3e2bb",
            "accepting_daa": 513870178, "accepting_block": "3d1c00…45e7e2",
            "tx_index": 18 }
        ],
        "events_truncated": false,
        "utxos": [
          { "outpoint": "82aabd…25dcb:0", "value": 230415954231, "live": false,
            "template": "p2sh commitment",
            "state_fields": [ { "name": "program_hash", "value": "d9a9e982…f475d17" } ],
            "revealed_template": "SilverScript · Escrow",
            "revealed_fields": [ { "name": "arbiter_hash", "value": "f4445661…" } ] }
        ]
      }
      FieldTypeNotes
      namestringthe coin's friendly name — deterministic from the id
      lineage_completeboolfalse means kascov started watching mid-life; earlier history is honestly missing
      events_truncatedbooltrue when the timeline was capped — event_count still counts everything
      events[].payload · payload_lenhex? · int?the tx's v1 payload — inlined as hex when ≤ 512 bytes, collapsed to its byte length above that
      events[].with_covenantsarray?up to 4 other coins the same transaction moved (multi-contract flows)
      utxos[].revealed_templatestring?the contract a P2SH state actually ran, decoded and hash-verified when it was spent
      …_fields[]array{ name, value } — labeled constructor arguments
      holders[]arrayup to 100 p2pk-state owners: { pubkey, controls_now, states_seen, first_seen_daa, last_seen_daa }
      utxos[].zk_systemstring?on zk-using states, a size heuristic over the proof push: risc0 (≥1024 B) or groth16 (128–256 B)
      utxos[].revealed_zk_systemstring?the same heuristic applied to the program revealed at spend
      GET/data/{network}/coins?ids=

      Batch summaries — several coins in one request, each row in the exact shape the snapshot grid serves. ids is comma-separated 64-hex covenant ids; ids the index doesn't know are simply absent from the answer, never an error.

      Request
      curl -s 'https://kascov.io/data/testnet-10/coins?ids=e55322e7…,a348a978…'
      Response · 200
      {
        "coins": [
          { "covenant_id": "e55322e7…60697", "name": "sleepy-olive-crane",
            "status": "active", "last_activity_daa": 513870198, … },
          { "covenant_id": "a348a978…f3a29", "name": "brave-jade-osprey",
            "status": "burned", "last_activity_daa": 513870178, … }
        ]
      }
      GET/data/{network}/tx/{txid}.json

      What did this transaction do, in covenant terms? Newer workers answer the whole story — the per-covenant events, the cells created and spent, and the decoded token actions — behind the site's #/{network}/tx/{txid} pages. Older workers answer only covenant_id / covenant_ids; every added field is additive, so feature-detect and fall back gracefully.

      Response · 200
      {
        "txid": "8e7040…3e2bb",
        "covenant_id": "b11acf6c…459c6",
        "covenant_ids": [ "b11acf6c…459c6" ],
        "accepting_daa": 513870178, "accepting_block": "3d1c00…45e7e2",
        "events": [
          { "covenant_id": "b11acf6c…459c6", "name": "brave-jade-osprey",
            "seq": 2, "kind": "burn", "tx_index": 18 }
        ],
        "cells": {
          "created": [ { "covenant_id": "…", "index": 0,
                         "value": 230415954231, "template": "p2sh commitment" } ],
          "spent": [ { "covenant_id": "…", "txid": "82aabd…25dcb", "index": 0,
                       "value": 230415954231,
                       "revealed_template": "SilverScript · Escrow",
                       "has_witness": true } ]
        },
        "token_actions": [
          { "token_id": "9cc4fb51…68f50", "name": "humble-crimson-magpie",
            "token_kind": "transfer", "amount": 1000000 }
        ]
      }
      Not covenant traffic · 404
      { "error": "not found" }
      FieldTypeNotes
      covenant_id · covenant_idshex · arraythe coin(s) this transaction touched — the only fields every worker serves
      accepting_daa · accepting_blockint? · hex?newer workers — where the chain accepted this transaction
      events[]array?newer workers — one row per covenant life event: covenant_id, name, seq, kind (genesis · transition · burn), tx_index?
      cells.created[]array?newer workers — covenant state this transaction made: covenant_id, output index, value (sompi), template?
      cells.spent[]array?newer workers — covenant state it consumed: the outpoint's txid + index, value, revealed_template?, and has_witness (the captured unlocking script — replayable through the spend debugger)
      token_actions[]array?newer workers — the decoded token view: token_id, name, token_kind (mint · transfer · burn), amount? in raw token units
      GET/data/{network}/addr/{address-or-pubkey}.json

      Which smart coins has this address touched? Accepts kaspa:… / kaspatest:… or raw 32/33-byte pubkey hex. Matches p2pk covenant states.

      Response · 200
      {
        "address": "kaspatest:qp9…hzy0",
        "pubkey": "4b3402be…e9f2",
        "covenants": [
          { "covenant_id": "…", "name": "…", "role": "controls_now" }
        ]
      }
      FieldTypeNotes
      covenants[].rolestringcontrols_now · states_seen
      GET/data/{network}/digest.json

      The last 24 hours in one object — births, moves, burns, value born, the busiest coin, the biggest birth, and how many coins are alive. Refreshes about once a minute.

      Response · 200
      {
        "births": 24762, "moves": 25948, "burns": 55074,
        "value_born": 4831200000000, "alive": 6566,
        "busiest": { "covenant_id": "…", "name": "curious-jade-zebra-6366", "events": 1185 },
        "biggest_birth": { "covenant_id": "…", "name": "rapid-violet-seal-b788", "value": 937465200000000 }
      }
      GET/data/{network}/templates.json

      What runs on this network — recognized script templates aggregated over every state UTXO. Unrecognized scripts are counted, never hidden.

      Response · 200
      {
        "templates": [
          { "name": "p2sh commitment", "live_states": 6297, "live_value": 62970000000,
            "ever_seen": 14459, "covenants": 5990, "revealed_runs": 3 }
        ],
        "unrecognized": { "ever_seen": 12, "live_states": 4 }
      }
      FieldTypeNotes
      revealed_runsintcompiled contracts proven at spend time; a tx sweeping N states counts N
      GET/data/{network}/activity.json?range=24h

      The histogram behind the activity chart — life-event counts per DAA bucket. Empty buckets are omitted; treat them as zeros.

      QueryTypeNotes
      rangeenum1h · 6h · 24h · 48h · all (default 24h)
      Response · 200
      {
        "range": "24h", "bucket_daa": 6000,
        "buckets": [ { "daa": 509540000, "births": 812, "moves": 903, "burns": 1740 } ]
      }
      GET/data/{network}/families.json

      Covenant apps — clusters of coins that moved together in a transaction (multi-contract flows), union-found over shared txids. Each family lists its members with their recognized template.

      Response · 200
      {
        "families": [
          { "size": 2, "members": [
              { "covenant_id": "09ef275e…", "template": "p2sh commitment", "shared_txs": 8 },
              { "covenant_id": "901be291…", "template": "p2sh commitment", "shared_txs": 8 }
          ] }
        ]
      }
      GET/data/{network}/galaxy.json?fmt=2&tier=core

      The whole-network app graph behind the galaxy map — precomputed positions, weighted edges, and status for every smart coin. Built server-side and kept warm, so even a 168k-coin network answers instantly.

      QueryTypeNotes
      fmtenum2 — columnar arrays instead of one object per node (much smaller payload)
      tierenumcore — the big clusters only, for a fast first paint; fetch the full graph after

      Unknown params and unknown values degrade to the legacy full-object shape — old clients keep working unchanged.

      GET/data/{network}/lanes.json

      Based-app traffic by namespace — KIP-21 lanes, 4-byte app tags, and the JSON-inscription bucket, sorted by event count. Printable tags get their ASCII label; the rest show as 0x….

      Response · 200
      {
        "tip_daa": 509571904,
        "lanes": [
          { "label": "kspr", "hex": "6b737072", "ascii": true,
            "kind": "tag", "events": 40312, "covenants": 6120 },
          { "label": "JSON inscriptions", "hex": null, "ascii": false,
            "kind": "inscription", "events": 8123, "covenants": 950 }
        ]
      }
      FieldTypeNotes
      lanes[].kindstringlane (KIP-21 namespace) · tag (4-byte app tag) · inscription (JSON payloads)
      lanes[].hexhex?the raw 4-byte namespace; null for the JSON-inscription bucket
      GET/data/{network}/lane/{ns}

      One namespace's dashboard — headline counts, the 50 newest events, and an hourly activity series over the lane's whole life. {ns} is the 4-byte tag as exactly 8 hex characters — anything else is a 400.

      Request
      curl -s https://kascov.io/data/testnet-10/lane/6b737072
      Response · 200
      {
        "namespace": "6b737072",
        "events": 40312, "covenants": 6120,
        "recent": [ { "covenant_id": "…", "txid": "…",
                      "accepting_daa": 509571333, "kind": "transition" } ],
        "activity": [ { "daa": 509540000, "count": 812 } ],
        "bucket_daa": 36000
      }
      FieldTypeNotes
      bucket_daaintbucket width of the activity series — 36,000 DAA ≈ 1 hour
      GET/data/{network}/inscriptions.json

      The JSON-inscription breakdown — events and distinct coins per decoded label (KRC-20 ops and friends).

      Response · 200
      {
        "inscriptions": [
          { "label": "krc-20 mint", "events": 5123, "covenants": 640 }
        ]
      }
      GET/data/{network}/tokens.json

      The covenant-token directory behind the tokens page — every KCC20-shaped coin the indexer decoded, its ticker and fields read straight from the chain's bytes, plus a conservative validation verdict per token: status is verified only when every event in the token's history matched a known KCC20 rule and supply is conserved; anything unknown or ambiguous stays unvalidated with a reason — never a false "valid". The response explains itself in note — surface it to your users too. Older workers answer the pre-validation shape (status is the coin's active/burned liveness, no supply fields) — treat those tokens as unvalidated.

      Response · 200
      {
        "note": "validated from chain — verified means every event matched …",
        "tip_daa": 513875491, "tip_at_ms": 1783746272524,
        "tokens": [
          { "covenant_id": "9cc4fb51…68f50", "name": "humble-crimson-magpie",
            "template": "KCC20 token",
            "fields": { "tick": "NACHO", "op": "deploy" },
            "live_value": 100000000,
            "status": "verified",
            "supply": 2100000000, "minted": 2100000000, "burned": 0,
            "holders": 12,
            "last_activity_daa": 513875477 }
        ]
      }
      FieldTypeNotes
      tokens[].fieldsobjectthe decoded key/values exactly as the covenant wrote them — no registry, no labels kascov invented
      tokens[].live_valueint?sompi the coin holds right now
      tokens[].statusstringverified · invalid · unvalidated — conservative: verified means every event in the history matched a known rule
      tokens[].invalid_reasonstring?why the verdict isn't verified — present on invalid (and unvalidated) tokens
      tokens[].supplyint?live supply in raw token units — minted − burned
      tokens[].minted / burnedint?lifetime totals in raw token units
      tokens[].holdersint?owners with a non-zero balance right now
      GET/data/{network}/token/{covenant_id}

      One token's whole story behind its token page — the directory row, the top 100 balances, every event classified as mint / transfer / burn with amounts and owners, and the validation verdict. Same conservative contract as the directory: a token is verified only when every checked event matched a known KCC20 rule. 404 when the id isn't a token this worker knows (or the worker predates token pages).

      Response · 200
      {
        "token": { "covenant_id": "9cc4fb51…68f50",
                   "name": "humble-crimson-magpie", "template": "KCC20 token",
                   "fields": { "tick": "NACHO" }, "status": "verified",
                   "supply": 2100000000, "minted": 2100000000, "burned": 0,
                   "holders": 12, "last_activity_daa": 513875477 },
        "balances": [ { "owner": "3f9a…c221", "balance": 1250000000 } ],
        "events": [ { "txid": "…", "accepting_daa": 513875477,
                      "kind": "transition", "token_kind": "mint",
                      "amount": 1000000, "owner_to": "3f9a…c221" } ],
        "validation": { "checked": 266 }
      }
      FieldTypeNotes
      tokenobjectthe same row the directory serves for this token
      balancesarraythe top 100 balances, largest first — owner is the controlling pubkey, balance is raw token units
      events[].token_kindstringmint · transfer · burn — the canonical event object plus the token classification, amount and owner_from/owner_to
      validationobjectthe verdict's working: checked events walked; reason says what stopped a verified verdict when the status isn't verified
      GET/data/{network}/lifespans.json

      How long do smart coins live? A bucketed lifespan histogram over every retired coin, plus the median. Times derive from DAA at 10 per second, so median_ms is an estimate.

      Response · 200
      {
        "buckets": [ { "label": "< 1 min", "count": 41230 } ],
        "median_daa": 1840, "median_ms": 184000, "total": 83181
      }
      GET/data/{network}/reorgs.json

      The indexer's own reorg ledger — the last 500 virtual-chain reorgs it applied, newest first. Every entry is a rewind the index actually performed, not a heuristic.

      Response · 200
      {
        "reorgs": [ { "daa": 509571904, "at_ms": 1751799000000, "rolled_back": 2 } ]
      }
      GET/data/{network}/consistency.json

      An automated cross-check between independent ecosystem indexers — kascov's decoded token set compared against another indexer's, token by token. Differences usually mean one of us has a bug; we fix ours and share theirs kindly. Served by newer workers; older ones 404.

      Response · 200
      {
        "network": "mainnet", "checked_at_ms": 1783948200000,
        "our_tip_daa": 513875477, "other_source": "…",
        "other_blue_score": 513875470,
        "tokens_ours": 270, "tokens_other": 268, "intersection": 265,
        "agree": 261, "differ": 4,
        "only_kascov": 5, "only_other": 3, "not_comparable": 0,
        "details": [ { "token_id": "…", "field": "supply",
                       "ours": 2100000000, "theirs": 2099999000 } ],
        "note": "tips differ — small disagreements near the tip are expected"
      }
      FieldTypeNotes
      network · checked_at_msstring · intwhich network was compared, and when the check ran
      our_tip_daaintkascov's chain tip at check time
      other_source · other_blue_scorestring · intthe other indexer's identity and its chain position — two indexers at different tips can honestly disagree
      tokens_ours · tokens_other · intersectioninthow many tokens each side knows, and how many both do
      agree · differintof the intersection: tokens whose compared fields match, and tokens where they don't
      only_kascov · only_otherinttokens one side has indexed that the other hasn't (yet)
      not_comparableinttokens skipped because the two sides expose incompatible shapes for them
      details[]arraythe actual disagreements, per token — what we say, what they say
      notestringplain-language context for reading the numbers
      GET/data/{network}/search?q=&limit=

      Find a coin — by covenant-id hex prefix (4+ characters), friendly-name prefix, or template substring. Each hit says which way it matched.

      QueryTypeNotes
      qstringrequired, 1–64 characters; 400 otherwise
      limitintmax results, default 10, clamped
      Request
      curl -s 'https://kascov.io/data/testnet-10/search?q=mece'
      Response · 200
      {
        "query": "mece",
        "results": [
          { "id": "b4ade48e…", "name": "brave-amber-lynx-b4ad",
            "template": "SilverScript · Mecenas",
            "status": "active", "matched": "template" }
        ]
      }
      FieldTypeNotes
      results[].matchedstringid · name · template
      GET/data/{network}/debug/{txid}

      Replay a real on-chain covenant spend through the actual script engine, with the witness captured at index time — a concrete per-opcode trace of what really ran. The transaction context is fabricated, so signature/introspection checks may diverge from the original; the response says so in note.

      Response · 200
      {
        "ok": true, "pass": true, "verdict": "script accepted",
        "covenant_id": "…", "outpoint": { "txid": "…", "index": 0 },
        "value": 500000000,
        "trace": [ { "op": "OpBlake2b", "dstack": ["…"], "astack": [] } ],
        "trace_truncated": false,
        "note": "replayed with a fabricated tx context"
      }
      FieldTypeNotes
      okboolfalse when the txid spent no tracked state, or no witness was captured (reason says which)
      trace_truncatedboolthe trace is capped at 2,000 opcodes
      GET/share/{network}/{covenant_id}

      The crawler-visible page for one coin (the SPA is hash-routed, so scrapers never see #/… urls) — OG/Twitter meta plus a redirect into the app for humans. Its picture is a server-rendered PNG card, and /sitemap.xml lists every share page so search engines find every coin. These live on the site root, not under /data.

      The card image
      GET /og/{network}/{covenant_id}.png
      → 1200×630 Open Graph card (name, avatar,
        holdings, story), rendered on demand
      The index
      GET /sitemap.xml
      → every coin's /share/… url
      POST/data/{network}/simulate

      Run a hypothetical spend of a compiled contract through the real script engine, off-chain — the same PASS/FAIL a node would decide, with the violated rule in plain English on failure. Pure computation: the network segment is cosmetic, and responses are no-store.

      Request body
      {
        "program_hex": "20d9a9…87",   // ≤ 20,000 hex chars
        "entrypoint": "spend",        // spend · reclaim · cold · inherit
        "recipient": "buyer",         // buyer · seller · other · self
        "value": 500000000,           // the state coin, sompi
        "amount": 499999000,          // output 0; default value − 1000
        "trace": true                 // capture per-opcode stacks
      }
      Response · 200
      {
        "ok": true, "pass": false,
        "verdict": "…", "rule": "output 0 must pay the buyer exactly value − 1000",
        "template": "SilverScript · Escrow",
        "trace": [ { "op": "OpDup", "dstack": ["…"], "astack": [] } ],
        "note": "simulated with a stand-in signer"
      }
      POST/data/{network}/preflight

      Will this transaction pass? Post a transaction as JSON (the SDK / RPC shape — {version, inputs, outputs, lockTime, …}) and get a verdict before you broadcast: per-input compute-budget checks (the classic “script units exceeded” trap), compute/storage mass against the block limit, an honest fee estimate — and for inputs that carry both their signatureScript and their utxo.scriptPublicKey, a real run through the script engine with the budget the transaction declares. Nothing is broadcast, nothing is stored: responses are no-store and the transaction goes into the call and nowhere else. The network segment picks the mass limits. Served by newer workers; older ones answer 404. Also behind the playground’s check-a-transaction mode.

      Request body — the transaction itself
      {
        "version": 1,
        "inputs": [
          { "previousOutpoint": { "transactionId": "…", "index": 0 },
            "signatureScript": "…", "sequence": 0,
            "sigOpCount": 1, "computeBudget": 20,
            "utxo": { "amount": 1000000000,
                      "scriptPublicKey": { "version": 0, "script": "…" } } }
        ],
        "outputs": [ { "value": 999999000,
                       "scriptPublicKey": { "version": 0, "script": "…" } } ],
        "lockTime": 0
      }
      Response · 200
      {
        "ok": true, "verdict": "will_fail",
        "findings": [
          { "severity": "error", "code": "compute_budget_missing",
            "message": "input 0 is a v1 input with no computeBudget — its
                        effective allowance is 9,999 script units and one
                        CheckSig needs 100,000",
            "input_index": 0, "suggestion": "set computeBudget: 10" }
        ],
        "masses": { "compute": 12345, "storage": 0,
                    "transient": 512, "limit": 500000 },
        "fee": { "estimate_sompi": 202400, "note": "an estimate, not consensus" },
        "executed": [
          { "input_index": 0, "pass": false,
            "script_units_used": 100000, "allowance": 9999 }
        ]
      }
      FieldTypeNotes
      verdictstringready — every check passed · will_fail — a node would reject this as-is · incomplete — not enough of the transaction to decide
      findings[]arrayeach check’s answer: severity (error · warn · info), a stable machine-readable code, a plain-English message, the offending input_index?, and a concrete suggestion? (the fix, e.g. set computeBudget: 10)
      massesobject?compute / storage / transient against the network’s block limit; omitted when budget findings block the mass calculation
      feeobject?estimate_sompi + note — labeled an estimate honestly: no consensus min-fee primitive exists
      executed[]array?per executable input: pass, script_units_used, allowance — the real engine, the transaction’s own declared budget
      POST/data/{network}/compile

      Compile SilverScript source server-side. Body is { source, args } — source up to 40,000 characters, at most 16 constructor args of ≤200 characters each.

      Response · 200
      { "ok": true, "hex": "20d9a9…87" }
      // or
      { "ok": false, "error": "compile error: …" }
      POST/data/{network}/publish

      Verify-and-publish, Etherscan-style: submit { source, args }; the worker compiles it and stores the source keyed by the compiled program's blake2b-256 hash. Any coin whose revealed program hashes the same now shows your source as community-verified.

      Response · 200
      { "ok": true, "hash": "d9a9e982…f475d17",
        "template": "SilverScript · Mecenas" }
      GET/data/{network}/verified/{hash}

      The published source for a program hash — what /publish stored. { "ok": false } when nothing is published for that hash.

      Response · 200
      { "ok": true, "source": "contract Mecenas(…) { … }",
        "args": "…", "template": "SilverScript · Mecenas",
        "verified_at": 1752130000000 }
      POST/data/{network}/subscribe

      Register a webhook: your URL gets one JSON POST per matching covenant event, delivered as it happens. Filter to one coin with covenant_id (must be exactly 64 hex characters — anything else is rejected, never a silent wildcard) and/or one event kind; omit both for everything. The response hands you a generated secret — shown once, never readable back: every delivery is signed with it, and only someone holding it can unsubscribe the hook. Unsubscribe with POST /data/{network}/unsubscribe, the returned id, and that secret.

      Request body
      {
        "url": "https://example.com/hook",
        "covenant_id": "b4ade48e…",  // optional, 64 hex
        "kind": "burn"               // optional: genesis · transition · burn
      }
      // → { "ok": true, "id": 7, "secret": "9f2ab4…" }  // save it — shown once
      // unsubscribe: { "id": 7, "secret": "9f2ab4…" }
      //            → { "ok": true, "deleted": true }
      Each delivery
      POST your-url  ·  application/json
      X-Kascov-Signature: 3f9c41…  (keyed with your subscription's secret)
      {
        "network": "testnet-10",
        "covenant_id": "b4ade48e…",
        "kind": "burn",
        "txid": "1461f8…07ed",
        "accepting_daa": 509571520,
        "tx_index": 18
      }
      SemanticsNotes
      authenticityevery delivery carries X-Kascov-Signature: the hex keyed blake2b-256 of the exact request body, keyed with the secret /subscribe returned — recompute it over the raw bytes you received and compare before trusting the event
      deliveryup to 3 attempts with backoff, 5s timeout, redirects never followed; a subscription that keeps failing is retired automatically
      targetspublic http(s) hosts only — every resolved address is checked, and loopback / private / link-local targets are rejected (SSRF guard)
      guaranteesbest-effort hints, not a durable feed — confirm through the polled endpoints
      POST/data/{network}/deploy

      One-click deploy: the server births your compiled contract as a real covenant on testnet-10 using its own faucet key — no local toolchain. Gated off by default: the route exists only when the server is armed (KASCOV_DEPLOY_KEY) and the network is testnet-10; otherwise it answers 404. Never on mainnet.

      Request body
      {
        "program_hex": "20d9a9…87",  // ≤ 20,000 hex chars
        "value": 1000000000          // 1–10 TKAS, in sompi
      }
      Response · 200
      { "ok": true,
        "covenant_id": "b4ade48e…",
        "network": "testnet-10" }
      LimitNotes
      globaltoken bucket — 5 burst, refilling 1 per 10 minutes (≈144/day); throttled requests get 429
      per IP20 deploys per day (best-effort)
      in flightone deploy at a time — they share the funding wallet

      Served by an always-on indexer that follows the chain live. Want the raw index or to run your own? github.com/Knitser/kascov.

      builder guide · testnet-10 · 15 minutes

      Deploy → spend → replay a covenant.

      The working end-to-end path: compile a SilverScript Mecenas, deploy it as a real covenant (KIP-20) on testnet-10, spend it under its own rules, then replay that spend opcode-by-opcode in the browser. Along the way: the compute-budget trap that has stopped most first covenant deploys, and exactly how to avoid it.

      Everything here is tested. Every command was run against the live chain and the shipped kascov toolchain in July 2026; every transaction id links to a real, replayable on-chain spend. Nothing is hypothetical.

      On this page
      0Prerequisites 1Compile a Mecenas 2Deploy it as a covenant 3The compute-budget trap 4Spend it 5Replay it here ★Name your token on chain 6A real example, live

      0Prerequisites — 5 minutes, once

      You need a Rust toolchain (stable) and the kascov repo. kascov-lab is the covenant lab CLI: it builds, signs and broadcasts real covenant transactions on testnet-10.

      git clone https://github.com/Knitser/kascov.git
      cd kascov
      cargo build -p kascov-lab              # one-time; grabs the pinned rusty-kaspa rev
      
      cargo run -p kascov-lab -- keygen      # makes a throwaway TN10 key, prints your address
      address:         kaspatest:qq…
      pubkey (x-only): 3f9c…
      blake2b(pubkey): e377…                # you'll paste these two into the generator

      Fund the printed address at faucet-testnet.kaspanet.io (it’s Cloudflare-gated — use a browser), then confirm it arrived:

      cargo run -p kascov-lab -- balance
      balance: 100.00000000 TKAS

      In a hurry? One command runs this entire guide’s loop — deploy a Mecenas, then spend it so it names itself on the explorer:

      cargo run -p kascov-lab -- contract-demo  (or escrow-demo for a dispute-resolution flow). kascov-lab examples prints every recipe. The rest of this page does the same thing step by step, so you can see the moving parts.

      1Compile a Mecenas

      A Mecenas is a patronage contract: after every period, anyone may push one pledge to the recipient (receive); the funder can reclaim the rest at any time with a signature (reclaim). Two ways to get the compiled hex:

      Option A — the generator (no toolchain)

      Open the script playground → “make a Mecenas” — fill in recipient (your keygen pubkey), funder (your keygen blake2b(pubkey) — this is what lets you reclaim), a pledge, and a period, and copy the compiled hex. Pick a short period like 600 DAA (≈ 1 minute on testnet-10, which runs ~10 DAA per second) so you can trigger receive within this guide. The guided builder does the same thing as a form, and can deploy it for you.

      Option B — the compile API

      The same compiler (silverc) runs behind POST /data/testnet-10/compile. Constructor args are bare positional strings: 0x… hex for pubkey/byte[32], plain decimals for int:

      curl -s -X POST -H 'content-type: application/json' \
        https://kascov.io/data/testnet-10/compile \
        -d '{"source": "<the Mecenas source from the playground>",
             "args": ["0x<recipient pubkey>", "0x<funder blake2b>", "100000000", "600"]}'
      {"ok":true,"hex":"6b6c76009c6375…"}

      Heads-up on arg encodings. The upstream silverc --constructor-args CLI wants a different shape — tagged Expr JSON like {"kind":"int","data":600}, where a byte[32] is an array of 32 byte objects — while the debugger and the endpoints above take the bare forms. The zero-dep converter clients/js/kascov-encode.mjs translates both directions (node kascov-encode.mjs 0x44…44 600), so you never hand-write 32 nested byte objects.

      2Deploy it as a covenant

      cargo run -p kascov-lab -- deploy --program-hex <the compiled hex> --value 1000000000
      program:   SilverScript · Mecenas (181 bytes)
      BIRTH      covenant b4ade48e3ad1eda5…
                 tx a9a7df1a71307c05…
                 program blake2b 3483c24b…

      What just happened, precisely:

      • The coin is born holding 10 TKAS with a P2SH commitment state: OpBlake2b <blake2b-256(program)> OpEqual. The program itself stays hidden until you spend.
      • Output 0 carries a CovenantBinding with a fresh covenant id — blake2b(outpoint ‖ authorized outputs) — the identity that persists across every future state transition (KIP-20).
      • The command prints a link like kascov.io/testnet-10/c/<id>?program=<hex> — the explorer verifies your program hash against the on-chain commitment in the browser, so you can prove what you deployed before revealing it.

      The coin appears on the explorer within about a minute, labeled “p2sh commitment” until the reveal.

      3The trap: “script units exceeded” and the v0-UTXO bootstrap

      This is where most first deploys die (rusty-kaspa#1073 is the canonical report). You build a version-1 covenant transaction, set a huge computeBudget, spend a plain faucet UTXO — and the node answers:

      failed to verify the signature script: script units exceeded
      the amount committed in the input: used=100000, limit=9999

      Three facts decode that message:

      factnumber
      Toccata (v1) transactions replace the per-input sigOpCount: u8 with a per-input computeBudget: u16 commitment. 1 budget unit = 100 grams = 10,000 script units.1 → 10,000
      Every input gets a free allowance of one budget unit minus one script unit — that is the limit=9999 you hit when your committed budget is effectively 0.9,999 free
      One Schnorr OpCheckSig costs 1,000 grams × 100 units/gram — the used=100000. So even the most boring pay-to-pubkey input needs budget ≥ 10 in a v1 transaction. Witness bytes you push are charged 1:1 on top.100,000 / sig

      And the two misconceptions the error invites:

      • It is not the v0 UTXO limiting you. A v1 transaction can spend v0 (pre-Toccata) UTXOs — that is the bootstrap path; every covenant on testnet-10 started from a plain faucet UTXO. The budget is committed by the spending input, not derived from the UTXO.
      • You can’t patch the budget in afterwards. For v1 transactions the computeBudget field is covered by the signature hash. Setting it after signing invalidates the signature; and SDKs pinned to pre-Toccata wire formats silently drop the field when serializing — the node then sees budget 0 and you get limit=9999 no matter what your local object said. If your tooling predates Toccata (e.g. wasm/py SDKs frozen at 2.0.x), it cannot express this transaction.

      The fix (what kascov-labkit does, verbatim from the shipped code):

      Every input of a v1 transaction gets an explicit commitment — TransactionInput::new_with_mass(outpoint, script, seq, ComputeCommit::ComputeBudget(ComputeBudget(n))) — including the plain fee input, the one everybody forgets because it “never needed anything” pre-Toccata. kascov-lab spend exposes it as --compute-budget (default 20 per input; 1 unit = 10,000 script units).

      Committed budget is charged as compute mass whether the script uses it or not, so the fee must scale with it. labkit prices two-input constrained spends as fee = 100 sompi × (2 × budget × 100 grams + size headroom) — commit generously, but not 65535.

      For plain (non-covenant) transfers, rusty-kaspa’s own consensus-core sign() already stamps ComputeBudget(10) on every v1 input — exactly one signature check. That is what labkit’s deploy relies on.

      Or let the tool run this whole section for you: kascov’s transaction preflight takes your transaction as JSON and answers ready / will fail / incomplete before you broadcast — per-input budgets checked against exactly this trap, masses against the block limit, and the fix spelled out (“set computeBudget: 10”). Your transaction goes into the call and nowhere else.

      4Spend it — --entrypoint receive

      receive is the interesting entrypoint: it is output-constrained and needs no signature at all. Once the coin is older than period, anyone may build the spend — but the contract’s own introspection (KIP-17) forces the outputs: exactly pledge to the recipient, and the remainder re-bound to the same P2SH so the covenant lives on. Dry-run it first — this runs the real Kaspa script engine locally, without broadcasting:

      cargo run -p kascov-lab -- spend --program-hex <the same hex> --entrypoint receive --dry-run
      SIMULATE   SilverScript · Mecenas . receive  (not broadcast)
                 ✓ PASS —  the spend SATISFIES the contract — a node would accept it
                 output[0] 1.00000000 TKAS
                 output[1] 8.99999000 TKAS [covenant continues]

      (If you dry-run before period has elapsed you’ll see the engine reject honestly — the age gate compiles to OpCheckSequenceVerify. Wait out your period and re-run.) Then drop --dry-run to broadcast:

      cargo run -p kascov-lab -- spend --program-hex <the same hex> --entrypoint receive
      SPEND      SilverScript · Mecenas . receive
                 tx 05128801…

      Mechanics worth knowing:

      • The witness reveals the program: push(selector) ++ push(program) for receive (no signature args); pure-signature entrypoints prepend push(pubkey) ++ push(sig). From this moment kascov shows the coin as SilverScript · Mecenas, constructor args labeled, permanently.
      • The tx has a second, plain input paying the real network fee — the contract’s internal math hardcodes a 1000-sompi fee, and the node’s compute-mass fee must hold on top. Both inputs commit --compute-budget (see step 3).
      • The funder’s exit is --entrypoint reclaim: a pure-signature spend that works immediately, if your key’s blake2b is the coin’s funder field.

      5Replay it right here

      Open your coin’s page (kascov.io/testnet-10/c/<covenant-id>, or paste the txid into the search box above). In the UTXO panel, every spent state has a “⧉ replay this spend” button — the worker fetches the captured witness and runs it through the actual TxScriptEngine, streaming an opcode-by-opcode trace with live stacks: the age check, the introspection ops reading your outputs, the final verdict.

      The same replay is a plain JSON endpoint (API docs):

      curl -s https://kascov.io/data/testnet-10/debug/<txid> | jq '.verdict, .trace[0:3]'

      Honest caveat, stated by the endpoint itself: the replay fabricates a 1-in/1-out context, so signature and output-introspection checks can diverge from the original transaction — the witness, program and data flow are the real on-chain bytes, but a pass: false replay does not mean the on-chain spend was invalid (it was accepted by consensus).

      ★Name your token on chain (launchpads, this one’s for you)

      kascov shows a token’s real name only when that name lives on the chain itself — never from anyone’s private database. The convention is simple: put a small JSON document in the genesis transaction’s payload when you launch. Whoever authors the genesis (you, or your launchpad) is exactly the right party to claim the name, and anyone can verify it from the chain forever.

      {"name": "My Token", "ticker": "MTK",
       "image": "https://…/mtk.png",
       "image_hash": "sha256 hex of the image bytes"}

      Rules kascov applies when reading it: name up to 48 characters, ticker (or symbol) up to 12, plain JSON or hex-encoded JSON both work, and the whole payload stays under 4 KB. The token directory then shows the claimed name with a named on chain badge — honestly labeled as a deployer claim (names aren’t unique; the canonical kascov name never disappears).

      Images: include image_hash — the SHA-256 of the exact image bytes — next to the URL. Today kascov surfaces the image as a link. Hash-committed images are the path to being rendered: because the genesis pins the bytes forever, kascov can fetch, verify, and serve the art itself, and nobody can swap it afterwards. An image without a hash stays a link permanently — an unpinned URL can change under everyone’s feet, and kascov doesn’t display what the chain can’t hold to account. The KCC-0020 token draft doesn’t cover display metadata yet; if a metadata convention lands in the standards track, kascov will read that too.

      6A real production example you can inspect right now

      This exact loop ran end-to-end on the live site on July 10, 2026 — deploy → genesis → reclaim → revealed. The coin is jolly-pearl-quail (b4ade48e3ad1eda5…), a Mecenas born via the one-click web deploy, then reclaimed by the funder key with kascov-lab spend --entrypoint reclaim:

      eventtxid
      genesis (deploy — P2SH commitment, provable)a9a7df1a71307c0518aa001920b42b9afecbaa503d61c279c08e45ee6bfc8bdb
      reclaim (reveals the program; funds exit → burn)051288013eecf1cc3f890005236b96567f743423390a540650af65f50c4288b3

      Replay the reveal yourself — one curl, no setup:

      curl -s https://kascov.io/data/testnet-10/debug/051288013eecf1cc3f890005236b96567f743423390a540650af65f50c4288b3 \
        | jq '{verdict, steps: (.trace|length), fields: .trace[3].op}'

      The trace shows the witness pushing the funder’s pubkey and signature, then the full 181-byte Mecenas program whose blake2b matches the deploy commitment — the coin page shows the decoded constructor args (recipient, funder_hash, pledge, period) it recovered from those bytes.

      And it isn’t just this guide’s own loop anymore — builders are shipping on covenants. The Kaspa Covenant Game Kit (by @w00c00) runs 1v1 games on player-funded covenant escrow with on-chain settlement, and its escrow contract verifies byte-identical on kascov — inspect the verified escrow coin yourself. More projects live in the “built with covenants” showcase on the landing page.

      script playground & generator · JSON API docs · github.com/Knitser/kascov · docs/Covenant Lab.md

      Questions or a step that didn’t work as written? Open an issue — this page only documents what has actually been run.

      what’s new

      the milestones — what kascov learned to do, newest first.

        Kaspa nodes forget after roughly 30 hours. kascov remembers.

        explorer · playground · JSON API & docs · the 15-minute guide · what’s new · feed

        also built here: fees.kas your lifetime Kaspa fees · rewind your wallet’s story

        github.com/Knitser/kascov · @Knitser on X

        data streams live from the kascov indexer — it never stops watching

        not affiliated with the Kaspa Foundation