Files
smirk-backend-core/.env.example
T
jwinterm 56238dcac8 feat(restore): default create-only + PoW-priced restore depth (gate #2)
Default restore policy is now create-only — which betrays no self-sovereignty,
since that lives at the ECOSYSTEM level (run your own backend). On top of the
create-only|bounded|unlimited depth gate, restore depth can be PRICED in hashcash
PoW so an importer pays compute proportional to the chain-scan cost they impose
on the operator (deeper restore => more work).

- core::restore_pow: a self-contained hashcash bound to (asset, address,
  start_height) — a solved nonce is valid only for that exact restore, so no
  challenge state, no replay, no depth-downgrade. Difficulty = leading zero bits.
- RestoreConfig: pow_free_days / pow_days_per_bit (0 = off) / pow_max_bits curve;
  required_restore_pow_bits + enforce_restore_pow (literal errors, no oracle).
- Enforced at xmr/wow register + grin scan via a new restore_pow_nonce; the curve
  is advertised via /capabilities so the wallet computes + solves it.
- Default WALLET_RESTORE_POLICY flipped bounded -> create-only.
- Unit tests (curve scaling/cap, off-when-zero, valid/invalid nonce, hashcash
  roundtrip + param-binding); .env.example; OpenAPI drift clean.
2026-06-30 18:35:20 -04:00

132 lines
6.4 KiB
Bash

# smirk-backend-core — example environment.
#
# Copy to `.env` and fill in for your deployment. NEVER commit a real `.env`.
# Generate secrets with: openssl rand -hex 32
# ── Server ────────────────────────────────────────────────────────────────
SERVER_HOST=0.0.0.0
SERVER_PORT=8080
# Public absolute API base URL. REQUIRED when admin/erasure features are enabled:
# it is the canonical value the NIP-98 `u` tag is verified against (never the
# request Host header). Boot fails if unset while those features are on.
PUBLIC_API_URL=https://backend.example.org/api/v1
ENVIRONMENT=development
# Stable per-node identifier. Bound into signed admin/erasure actions so a proof
# captured against one node cannot be replayed across a load-balanced fleet.
INSTANCE_ID=
# ── Database ──────────────────────────────────────────────────────────────
DATABASE_URL=postgres://user:password@127.0.0.1:5432/smirk
# ── Auth (JWT) ────────────────────────────────────────────────────────────
JWT_SECRET=
JWT_EXPIRY_HOURS=24
# ── Proof-of-work signup gate (ALTCHA v2) ─────────────────────────────────
ALTCHA_HMAC_KEY=
POW_REQUIRED=false
ALTCHA_COST=100000
# ── Registration gates (composable; each advertised via /capabilities) ────
# Require an operator-minted invite code to create a NEW wallet. Mint codes with
# `smirk-admin mint-invite --count <n>`. Returning wallets bypass it, and
# self-hosting bypasses all gates (run your own backend). PoW is the ALTCHA gate
# above; pay-to-register is planned. Compose freely (e.g. invite + PoW).
REGISTRATION_REQUIRE_INVITE=false
# ── Bitcoin ───────────────────────────────────────────────────────────────
BTC_NETWORK=mainnet
BTC_USE_LOCAL_NODE=false
BTC_RPC_URL=http://127.0.0.1:8332
BTC_RPC_USER=bitcoinrpc
BTC_RPC_PASS=
BTC_ELECTRUM_URL=
BTC_ELECTRUM_FALLBACKS=
# ── Litecoin ──────────────────────────────────────────────────────────────
LTC_NETWORK=mainnet
LTC_USE_LOCAL_NODE=false
LTC_RPC_URL=http://127.0.0.1:9332
LTC_RPC_USER=litecoinrpc
LTC_RPC_PASS=
LTC_ELECTRUM_URL=
LTC_ELECTRUM_FALLBACKS=
# ── Monero (daemon + light wallet server) ─────────────────────────────────
XMR_LWS_URL=http://127.0.0.1:8443
XMR_LWS_ADMIN_URL=http://127.0.0.1:9443
XMR_LWS_ADMIN_KEY=
XMR_DAEMON_URL=http://127.0.0.1:18081
# ── Wownero (daemon + light wallet server) ────────────────────────────────
WOW_LWS_URL=http://127.0.0.1:18443
WOW_LWS_ADMIN_URL=http://127.0.0.1:19443
WOW_LWS_ADMIN_KEY=
WOW_DAEMON_URL=http://127.0.0.1:34568
# ── Grin (wallet owner/foreign API + node API) ────────────────────────────
GRIN_OWNER_API_URL=http://127.0.0.1:3420/v3/owner
GRIN_OWNER_API_SECRET=
GRIN_WALLET_PASSWORD=
GRIN_FOREIGN_API_URL=http://127.0.0.1:3415/v2/foreign
GRIN_NODE_API_URL=http://127.0.0.1:3413/v2/owner
GRIN_NODE_API_USER=grin
GRIN_NODE_API_PASS=
GRIN_NODE_FOREIGN_API_URL=http://127.0.0.1:3413/v2/foreign
GRIN_NODE_FOREIGN_API_SECRET=
# ── Wallet restore (import) policy ────────────────────────────────────────
# How far back a wallet may ask THIS instance to scan on import. The backfill
# cost lands on your LWS/node, so it is your call; it is advertised via
# /capabilities so the wallet adapts its import UX. Self-sovereignty is
# preserved — a user needing a deeper restore runs their own instance or picks
# an operator with a looser policy.
# create-only — new wallets only (scan starts at the tip). DEFAULT — and it
# betrays nothing: self-sovereignty is run-your-own-backend.
# bounded — restore up to WALLET_MAX_RESTORE_DEPTH_DAYS behind the tip
# unlimited — any restore height (the natural choice for self-hosting)
WALLET_RESTORE_POLICY=create-only
WALLET_MAX_RESTORE_DEPTH_DAYS=365
# PoW-priced restore: charge the importer compute proportional to the scan depth
# they impose on you. Free below WALLET_RESTORE_POW_FREE_DAYS, then +1 hashcash
# difficulty bit per WALLET_RESTORE_POW_DAYS_PER_BIT days of depth (0 = pricing
# OFF), capped at WALLET_RESTORE_POW_MAX_BITS. Applies within bounded/unlimited.
WALLET_RESTORE_POW_FREE_DAYS=90
WALLET_RESTORE_POW_DAYS_PER_BIT=0
WALLET_RESTORE_POW_MAX_BITS=24
# ── Feature flags (each feature ships in the open core, toggled per deployment) ──
# The wallet client reads GET /api/v1/capabilities and greys out disabled features.
FEATURE_PRICES=true
PRICES_PROVIDER=coingecko
PRICES_FETCH_INTERVAL_SECS=300
# Tips are parked for the conditional-transfer redesign; off until that lands.
FEATURE_TIPS=false
# ── Admin surface (see docs; default posture is loopback/Tor) ──────────────
# Admin auth is "Sign in with Smirk": only admin PUBLIC keys are stored here,
# never a seed or private key. The allowlist is mutated CLI/loopback-only in v1.
ADMIN_ENABLED=false
ADMIN_BIND=127.0.0.1:8081
ADMIN_JWT_SECRET=
# Integrity MAC secret protecting admin/setup trust anchors against DB tampering.
# Losing this locks out admin — back it up (see docs/operations).
ADMIN_KEY_INTEGRITY_SECRET=
ADMIN_MAX_KEYS=8
ADMIN_PENDING_KEY_TTL_DAYS=7
# ── Public landing page (OFF by default; full + per-field tunable when on) ──
PUBLIC_LANDING_ENABLED=false
PUBLIC_EXPOSE_FEATURES=false
PUBLIC_EXPOSE_UPTIME=false
PUBLIC_STATS_ENABLED=false
PUBLIC_STATS_CACHE_HOURS=24
# ── Data retention / erasure ──────────────────────────────────────────────
ERASURE_ENABLED=false
ERASURE_PURGE_LOGIN_EVENTS=true
ERASURE_EXPORT_PER_DAY=3
# Log retention (auto-deletion); 0 = keep indefinitely.
RETENTION_LOGIN_EVENTS_DAYS=90
RETENTION_AUDIT_DAYS=365