Operate & Troubleshoot

Operational guide for running a Bitcoin Computer Node and diagnosing empty results, auth failures, schema upgrades, and version mismatches. For install and .env reference see the Node overview.

Authentication

Almost all HTTP routes go through stateless public-key authentication.

How it works

  1. The client signs a message: SHA256(BCN_URL + timestamp) with the wallet private key.
  2. It sends an Authentication header:

    Authentication: Bearer <base64(signature:publicKey:timestamp)>
  3. The node checks:
    • Header is present
    • Timestamp is recent (within a few minutes; see SIGNATURE_FRESHNESS_MINUTES)
    • Signature matches publicKey for that origin + timestamp
    • Timestamp is strictly newer than the last successful auth for that public key (replay protection)

@bitcoin-computer/lib builds this header automatically for every request when the Computer wallet has a private key (normal constructor usage).

Curl and external clients

Plain curl without a signed header gets 401:

{ "error": "Auth failed with error 'no Authentication key provided' ..." }

Options:

  • Prefer the lib client (new Computer({ url, chain, network, ... })) for app code.
  • For manual calls, sign with the same scheme as createBCNAuthHeader in the library (base URL must equal the node’s configured BCN_URL).

Common auth errors

Symptom Likely cause What to do
401 no Authentication key Missing header Use lib, or add a valid Bearer token
401 Signature is too old Clock skew or reused token Sync clocks; mint a new timestamp each request
401 Please use a fresh authentication token Replayed or out-of-order timestamp Do not reuse headers; ensure monotonic client timestamps
401 origin / public key mismatch BCN_URL on node ≠ URL the client signs Align .env BCN_URL with the URL clients use (e.g. http://127.0.0.1:1031)

Health checks may skip auth depending on deployment; normal API routes do not.


Empty or incomplete results

The chain stores transactions; the node indexes outputs, inputs, and modules into Postgres. Queries hit the index, not a full re-scan of the chain each time.

Checklist

  1. Chain / network / URL
    Client chain, network, and url must match the node (BCN_CHAIN, BCN_NETWORK, BCN_URL / port).

  2. Sync status
    On mainnet/testnet the node may take a long time to catch up. Empty get-txos / modules while height is still catching up is expected. Watch logs until workers finish backfill; use regtest for local development.

  3. Indexer lag after write
    After broadcast / new / deploy, wait until the tx is indexed:

    await computer.waitForIndexed(txIdOrRev)
    // then:
    await computer.getOUTXOs({ publicKey })
    await computer.getModules({ limit: 50 })

    Or poll isIndexed. Prefer this over fixed sleep.

  4. Wrong query shape

  5. Mempool cleanup
    Unconfirmed rows can be removed after the stale grace period (clean-mempool). Apps can listen with streamMempoolCleanup.

  6. Reorg
    Confirmation fields (blockHash / blockHeight) on outputs and modules are cleared for orphaned blocks. Re-query; do not assume a previous confirmation is final until deeper.

“I see the tx on the chain but not in the API”

Check Action
Tx in bitcoind, not in DB Sync/ZMQ path; inspect node logs for parse/insert errors
Tx in Output but not in Module Deploy format (must be { ept } or taproot BC); node version with Module table
GET /module/:mod 404 Specifier wrong, not indexed yet, or hard-deleted unconfirmed row
Client getModules empty Same as above + auth + filters (storageType, isConfirmed)

Module table (schema upgrade)

New nodes create the Module table from db_schema.sql.

Existing Postgres volumes created before module indexing need the table applied manually (schema is CREATE TABLE IF NOT EXISTS, but older DBs never ran that statement).

Apply on an existing database

CREATE TABLE IF NOT EXISTS
  "Module" (
    "mod" VARCHAR(70) NOT NULL PRIMARY KEY,
    "ept" TEXT NOT NULL,
    "storageType" VARCHAR(16) NOT NULL,
    "blockHash" VARCHAR(64),
    "blockHeight" INTEGER,
    "timestamp" timestamp default CURRENT_TIMESTAMP not null
  );

CREATE INDEX IF NOT EXISTS "ModuleBlockHashIndex"
ON "Module"("blockHash");

CREATE INDEX IF NOT EXISTS "ModuleBlockHeightIndex"
ON "Module"("blockHeight");

Connect with:

docker exec -it <postgres_container> psql -h localhost -p 5432 -U bcn bcn

Symptoms if the table is missing: 500s on /modules or /module/:mod, or insert errors when indexing deploys.

Backfill

New deploys are indexed from ZMQ and sync. Historical deploys before the feature may require a re-sync or targeted re-index of those blocks (not automatic for already-synced height). For regtest, a clean npm run clean + up is simplest.


Version compatibility

Use the same release version of @bitcoin-computer/lib and the Bitcoin Computer Node.

Mismatch symptom Typical cause
Module routes 404 / table missing Old node without Module API/schema
Client cannot parse module txs / load fails Lib expects { ept } / BC; node or peers on legacy assumptions
decode vs load confusion Lib rejects module deploys in decode (ModuleDecodeError)
Empty index after upgrade Schema not applied (see above) or node not restarted

See also Breaking changes for protocol-level wire format notes (module deploys, protocol id BC).

Pin versions in apps:

{
  "dependencies": {
    "@bitcoin-computer/lib": "0.27.0-beta.1"
  }
}

Match the node image / monorepo tag to the same version line.


Quick FAQ (ops)

401 Unauthorized

Missing or invalid Authentication header, clock skew, or BCN_URL mismatch. See Authentication.

404 Module not found / Not found

Unknown specifier/tx, not yet indexed, or cleaned from mempool. Use waitForIndexed, confirm chain tip, check Module / Output tables.

400 Invalid module specifier / validation errors

Path or query params failed validation (e.g. mod not txid:vout, bad storageType). Fix the client request.

Empty [] with 200

Usually filters, sync lag, or indexer lag—not always an error. Walk the empty results checklist.

Reorg: confirmation fields null

Expected. Rows remain; blockHash / blockHeight cleared until re-confirmed.