Skip to content

Tools

Environment reference

You supply thisYou can find any variable, its validation rule, and its failure mode in a few seconds.

Fourteen variables are required and the process will not start without them. The rest have defaults that are safe, which usually means closed.

Environment variables

36 of 36

TANDEM_NETWORKbinding

Required. One of mainnet, signet, testnet4, regtest.

Selects the network byte used in every marker and in the namespace commitment, and sets the chain identity the readiness probe expects from Bitcoin Core. The value mainnet maps to the chain name main when comparing against getblockchaininfo.

An unrecognised value fails the boot with `unsupported TANDEM_NETWORK`.

TANDEM_INIT_TXIDbinding

Required. 32-byte lowercase hex.

The configured INIT transaction id in the usual display order. It is one third of the deployment binding and it is never discovered by scanning. A different txid is a different protocol identifier even when every payload field is identical.

Reversed to wire order exactly once before it enters the namespace preimage.

TANDEM_SPEC_HASHbinding

Required. 32-byte lowercase hex.

SHA256 over the exact bytes of the normative specification file. It is the third part of the binding, and the INIT payload has to carry the same value for the deployment to activate.

A raw digest, so unlike a txid it is never byte reversed.

TANDEM_INIT_HEIGHTbinding

Required. Non-negative integer.

The canonical height the configured INIT transaction confirms at. It also sets the floor for reorg rollback, which never crosses one block below it.

The open height must be at least 1,008 blocks later or the boot fails.

TANDEM_OPEN_HEIGHTbinding

Required. Non-negative integer.

The first height at which a CREATE can be valid. A CREATE confirming before it is rejected with BAD_HEIGHT_OR_PHASE.

TANDEM_CLOSE_HEIGHTbinding

Required. Must equal the open height plus 4,320.

The end of the founding window, as a half open bound. A CREATE at or after it still creates a valid object, just not a founding one.

Any other value fails the boot with a message naming the 4,320 block window.

TANDEM_NAMESPACEbinding

Required. 32-byte lowercase hex.

The namespace commitment over the domain tag, the network byte, the INIT txid in wire order, and the spec hash. Every non-INIT marker has to carry this exact value or it is rejected as WRONG_NAMESPACE.

The service derives it independently and refuses to start if your value differs, so it is a checksum on the whole binding rather than a free parameter.

NODE_ENVservice

Optional. Defaults to development.

Reported in the service configuration. Compose sets it to production.

HTTP_HOSTservice

Optional. Defaults to 127.0.0.1.

The interface the HTTP server binds to. Compose overrides it to 0.0.0.0 so the published port reaches the application.

Set this explicitly only when you intend to accept connections from outside the host. There is no authentication in front of any route.

PORTservice

Optional. Defaults to 3021.

The listening port. The container health check reads the same variable when probing liveness.

BITCOIN_RPC_URLbitcoin

Required.

The JSON-RPC endpoint of a dedicated Bitcoin Core node. Inside a container this must not be a loopback address, because loopback points at the container itself.

BITCOIN_RPC_USERbitcoin

Required.

The RPC user, sent with HTTP Basic authentication on every call.

BITCOIN_RPC_PASSWORDbitcoin

Required.

The RPC password. Load it from a secret manager rather than a file in source control.

BITCOIN_RPC_TIMEOUT_MSbitcoin

Optional. Defaults to 15000.

Abort timeout for a single RPC call. There is one attempt and no retry, backoff, or connection pooling, so this value is the entire patience budget.

BITCOIN_ZMQ_HASHBLOCKbitcoin

Optional. Blank by default.

A ZMQ endpoint for block hash notifications. When set, the service subscribes and emits an internal event.

Nothing currently listens for that event, so notifications are received and discarded. Treat ZMQ as intended design rather than as working delivery.

BITCOIN_ZMQ_RAWTXbitcoin

Optional. Blank by default.

A ZMQ endpoint for raw transaction notifications.

As with the block topic, no listener is registered. There is also no raw transaction deserializer in this repository.

BITCOIN_ZMQ_SEQUENCEbitcoin

Optional. Blank by default.

A ZMQ endpoint for the sequence topic.

Only the first message frame is read, so Bitcoin Core's per-topic counter in the following frame is discarded and message loss cannot be detected.

MYSQL_HOSTdatabase

Required.

Database host. Compose overrides it to the service name mysql.

MYSQL_PORTdatabase

Optional. Defaults to 3306.

Database port.

MYSQL_USERdatabase

Required.

Database user. It needs data access plus the DDL rights to run migrations, unless you run migrations separately as a different user.

MYSQL_PASSWORDdatabase

Required.

Database password.

MYSQL_DATABASEdatabase

Required.

Database name. Use an empty, dedicated schema. The connection uses the utf8mb4_bin collation and the UTC timezone, and reads big numbers as strings.

READINESS_MAX_BLOCK_LAGreadiness

Optional. Defaults to 2.

How many blocks the canonical tip may trail the node height before readiness reports canonical_tip_stale.

AGREEMENT_KEY_IDsigning

Optional, but required as soon as any key is set.

The identifier that travels in the signed envelope so a verifier knows which trusted public key to use. Limited to letters, digits, dot, underscore, colon and hyphen, up to 128 characters.

Setting a private or public key without a key id fails the boot.

AGREEMENT_PRIVATE_KEY_HEXsigning

Optional. 32-byte lowercase hex.

The Ed25519 signing key for this pipeline's agreement tuples. Without it the readiness probe reports agreement_signer_unavailable and every verified route stays closed.

Read as raw hex from the environment. There is no file, HSM, or KMS loader, so custody is entirely your design.

AGREEMENT_PUBLIC_KEY_HEXsigning

Optional. 32-byte lowercase hex.

An optional assertion of the public key. If present it must equal the key derived from the private key, otherwise the signing boundary is treated as unconfigured.

Useful as a deployment guard against pasting the wrong secret.

TANDEM_PARSER_COMMITsigning

Optional. 20-byte lowercase hex, so 40 characters.

The commit of the parser artifact this pipeline is running. Part of the signed release identity.

All four release identity values must be present before this pipeline can sign anything.

TANDEM_INDEXER_COMMITsigning

Optional. 20-byte lowercase hex.

The commit of the indexer artifact this pipeline is running.

TANDEM_PARSER_BINARY_SHA256signing

Optional. 32-byte lowercase hex.

The digest of the parser artifact. Signed evidence of exactly which build produced the numbers.

TANDEM_INDEXER_BINARY_SHA256signing

Optional. 32-byte lowercase hex.

The digest of the indexer artifact.

Nothing in the repository recomputes these against a real artifact, so their accuracy is an operator commitment.

PIPELINE_B_BASE_URLverification

Optional. Absolute http or https URL.

Where the independently operated second pipeline lives. This pipeline requests its agreement at that base URL followed by /agreement/{height}. Trailing slashes are stripped.

Rejected if it carries credentials, a query string, or a fragment. Unset means the verified surface returns 503 for everything.

PIPELINE_B_REQUEST_TIMEOUT_MSverification

Optional. Defaults to 5000.

How long to wait for the second opinion before treating it as unavailable. A slow pipeline B closes the surface just as firmly as a wrong one.

PIPELINE_A_TRUSTED_KEYS_JSONverification

Optional. JSON object, defaults to empty.

Maps each accepted key id to a 64 character lowercase hex Ed25519 public key for this pipeline. An envelope whose key id is absent is rejected before its signature is even checked.

Parsed once at boot and frozen, so rotating a key needs a restart.

PIPELINE_B_TRUSTED_KEYS_JSONverification

Optional. JSON object, defaults to empty.

The same map for the second pipeline.

Nothing checks that the two maps are disjoint. Confirm yourself that no single signer can satisfy both sides of the comparison, because that would quietly turn two opinions into one.

TANDEM_VERIFIED_MAINNET_ENABLEDverification

Optional. Defaults to false.

When the network is mainnet and this is not exactly true, the gateway refuses before it asks this pipeline to sign anything. The check lives inside the gateway, so it cannot be bypassed by routing.

Only the strings true and false are accepted. Anything else fails the boot.

MYSQL_ROOT_PASSWORDcompose

Compose only. Not read by the application.

Used by the MySQL container to initialise the instance and by its health check. The indexer never reads it.

Three of them decide whether the verified surface answers at all, and all three default to closed:

  • PIPELINE_B_BASE_URL is unset, so there is nobody to compare against.
  • PIPELINE_A_TRUSTED_KEYS_JSON and PIPELINE_B_TRUSTED_KEYS_JSON are empty objects, so no signer is trusted.
  • TANDEM_VERIFIED_MAINNET_ENABLED is false, so mainnet is refused even if everything else is configured.

A fresh install returns 503 from every verified route, though not yet because of these three. The gateway probes readiness first, and readiness cannot pass without a canonical tip and a matching checkpoint, so the log reads pipeline A is not ready at a canonical height. These three are what keep the surface closed once readiness does pass. Either way it is the system working, not a misconfiguration to route around.

Configuration is validated once, at startup, before anything connects. A missing required variable, a hash that is not 32 bytes of lowercase hex, a namespace that does not match the derived commitment, a close height that is not the open height plus 4,320, an init lead under 1,008 blocks, a trusted key map that is not valid JSON, or a pipeline B URL carrying credentials will each stop the process with a message naming the variable.

This is worth relying on. If the service is running, its binding is internally consistent.

AGREEMENT_PRIVATE_KEY_HEX is read as raw hex from the environment. There is no file loader, no HSM integration, and no KMS integration in this repository, so how that value reaches the process is entirely your design and entirely your risk.

PIPELINE_B_TRUSTED_KEYS_JSON has a gap the code does not close: nothing checks that it is disjoint from the pipeline A map. If the same key id and public key appear in both, a single signer can satisfy both sides of the comparison and the second opinion stops being second. Verify that yourself.

To build a consistent binding from scratch, use the configuration builder. For what each variable means operationally, configuration works through them in context.