Coldtill Get Coldtill

Configuration

Paste into Claude Code, Codex, Cursor or your favourite coding agent. View Markdown

Coldtill is configured with environment variables. coldtill init writes the essentials to deploy/.env, which Docker Compose passes to every container; the compose file adds per-service values.

Coldtill was called Keyhold before. Each COLDTILL_ variable that isn't set falls back to its old KEYHOLD_ name and logs a warning, so an existing .env keeps working until you rename it. API keys issued before the rename (kh_…) keep working too; new ones start ct_.

Core

Variable Default Purpose
COLDTILL_MODE testnet testnet or mainnet. Decides which networks are seeded. Each install runs in one mode
COLDTILL_SITE_URL http://localhost:8080 Public URL. Used for checkout links, cookies and the CSRF origin check. Mainnet requires https://
COLDTILL_LISTEN :8080 Address the api process listens on
COLDTILL_DATABASE_URL local default PostgreSQL connection URL
COLDTILL_SECRET none 64 hex characters (32 bytes). Signs sessions, seals two-factor and webhook secrets, keys the payout approval codes. Required by api and watcher; the compose file blanks it for sweeper and payouts
COLDTILL_TRUST_PROXY false Take the client IP from the rightmost X-Forwarded-For entry. The compose file sets it for api, behind Caddy. Leave it off if nothing in front of Coldtill sets that header
COLDTILL_SECURE_COOKIES true for https:// site URLs Mark session cookies Secure

Hot wallets

Variable Default Purpose
COLDTILL_AGE_RECIPIENT none Public half of the hot-wallet identity. The api uses it to encrypt keys you generate or import
COLDTILL_AGE_IDENTITY_FILE /data/coldtill-identity.age sweeper and payouts only: the encrypted identity
COLDTILL_HOT_PASSPHRASE_FILE none sweeper and payouts only: file holding the passphrase, for unattended restarts
COLDTILL_HOT_PASSPHRASE none The passphrase itself. Takes precedence over the file. Use it to supply the passphrase at start-up instead of storing it

Editions

Coldtill is built in three editions. The rate is compiled into the binary and enforced on chain for new deposit wallets.

Edition Build Sweep fee Who
Pro default (EDITION=pro) 0% Paid licence, production
Standard -tags standard 1% Cheaper paid tier
Free -tags free 2% Free to use

Fee-edition images need the FeeConfig addresses at build time:

make image-free FEE_CONFIG_EVM=0x… FEE_CONFIG_TRON=T…

Do not lock FeeConfig or the Solana program until the NFT-share / splitter recipient is final. coldtill-fees lock-evm refuses unless COLDTILL_CONFIRM_FEE_LOCK=yes.

Solana

Variable Default Purpose
COLDTILL_SOL_PROGRAM_ID EuGW7oFaoXT2acbTyNUzhRjs4sY1W8iq7BPKkGWZ1bk8 Deposit program. Change it only if you deploy your own build; the dashboard asks for extra confirmation when it differs
COLDTILL_SOL_GENESIS devnet or mainnet by mode Pins the Solana cluster. Watching, sweeping and paying out refuse to run against a different cluster. any disables the check, for local validators only

Email

Payout approval codes are emailed. On testnet without SMTP they are printed to the api log instead. On mainnet Coldtill starts without SMTP, but held payouts can't be approved until it is configured.

Variable Default Purpose
COLDTILL_SMTP_HOST none SMTP server
COLDTILL_SMTP_PORT 587 SMTP port (STARTTLS)
COLDTILL_SMTP_USERNAME, COLDTILL_SMTP_PASSWORD none SMTP credentials
COLDTILL_SMTP_FROM none Sender address

Update checks

Variable Default Purpose
COLDTILL_UPDATE_CHECK on Once a day the API asks coldtill.com for the latest version and security advisories, sending only its version and edition. off turns it off
COLDTILL_UPDATE_FEED https://coldtill.com/releases.json Release feed URL, for mirrors or testing. Format: Updates and advisories

Auto-convert

Variable Default Purpose
COLDTILL_ONECLICK_URL https://1click.chaindefuser.com NEAR Intents 1Click API. Setting it in testnet mode turns auto-convert on against that endpoint, for testing with a stub
COLDTILL_ONECLICK_API_KEY none Optional 1Click JWT. Without one, 1Click adds a 0.2% fee to every quote
COLDTILL_THORNODE_URL, COLDTILL_MIDGARD_URL Liquify public gateway THORChain endpoints

On mainnet the provider HTTP client never follows redirects and refuses private and reserved addresses, so provider URLs must be public.

Compose-only

Variable Purpose
POSTGRES_PASSWORD Database password; also embedded in COLDTILL_DATABASE_URL
COLDTILL_IMAGE Image to run (default coldtill:local)
COLDTILL_UID, COLDTILL_GID User the sweeper and payouts containers run as, so they can read secrets/
COLDTILL_CADDY_ADDRESS Site address for Caddy: a bare domain (pay.example.com) gets an automatic certificate. Comma-separate several names (pay.example.com, pay.shop.com) when projects have their own checkout domain
COLDTILL_HTTP_PORT, COLDTILL_HTTPS_PORT Host ports Caddy publishes (default 80 and 443)
COLDTILL_TRUSTED_PROXIES Space-separated ranges of proxies in front of Caddy whose client-IP header is believed (default: private ranges only)
COLDTILL_CLIENT_IP_HEADER Header those proxies put the visitor's IP in (default X-Forwarded-For)

Behind Cloudflare

With Cloudflare's proxy (orange cloud) in front, every request reaches the server from a Cloudflare address. Tell Caddy to believe Cloudflare's CF-Connecting-IP header, but only from Cloudflare's own ranges, so rate limits apply per visitor and the header can't be forged by connecting to the server directly:

cd deploy
echo "COLDTILL_TRUSTED_PROXIES=$( { curl -fsS https://www.cloudflare.com/ips-v4; echo; curl -fsS https://www.cloudflare.com/ips-v6; } | xargs)" >> .env
echo "COLDTILL_CLIENT_IP_HEADER=CF-Connecting-IP" >> .env
docker compose up -d caddy

Set Cloudflare's SSL/TLS mode to Full (strict). Cloudflare's ranges change rarely; re-run the first command if they do.

Per-project settings

Most behaviour is configured per project in the dashboard, not in the environment:

  • Branding: name, logo, colour, theme, checkout message, support and return links, payment expiry. See Branding.
  • Sweeps: activation minimum, sweep amount, address count and interval.
  • Payouts: minimum, auto-approve amount, hourly and daily limits. See Payouts.
  • Auto-convert: settlement asset, maximum slippage, THORChain fallback. See Auto-convert.

Per network (global admin): enabled, confirmations, nodes, and which tokens are enabled.

Checkout domains

A project can send customers to its own hostname, such as pay.shop.com, instead of the dashboard's address. A server admin sets it under Project settings → Checkout domain (two-factor required). Before saving:

  1. Point a DNS A record for the name at the server.
  2. Add the name to COLDTILL_CADDY_ADDRESS, comma separated, and run docker compose up -d caddy.

On a checkout domain Coldtill serves only /pay/, /api/checkout/, /api/v1/, static assets, branding images and /healthz. The login page, the dashboard and /api/dash answer 404 there, so the checkout name can't be used to phish admins. New payment links use the domain; links already sent keep working on the old address too.

For DNS, Cloudflare certificates and a worked example with several projects, see Guide: projects, domains and a private dashboard.