mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
First of the operator-configurable registration gates (PoW-priced restore and pay-to-register follow). A NEW wallet must present a valid invite code when REGISTRATION_REQUIRE_INVITE is on; returning wallets and self-hosting bypass it, and the active gates are advertised via /capabilities so the wallet shapes onboarding. - migration + db: invite_codes table storing only sha256(code) (128-bit codes, no pepper needed). Redemption is an atomic single-statement claim (UPDATE ... WHERE used_at IS NULL RETURNING) — single-use even under concurrent submission (L1 test fires 8 concurrent claims; exactly one wins). - core::invite: generate (base58, 128-bit) + sha256 hash. - enforce_invite in extension_register, mirroring enforce_pow (returning bypass; ONE literal error for not-found/used/expired — never an oracle). - RegistrationConfig.require_invite + RegistrationCapability in /capabilities. - smirk-admin mint-invite --count/--label (prints raw codes once; stores hashes). - L1 tests (single-use + concurrency race), .env.example, OpenAPI (drift clean).
124 lines
5.9 KiB
Bash
124 lines
5.9 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)
|
|
# 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=bounded
|
|
WALLET_MAX_RESTORE_DEPTH_DAYS=365
|
|
|
|
# ── 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
|