jwinterm 51a3371be1 feat(admin,landing): admin-plane hardening + public landing [operator §2]
Admin posture (§2.1/§2.2):
- Hard fail-closed bind guard in main: a non-loopback ADMIN_BIND refuses startup
  unless ADMIN_ALLOW_PUBLIC_BIND=true (was a warning).
- admin_plane_guard middleware on the admin router: Host allowlist
  (localhost/127.0.0.1/[::1]/configured TOR_ADMIN_ONION; a missing Host is allowed
  since rebinding must supply one and the bind is the primary control) to defeat
  DNS-rebinding against the loopback socket, plus X-Frame-Options: DENY +
  CSP frame-ancestors 'none' (anti-clickjacking). Still no CORS on the admin plane.

Public landing (§2.3), default-OFF, one fingerprint surface:
- GET /api/v1/server-info — built from a hand-written ServerInfo allowlist (never
  by serializing config/state). Default projection is minimal { software.name,
  status }; version (coarse major.minor), chains, price_feed, and uptime (bool
  `up` only) are each individually opt-in. The enabled-feature tuple is NEVER
  emitted anonymously (connected clients use /capabilities).
- GET / — minimal HTML (operator title HTML-escaped); bare 404 when off (does not
  hint an admin plane exists). server-info also 404s when off.
- config: ADMIN_ALLOW_PUBLIC_BIND, TOR_ADMIN_ONION; PUBLIC_LANDING_TITLE +
  per-field PUBLIC_EXPOSE_VERSION/CHAINS/PRICE_FEED toggles.

Tests: host_allowlist + html_escape unit tests; landing off->on minimal-projection
(asserts only software+status, no version/chains/features); admin-plane Host-403 +
anti-clickjacking headers. Landing/admin stay off the public OpenAPI.

Deferred (documented): §2.5 NIP-05 consent-gating (changes existing
/.well-known/nostr.json behavior — needs the standing v0.2.4-compat verification
first), §2.3 k-anon public stats, response-header/TLS-fingerprint stripping (proxy
/ops-doc concern).
2026-06-27 15:32:11 -04:00

smirk-backend-core

Open, self-hostable backend for the Smirk non-custodial, multi-chain wallet. It provides the chain-access and identity surface a Smirk wallet needs — authentication, per-chain wallet access (Bitcoin, Litecoin, Monero, Wownero, Grin), the Grin slatepack relay, and Nostr-based identity — so anyone can run their own backend and a Smirk wallet can connect to it.

Status: early, under active development. Interfaces and the database schema may change before the first tagged release. Not yet recommended for production self-hosting.

What it is

  • Non-custodial. The server never holds spend keys or seeds. It brokers access to blockchain data and relays wallet-to-wallet messages.
  • Open core, feature-flagged. Every feature ships in this codebase and is toggled per deployment; the wallet reads GET /api/v1/capabilities and adapts to what a given backend offers.
  • Identity via "Sign in with Smirk". Authentication uses a seed-derived Nostr key (NIP-98); the server stores only public keys.
  • Privacy first. No PII is required to use it; operator and user privacy are explicit design goals.

Architecture

Rust + Axum, PostgreSQL via sqlx. The public HTTP contract is generated from the handlers with utoipa (OpenAPI), which in turn drives the wallet's generated TypeScript client — one source of truth, checked for drift in CI.

Quickstart

cp .env.example .env        # then fill in DATABASE_URL, JWT_SECRET, node URLs
cargo run                   # starts the API server

A full self-hosting guide (node requirements, reverse proxy, Tor admin onion, backups) will live in docs/ as it stabilises.

Security

Please report vulnerabilities privately — see SECURITY.md. Do not open public issues for security reports.

License

To be finalized (MIT vs AGPL-3.0-or-later). Until a LICENSE file is added, all rights are reserved.

S
Description
Mirror of github.com/Such-Software/smirk-backend-core
Readme MIT
2.1 MiB
Languages
Rust 97.6%
TypeScript 1.4%
Shell 0.6%
PLpgSQL 0.3%