Kaspa x402

1.0.1 released · Standard and SDK · Release details

Demo Gateway Operations

1.0.1 patch deployment

The public website and demo Worker run 1.0.1. Actual Worker modules, current configuration, secret bindings and the existing Durable Object namespace were verified. The pre-existing funded head was preserved. A user-operated public browser payment on a separate network advanced it from 1.4 to 1.6 Testnet KAS; the user confirmed the identical retry, and the agent independently verified selected-chain acceptance and the successor UTXO. The automated local browser payment/retry also passed. See the deployment proof.

The original test caller correctly hit the one-grant rolling 24-hour quota. Bounded logging confirmed that category; the approved Worker was restored and its module hashes rechecked. No quota or payment state was reset.

This patch retains escrow-v5/head-v2 and demo-gateway-state-v2. Routine patch deployment preserves active payment state. The historical clean-cutover procedure below is not needed for this patch.

1.0.0 demo cutover

This section records the original 1.0.0 website and demo Worker cutover. The funded browser payment and same-transaction retry passed through the actual public origins; see the deployment proof. The earlier local qualification and RC2 deployment records remain separate historical evidence. The steps below repeat the fresh-state cutover; do not reset active payment state on routine redeploys.

  1. Run the authenticated transport relay on a Node host behind an HTTPS reverse proxy. The public demo uses the Cloudflare Container host in the existing demo account; this packages the same bounded relay and needs no separate server. It has no wallet, grants or payment database. For another Node host, create a private 0600 token file with at least 32 random bytes and a private JSON config:

    {
      "endpoints": ["wss://vector-10.kaspa.green/kaspa/testnet-10/wrpc/json"],
      "tokenFile": "/private/demo-pnn-token",
      "host": "127.0.0.1",
      "port": 8789
    }
    
    npm ci
    node scripts/demo-pnn-relay.mjs /private/demo-pnn-relay.json
    

    Only explicitly listed endpoints and seven required RPC methods are allowed. Requests/responses are capped at 4,259,840 bytes, sockets enforce the same reassembled-message cap, calls time out, and concurrency is limited to 32. HTTP redirects are rejected. Keep this transport token out of the browser.

  2. Configure the candidate Worker with the same PNN endpoints, KASPA_X402_PNN_RPC_ORIGIN=https://your-private-relay.example, and secret KASPA_X402_PNN_RPC_TOKEN containing the relay token. Keep the explicit Testnet trust policy and configure the existing admission/admin secrets. KASPA_X402_RELEASE_VERSION is 1.0.0. Enable the gateway and hash-chain demo only when the relay and fresh state are ready. A failed/missing relay must not fall back to an unbounded native WebSocket.

  3. Use a new private data directory with the existing demo:hash-chain init operator command to create a funded head-v2 genesis. Retain the owner key, signed setup and registration privately. Register its cloudflare-head.json using the admin endpoint. The candidate's demo-gateway-state-v2 object is separate from RC2's demo-gateway-v1.0.0-rc.2; do not copy old heads/channels. Preserve RC2 recovery keys and refund outstanding old channels separately.

  4. Before authorizing deployment, run the actual candidate locally against funded Testnet. The config contains sdkModule (the Node Kaspa WASM module), rpcUrl (a synced Testnet-10 Borsh endpoint), and walletFile (an isolated Testnet private-key file). Start with a clean committed candidate:

    npm run build
    npm run site:build
    node verification/proof-demo-release-e2e.mjs --out /private/controlled.json
    node scripts/proof-browser-demo-testnet.mjs --live \
      --config /private/browser-proof.json --out /private/fresh-browser-run
    

    The proof creates fresh head/payer state and exercises the built page through local HTTPS origins, the actual Worker/SQLite DO and the bounded relay. The browser claims the grant, signs/broadcasts on Testnet and receives the paid report. Retry must return the same transaction; the head must advance once. Keep the entire output directory private: it includes recovery keys/grants. Publish only the sanitized report. This is single-source Testnet evidence, not a deployed-host or independent-mainnet claim.

  5. After separate deployment authorization, coordinate the Worker and website cutover and run the same paid smoke on the public origins. Update site/release.json's observed deployment fields only after verifying them. Do not treat relabelling the RC2 gateway, or disabling its payments, as a completed 1.0.0 demo release.


Recorded RC2 operations

Development source uses escrow-v5 and head-v2 with signer-chosen sighash types; reference signing still defaults to ALL. The published/deployed RC2 service described below uses escrow-v4 and head-v1. Follow the signature policy and template transition for a development integration or operator cutover; the existing hosted RC2 service is not evidence of deployment of the new templates.

Status: operations runbook for the deployed RC2 kaspa:testnet-10 gateway. This runbook describes the public demo service at:

https://demo.kaspa-x402.org

RC2 was published and deployed on 2026-10-01. The hosted gateway uses PNN/WSS evidence for native exact, batch, and hash-chain payments. Release validation covered all 18 funded flows, hosted native/batch payments, three browser hash-chain payments across rotation, and retries after redeployment. The RC2 release reference and gateway deployment record link to the published evidence and identify the release source.

The gateway is an integration target, not a wallet, custodian, faucet, facilitator, mainnet service, or availability commitment.

Operator Controls

The Worker configuration lives in packages/demo-gateway/wrangler.jsonc.

Important non-secret variables:

Variable Purpose
KASPA_X402_GATEWAY_ENABLED Set to false to stop protected exact and batch endpoints with HTTP 503. /health, /canary, /metrics, and /supported remain visible.
KASPA_X402_PAY_TO Testnet address receiving exact payments.
KASPA_X402_SERVER_PUBLIC_KEY Testnet server public key advertised in batch escrow terms.
KASPA_X402_EXACT_AMOUNT Exact-payment price in sompi. Must be at least 10000000.
KASPA_X402_EXACT_PROFILE Exact profile: standard-native (default) or optional additive.
KASPA_X402_BATCH_AMOUNT Fixed per-request v1 RC2 batch charge in sompi.
KASPA_X402_MIN_DEPOSIT_SOMPI Batch escrow deposit floor. Must be at least 10000000.
KASPA_X402_CLAIM_RESERVE_SOMPI Advertised v1 RC2 minimum successor reserve R. Must be at least 10000000; the advertised deposit floor must cover the request ceiling plus this reserve.
KASPA_X402_REFUND_TIMEOUT_DAA_DELTA Maximum DAA horizon for the persisted absolute batch timeout. The Worker rolls the timeout only at the minimum-lead boundary.
KASPA_X402_MINIMUM_REFUND_LEAD_DAA Minimum remaining DAA lead required before accepting a batch payment.
KASPA_X402_GLOBAL_CONCURRENCY Deployment-wide cap for in-flight protected requests. Enforced by renewable leases in the gateway Durable Object; default 64, maximum 256.
KASPA_X402_PER_CALLER_CONCURRENCY Per-ingress active lease cap in the same Durable Object; default 8, maximum 256.
KASPA_X402_ADMISSION_HMAC_KEY Secret with at least 32 bytes, required when enabled. HMACs the Cloudflare ingress IP into an opaque admission key. Set as a Worker secret; do not commit it.
KASPA_X402_SITE_BASE_URL Standards site base URL used by canary checks.
KASPA_X402_RELEASE_VERSION Current release version checked against the standards site's /release.json.
KASPA_X402_GATEWAY_BASE_URL Gateway base URL used by canary checks.
KASPA_X402_HOSTED_EXACT_SETTLEMENT_ENABLED Set to true only when the hosted exact verifier, PNN broadcast path, and finality observation are deployed. Additive also requires a durable available head.
KASPA_X402_CHAIN_BROADCAST_MODE pnn for all hosted chain reads, transaction submission, and selected-chain recovery. REST mode is rejected.
KASPA_X402_PNN_ENDPOINTS Comma-separated TN10 WSS submission endpoints. In single-source mode these are one trusted verification authority, regardless of URL count.
KASPA_X402_CHAIN_TRUST_MODE Required when the gateway or hash-chain route is enabled: trusted-single-source or independent-corroboration. A single node remains supported.
KASPA_X402_PNN_VERIFICATION_AUTHORITIES In corroboration mode, JSON array of { "trustDomain": "operator-id", "endpoints": ["wss://..."] } groups. Each group is one administrative authority.
KASPA_X402_CHAIN_REQUIRED_AUTHORITIES In corroboration mode, number of distinct verification authorities required to agree; minimum 2. Conflicts always reject.
KASPA_X402_HASH_CHAIN_POOL_SIZE Maximum registered nonretired hash-chain heads in the shared Durable Object; default 1, maximum 16.
KASPA_X402_HASH_CHAIN_ENABLED Enable hash-chain exact in the existing gateway Durable Object after registering a funded Testnet head. Default: false.

The Worker must not receive a mainnet, operator wallet, head owner, or faucet key. Hash-chain registration supplies only its limited one-time grant keys. Merchant claim broadcasting is disabled in the hosted gateway package.

trusted-single-source makes the configured PNN group a full payment-authority dependency. Multiple failover URLs do not corroborate each other. Independent corroboration is recommended for higher-assurance Testnet-10 deployments; it does not satisfy the project's mainnet launch gates. A second node is not required for Testnet-10 operation. The optional corroborated mode reads origins, UTXOs, acceptance, checkpoint, finality, and selected-chain lineage from distinct configured trust domains; submission can use separate endpoints. A conflicting view fails closed. /health exposes the mode and required authority count without exposing endpoint URLs. Nearby selected tips can corroborate when their authorization facts agree; snapshot DAA scores must be within 100, and refund timing uses the highest verified DAA.

Rusty Kaspa 2.1.0 at pinned commit 01b532e8b553523216471682649693af92f0fd16 filters V2 minConfirmationCount by sink blue-score distance, with a strict distance > minConfirmationCount test. It does not measure selected-parent positions. The adapter therefore requests the unfiltered ordered V2 path, counts positions through its pinned checkpoint (including the accepting block), and records that measured depth.

Secret variables:

Variable Purpose
KASPA_X402_ADMIN_TOKEN Bearer token for additive exact-head registration, reconciliation, and stats endpoints. Set with wrangler secret put; do not commit it.

v1 RC2 State

The deployed RC2 Worker resolves GATEWAY_STATE with the logical object name demo-gateway-v1.0.0-rc.2. It uses a clean RC state model and must not import pre-RC channel or replay state.

Development state-v2 clean cutover

This development branch changes the logical Durable Object name to demo-gateway-state-v2. Deploying this branch creates a fresh object for all gateway channel, admission, replay, and hash-chain state. The new Worker never addresses demo-gateway-v1.0.0-rc.2, so it cannot read its old channel records or admission leases. There is no state migration or legacy parser. The old object remains stored but is not used by the new Worker.

Before an approved deployment, finish or refund any funded channels that still depend on the old object. Then run the build and disabled deployment commands below. Confirm /health on the new Worker, wait for a scheduled PNN canary to record a recent DAA checkpoint, and only then run the public-enable deployment command. A missing or stale checkpoint makes unsigned /exact and /batch return 503 quote_unavailable. Register new additive and hash-chain heads on the fresh object before enabling those optional profiles. Do not reuse the old head or channel records.

Deploy

Do not deploy the v1 RC2 Worker until the v1 RC2 static site is live. The Worker canary reads KASPA_X402_RELEASE_VERSION, so Worker-first deployment would fail its current-release check.

From the repository root:

npm --workspace @kaspa-x402/demo-gateway run build
npm --workspace @kaspa-x402/demo-gateway exec -- wrangler deploy --config wrangler.jsonc

The checked-in candidate configuration is fail-closed with KASPA_X402_GATEWAY_ENABLED=false. After the disabled deployment and funded operator canaries pass, the separate public-enable deployment is:

npm --workspace @kaspa-x402/demo-gateway exec -- wrangler deploy --config wrangler.jsonc --var KASPA_X402_GATEWAY_ENABLED:true

After deployment, verify:

curl -fsS https://demo.kaspa-x402.org/health
curl -fsS https://demo.kaspa-x402.org/canary
npm run check:demo-gateway

check:demo-gateway starts an isolated local Worker, proves the missing-cache 503 boundary, seeds a deterministic PNN checkpoint in its new Durable Object, then verifies exact and batch 402 offers, a corrective 402 for a foreign payment scheme, HEAD, health, and supported kinds. A deployed paid check still requires an isolated funded testnet wallet.

Batch Collection And Refunds

A successful voucher response records an authorized cumulative charge, not an on-chain merchant payout. Batch receipt transaction fields identify commitments; claims and refunds have separate on-chain transaction ids. The demo Worker's scheduled canary does not collect vouchers or hold a merchant signing key.

A merchant integration must persist vouchers and current channel lineage, run previewClaim, and configure a claimBuilder for executeClaim to build and sign the collection transaction. The claim must exceed its estimated fee and satisfy transaction/output policies. With the demo's 500-sompi price and 10,000-sompi configured claim fee, two calls (1,000 sompi) are not economical to claim. That configured fee is not a guarantee of the actual network fee; estimate against the actual transaction.

Schedule collection with enough time for confirmation and recovery before the absolute refund DAA. The server's minimum refund lead limits new payments; it does not schedule collection. After timeout, the buyer must submit and confirm a refund. The merchant claim branch has no matching expiry, so claim and refund can compete until a spend is accepted. An accepted refund returns the remaining escrow less its fee, including charges the merchant has not collected.

Batch v3 charges a fixed price per accepted call, not a later measured token count. Payment does not prove useful delivery; handlers with non-repeatable side effects need their own durable idempotency/outbox handling. Top-ups require both buyer and provider authorization.

Exact Profiles And Additive Heads

Hosted exact is enabled only when KASPA_X402_HOSTED_EXACT_SETTLEMENT_ENABLED=true. The default standard-native profile requires no merchant inventory. Optional additive also requires at least one durable merchant-owned KIP-10 head. The Worker does not hold the merchant wallet key and does not create heads from public requests.

  1. Create one or more independent merchant-owned KIP-10 additive heads from an isolated TN10 wallet.
  2. Record each head id/version, current outpoint and amount, serialized script public key, redeem script, additive threshold, status, and timestamps.
  3. Register the head records with the Worker:
KASPA_X402_DEMO_ADMIN_TOKEN=<token> \
  npm run demo:exact-heads -- register --file heads.json
  1. Confirm head availability:
KASPA_X402_DEMO_ADMIN_TOKEN=<token> npm run demo:exact-heads -- stats
curl -fsS https://demo.kaspa-x402.org/supported

standard-native advertises exact without a head. additive advertises exact only while a matching head is available. Issuing an unpaid 402 does not reserve, retire, or consume a head. Settlement atomically claims the advertised current outpoint, verifies a same-script successor whose delta equals the exact price, and advances that same durable lineage. Stale competing clients receive a fresh 402 against the current head.

Before each additive offer, the Worker confirms only the selected durable current outpoint against the accepted address UTXO set. It makes at most two bounded candidate attempts, independent of total head inventory. A missing or conflicting selected head becomes unavailable. Full-pool checks are operator work, not anonymous request work. If a known external transaction advanced a head, provide the complete ordered accepted lineage to recover it:

KASPA_X402_DEMO_ADMIN_TOKEN=<token> \
  npm run demo:exact-heads -- reconcile \
  --head-id <head-id> \
  --transactions <first-txid>,<next-txid>

The gateway accepts only a chain in which every transaction spends the prior outpoint, preserves the same script and output index, satisfies the KIP-10 threshold, and ends at the current unspent head. Never use an arbitrary output to the same P2SH address as lineage evidence.

Rollback

Use the Cloudflare Workers deployment list for the project named kaspa-x402-demo-gateway.

Rollback procedure:

  1. Identify the last deployment that served valid /health and /canary.
  2. Roll back to that Worker version in Cloudflare.
  3. Confirm https://demo.kaspa-x402.org/health returns ok: true.
  4. Run an unpaid batch request and confirm HTTP 402; confirm /exact returns 503 exact_unavailable unless a working hosted exact settlement path is deliberately enabled and tested.
  5. Record the version, reason, and verification result in the operator notes.

Emergency Disable

Set:

KASPA_X402_GATEWAY_ENABLED=false

Deploy the Worker. Protected endpoints must return:

{ "ok": false, "error": "gateway_disabled" }

Health and canary endpoints stay readable. /health proves only that the Worker loaded its configuration; use a fresh /canary result and funded proof to assess chain readiness.

Re-enable by restoring:

KASPA_X402_GATEWAY_ENABLED=true

Chain Evidence Or PNN Outage

The Worker uses the configured Testnet-10 PNN/WSS nodes for all funding, UTXO, DAA, transaction, and selected-chain evidence. It does not depend on the public REST transaction index. The reference batch policy still requires 30 confirmations proven by selected-chain traversal; blue-score difference alone is insufficient. /health shows configuration; /canary probes the node.

  1. If node evidence is unavailable, disable the gateway.
  2. Switch only to reviewed kaspa:testnet-10 PNN endpoints; never to mainnet.
  3. Unknown, removed-chain, or incomplete evidence fails closed. Never discard a stored pre-broadcast checkpoint to manufacture a successful retry.
  4. Re-enable after a fresh /canary, batch deposit and replay rejection, and exact payment checks pass.

The gateway persists PNN funding snapshots before exact submission and stores accepted transaction receipts for recovery. Offers also preserve a bounded ring of 128 node checkpoints, with one per 300 DAA scores, so a new batch deposit can be located from a checkpoint before its creation. Without a stored checkpoint, historical transactions use a bounded selected-parent traversal (at most 1,024 blocks, subject to the node timeout). A missing or pruned origin is unavailable evidence, not permission to weaken admission. Existing channels retain their verified genesis evidence and lineage cursor. Each batch lineage read stops at a fixed selected-chain checkpoint, including the recent chain data so its cursor cannot skip an unconfirmed transition. Covenant transitions still need the configured 30-block depth; a recent transition keeps the lane pending until that evidence is available. PNN receipts have separate limits of 4,096 records, 64 MiB total, and 64 KiB per record. Capacity exhaustion fails closed and requires operator maintenance; uncertain or active evidence is not automatically discarded.

On restart, resume every batch observer from its stored checkpoint. Process removed blocks before additions. If history is pruned, branching, or otherwise incomplete, keep the lane unavailable; do not reset its checkpoint, scan from a new arbitrary block, or trust peer channel metadata. A removed refund restores refund-only state and requires authoritative reconciliation before retry.

Scheduled Canary

The Worker runs a non-spending scheduled canary every 15 minutes.

The canary checks:

The scheduled canary skips paid exact and replay checks because the Worker does not hold spending keys. Those checks must be run manually from an isolated funded testnet wallet so canary failures cannot spend unbounded funds.

Read the enabled state and latest canary report:

curl -fsS https://demo.kaspa-x402.org/canary

An operator should check /canary after each gateway or site deployment and at least once per day while the demo is advertised. If ok is false, disable broad public guidance until the failed check is understood and fixed.

Manual Paid Canary

Use an isolated testnet key. Do not import a key that controls mainnet funds.

Minimum manual paid checks:

  1. If exact is not deployed with a working KIP-10 settlement path, request GET /exact and confirm HTTP 503 exact_unavailable.
  2. If exact is deployed, request GET /exact and confirm HTTP 402. For standard-native, build an ordinary exact native transfer. For additive, spend the advertised head and increase its same-script successor by exactly the advertised amount, with no separate merchant payment output. Retry with PAYMENT-SIGNATURE, and confirm the Worker broadcasts the artifact through PNN, observes accepted finality, and returns HTTP 200.
  3. Retry the identical paid exact request and confirm idempotent HTTP 200.
  4. Present the same exact transaction to a different resource and confirm conflict rejection.
  5. Open a batch channel with a deposit-voucher payment and confirm HTTP 200.
  6. Reuse the channel with a voucher-only payment and confirm HTTP 200.
  7. Replay an earlier stale batch voucher after the later voucher and confirm corrective HTTP 402.

Record transaction ids, output indexes, Worker version, response status, and PAYMENT-RESPONSE summaries in the operator notes.

The committed hosted exact proof pays /exact with a signed exact transaction artifact, confirms the Worker-broadcast transaction is accepted, retries the same payment for idempotency, and rejects the same transaction on a different resource. In default standard-native mode it needs no admin state. In additive mode it funds and registers a reusable head first:

KASPA_X402_RPC_URL=<tn10-rpc-url> \
KASPA_X402_FUNDING_WALLET=wallet-key:/path/to/testnet-key \
KASPA_X402_KASPA_WASM_MODULE=/path/to/kaspa.js \
KASPA_X402_EXPECTED_GATEWAY_ORIGIN=https://demo.kaspa-x402.org \
KASPA_X402_EXPECTED_EXACT_PROFILE=standard-native \
KASPA_X402_EXPECTED_EXACT_AMOUNT=20000000 \
KASPA_X402_EXPECTED_EXACT_PAY_TO=<expected-merchant-address> \
KASPA_X402_LIVE_CONFIRM=I_UNDERSTAND_THIS_USES_TESTNET_FUNDS \
  npm run proof:hosted-exact

Set KASPA_X402_EXACT_PROFILE=additive and KASPA_X402_EXPECTED_EXACT_PROFILE=additive, then provide KASPA_X402_DEMO_ADMIN_TOKEN=<token> to exercise the optional head profile. The additive proof derives and pins the locally created head address when an explicit expected payTo is absent. The proof refuses to sign if the gateway origin, resource, profile, amount, recipient, or network differs from the operator pins.

Also confirm that unsupported legacy exact-transfer evidence is rejected and does not return protected content.

Hash-chain Demo

The RC2 hash-chain demo runs in the existing GatewayState Durable Object. Grant tables use its SQLite storage; payment responses use the existing gateway ledger. Pages, the Worker, its binding, and the standard exact and batch routes remain the same. One funded head and manual resets are sufficient for this Testnet demo. No Container or separate server is required.

The hash-chain route uses the configured Testnet-10 PNNs for fresh UTXOs and selected-chain V2 transaction evidence. It snapshots the payer's accepted funding outputs before delivering a signing grant because spent outputs can disappear from the public REST index. Paid retries recheck selected-chain acceptance. The public REST API is not the hash-chain verification source.

Cloudflare Hosting

  1. Build and check the Worker and browser client:

    npm run build
    npm run site:build
    npm run check:hash-chain-worker
    npm run check:browser-demo
    
  2. Use a private local operator config. Set dataDir to a new private directory, sdkModule to the pinned Node WASM SDK, rpcUrl to a synced Testnet-10 node, and walletFile to the funded Testnet operator wallet. Also set publicBaseUrl to https://demo.kaspa-x402.org and adminTokenFile to the private file containing the existing gateway admin token. Run:

    node scripts/hash-chain-demo.mjs init --config <private-config> --live
    

    This funds a 1 KAS head and creates 32 fresh one-use grants. It saves cloudflare-head.json with mode 0600 alongside the local owner key and grant database. Keep these files outside the published site.

  3. Deploy the Worker with KASPA_X402_GATEWAY_ENABLED=true, preserving the live gateway variables and existing admin secret. Leave KASPA_X402_HASH_CHAIN_ORIGIN empty. Publish the fresh head:

    node scripts/hash-chain-demo.mjs publish --config <private-config>
    

    Publication uses the authenticated HTTPS admin route and returns only public head metadata. The operator wallet and owner keys stay local. Use this head only in the Cloudflare demo; the local Node service must not issue its grants.

  4. Set KASPA_X402_HASH_CHAIN_ENABLED=true, deploy the Worker, and deploy the static site to the existing Pages project. Check /hash-chain/status and /supported, then make a funded browser payment and retry that same payment. Check the existing exact and batch demos too.

An expired disclosed grant automatically enters needsRotation. The same claimant is limited to one disclosure per 24 hours and its qualifying funding outpoint remains in a durable tombstone across restart and owner rotation. Assigned grants from a pre-admission store remain readable for authenticated retry and selected-payment recovery. Because their original qualifying funding outpoint is unknown, that payer key cannot qualify another grant; the payer must use a fresh key and newly funded output for a later grant. To recover capacity, the operator signs and broadcasts the owner rotation from its own wallet, waits for selected-chain acceptance, then sends the rotation transaction ID, an earlier selected-chain checkpoint, and the matching fresh private grant chain to authenticated HTTPS POST /admin/hash-chain/recover. The Worker verifies that the selected transaction spends the quarantined head and installs the new guard only after acceptance. It never holds or uses the owner private key. Register independent funded heads up to the configured pool size before service; another ready head can continue issuing while one needs rotation. Ordinary exact and batch routes remain available during recovery.

Caller Admission

The Worker derives an opaque caller identity from Cloudflare's ingress IP and sets X-KASPA-X402-DEMO-CALLER on its private Durable Object call. The service returns that identity with the quote so the browser binds payment to the same caller. The four-live-quote limit applies per public IP; users sharing an IP share that limit. Use the same public IP for quotes, payments, and retries. Resource requests without trusted caller metadata are unavailable. Local Worker checks must supply CF-Connecting-IP; direct Node service checks also need the proxy bearer token and a 64-character lowercase hex caller header.

Optional Node Hosting

For a separate Node host, the Worker can proxy the same resource and grant routes under the public origin. Use this option instead of Cloudflare hosting. This optional Node adapter still uses a REST index for selected-chain reads; it is not the PNN-only configuration deployed by the RC2 public gateway.

Build the workspaces first (npm run build). Use Node 22.13 or newer and the pinned Rusty-Kaspa 2.0.0 Node WASM SDK, including its websocket dependency. Create a private config file, outside the published site, with:

{
  "dataDir": "./.kaspa-x402-live/hash-chain-demo",
  "sdkModule": "./.kaspa-x402-live/runtime/sdk-v2.0.0/kaspa.js",
  "rpcUrl": "wss://your-testnet-10-node/kaspa/testnet-10/wrpc/borsh",
  "apiBase": "https://api-tn10.kaspa.org",
  "publicBaseUrl": "https://demo.kaspa-x402.org",
  "walletFile": "./.kaspa-x402-live/operator-wallet.json",
  "proxyTokenFile": "./.kaspa-x402-live/hash-chain-proxy-token",
  "port": 8788
}

The operator wallet file contains a funded Testnet private key as plain hex or JSON with a private_key field. Wallet and token files must have mode 0600. Generate a random proxy token of at least 32 characters and save it privately. The service listens on loopback by default; expose it through an HTTPS reverse proxy or tunnel. Keep its data directory on persistent disk.

  1. Create the demo head with explicit Testnet broadcast permission:

    node scripts/hash-chain-demo.mjs init --config <private-config> --live
    

    This funds a 1 KAS merchant head and installs 32 one-use grants. It waits for 30 genesis confirmations. The private directory holds the encrypted issuer database, its encryption key, and the owner key.

  2. Start the Node process:

    node scripts/hash-chain-demo.mjs serve --config <private-config>
    

    The same private directory holds payments.sqlite, which saves exact settlement attempts, handler results, and paid responses. Restarting the process preserves identical paid retries, including after quote expiry. Keep both databases together on persistent disk and run one service process.

  3. Configure the Worker with KASPA_X402_HASH_CHAIN_ORIGIN set to the service's HTTPS origin. Set KASPA_X402_HASH_CHAIN_PROXY_TOKEN as a Wrangler secret containing the same token. Deploy this separately from the static website. An empty origin leaves the existing demo working and hash-chain unavailable.

  4. Check /hash-chain/status, /supported, and an unpaid /hash-chain/report quote. Then run one funded payment in the browser. The default price is 0.2 KAS. The target fee is 0.01 KAS; small change may raise it to at most 0.1 KAS, as shown before payment and in the result.

For an abandoned grant or an exhausted chain, stop the Node process, wait for any assigned grant to expire, and run:

node scripts/hash-chain-demo.mjs status --config <private-config>
node scripts/hash-chain-demo.mjs rotate --config <private-config> --live
node scripts/hash-chain-demo.mjs serve --config <private-config>

Rotation uses the owner key and a separate funding input for its fee, preserves the merchant value and covenant ID, and installs a fresh set of grants. It does not reuse an abandoned key. If setup or rotation was interrupted after submission, recover --config <private-config> checks the saved transaction and records its accepted result without broadcasting again. Resolve a pending setup before another init/rotation. Keep the pending setup file private.

The service uses selected-chain PNN evidence and fresh PNN UTXO reads for the current head. If the configured Testnet nodes fail, it stays unavailable/pending. Local simulation checks do not establish live payment acceptance. Enable the public flow only after the deployed paid browser check.

Durable State Policy

The hosted gateway uses one SQLite-backed Durable Object. It stores exact replay records, payment identifiers, batch channels, settlement commitments, locks, rate counters, metrics, and the latest canary report.

Each v1 RC2 batch row also owns its immutable covenant launch manifest, append-only accepted/removed lineage journal, durable selected-chain checkpoint, and atomically derived current head. Those fields must be committed together.

Policy for the public release candidate:

For v1 RC2, demo-gateway-v1.0.0-rc.2 is the authoritative Testnet state. Re-register only independently verified, still-unspent additive heads after a deliberate reset, and require batch clients to create new v1 RC lanes.

Rotate Addresses And Keys

Use this when a testnet address is too noisy, a test key is suspected exposed, or a clean public demo history is needed.

  1. Disable the gateway.
  2. Generate a new kaspatest: pay-to address using an isolated testnet wallet.
  3. Generate a new server public key for batch terms. Keep any private signing material outside the Worker.
  4. Update KASPA_X402_PAY_TO and KASPA_X402_SERVER_PUBLIC_KEY.
  5. Keep the current release's logical Durable Object name unless an incident explicitly requires another disclosed fresh-state cutover.
  6. Deploy and verify /health, /canary, unpaid offers, paid exact only when hosted exact settlement is enabled, batch deposit-voucher, voucher-only reuse, and replay rejection.
  7. Re-enable the gateway.

Incident Note Template

Date:
Operator:
Worker version:
Incident:
User-visible impact:
Gateway enabled state:
Chain evidence:
Canary result:
Actions taken:
Paid evidence affected:
Follow-up:

Keep incident notes factual and testnet-scoped.

Source: /docs/demo-operations.md