docs: MIT LICENSE (Such Software LLC) + README for v0.3.0

- LICENSE: MIT, © Such Software LLC (published under the company umbrella).
- Cargo.toml: license = "MIT".
- README: rewritten for the feature-complete release — non-custodial guarantee,
  chains, public API + operator surface, running it, security posture, testing.
This commit is contained in:
jwinterm
2026-06-28 08:38:57 -04:00
parent efb54911de
commit a7c144b11f
3 changed files with 128 additions and 35 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ edition = "2021"
authors = ["John Winter Murphy <jw@such.software>"] authors = ["John Winter Murphy <jw@such.software>"]
description = "Open, self-hostable backend for the Smirk non-custodial multi-chain wallet" description = "Open, self-hostable backend for the Smirk non-custodial multi-chain wallet"
repository = "https://github.com/smirk-cash/smirk-backend-core" repository = "https://github.com/smirk-cash/smirk-backend-core"
# license: TBD — deciding MIT vs AGPL-3.0-or-later (see README). Left unset until chosen. license = "MIT"
# Not a library for crates.io; this is a deployable server. Guard against accidental publish. # Not a library for crates.io; this is a deployable server. Guard against accidental publish.
publish = false publish = false
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Such Software LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+106 -34
View File
@@ -1,51 +1,123 @@
# smirk-backend-core # smirk-backend-core
Open, self-hostable backend for the [Smirk](https://smirk.cash) non-custodial, Open, self-hostable backend for the [Smirk](https://smirk.cash) non-custodial
multi-chain wallet. It provides the chain-access and identity surface a Smirk multi-chain wallet. Rust + Axum + PostgreSQL.
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 It gives a wallet chain access (balances, history, UTXOs, spend inputs, fee
> schema may change before the first tagged release. Not yet recommended for estimation, broadcast), Nostr-native identity, a fiat price feed, and an async
> production self-hosting. Grin slatepack relay — **without ever holding a spend key or seed**. Run your
own; the wallet is backend-agnostic.
## What it is > **Status:** v0.3.0 — feature-complete and security-reviewed, but young. The
> schema/API may still evolve. Run your own chain backends where you can, review
> the code, and treat early deployments accordingly.
- **Non-custodial.** The server never holds spend keys or seeds. It brokers ## Non-custodial by construction
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 The server stores **public addresses, public keys, and (for Monero/Wownero)
incoming view keys only** — never a spend key, never a seed. View credentials are
**forwarded per request** to the chain backends that scan with them and are not
persisted here. The wallet signs and broadcasts locally; the backend reads chains
and relays bytes.
Rust + [Axum](https://github.com/tokio-rs/axum), PostgreSQL via ## Chains
[sqlx](https://github.com/launchbadge/sqlx). The public HTTP contract is
generated from the handlers with [utoipa](https://github.com/juhaku/utoipa)
(OpenAPI), which in turn drives the wallet's generated TypeScript client —
one source of truth, checked for drift in CI.
## Quickstart | Chain | Source | Notes |
|-------|--------|-------|
| Bitcoin, Litecoin | Electrum / Fulcrum | reads, fee estimation, broadcast |
| Monero, Wownero | light-wallet-server (LWS) | stateless view-key forwarding |
| Grin | grin-wallet (view-only) + node | `rewind_hash` scan, broadcast, slatepack relay |
Each chain is independently feature-flagged; a chain whose source isn't configured
is reported `enabled: false` by `/capabilities` rather than failing at call time.
## API
The HTTP contract is generated from the handlers and committed to
[`openapi.json`](openapi.json) — the single source of truth for the API and the
wallet's generated client. A CI gate fails the build on any drift.
Public surface (`/api/v1`):
- **Identity & auth** — NIP-98 (Nostr) sign-in + link, wallet-signature auth,
audience-separated JWT sessions, NIP-05 directory (`/.well-known/nostr.json`).
- **Chain access** — per-chain balance / history / UTXOs / spend inputs / fee /
broadcast.
- **Grin relay** — non-custodial store-and-forward mailbox for interactive Grin
transfers (feature-flagged).
- **Capabilities** (`/capabilities`) — what this instance enables, so the wallet
adapts per-instance.
- **Prices** (`/prices`) — cached fiat feed, per-feed operator control.
- **Self-service erasure** (`/account/erasure`, `/account/export`) — action-bound,
two-phase delete-my-data + export.
- **Health** (`/health`) and an optional, default-off public landing (`/`).
## Operator surface
Operator/admin functions live on a **separate loopback listener** (front it with
Tor or an SSH tunnel) — confidentiality by socket, never merged into the public
router or OpenAPI.
- **Sign-in-with-Smirk admin auth** — a NIP-98 *signed action* over a single-use
nonce (AUTHN) + a MAC-protected key allowlist (AUTHZ), composed so a route can't
run without both.
- **First-run bootstrap** — an explicit, MAC-protected latch (no trust-on-first-
use); a live deployment adopts as already-bootstrapped; tampering fails closed.
- **`smirk-admin` CLI** — break-glass key management, `create-admin-wallet`
(zeroized), `doctor`. Talks to Postgres directly, bypassing the network plane.
- **Tamper-evident audit** — privileged actions are written to a hash-chained
audit trail, fail-closed (the state change rolls back if the audit write fails).
## Running it
Requires Rust (stable) and PostgreSQL.
```sh ```sh
cp .env.example .env # then fill in DATABASE_URL, JWT_SECRET, node URLs cp .env.example .env # then edit: DATABASE_URL + the required secrets
cargo run # starts the API server createdb smirk_backend_core # or point DATABASE_URL at an existing database
cargo run --release # migrations run automatically on startup
``` ```
A full self-hosting guide (node requirements, reverse proxy, Tor admin onion, The server **fails closed**: it refuses to start on a missing/weak secret or an
backups) will live in `docs/` as it stabilises. inconsistent feature configuration rather than booting with a control silently
defeated. Generate secrets with `openssl rand -hex 32`. See
[`.env.example`](.env.example) for the full, documented configuration surface.
## Security First admin (headless — the supported bootstrap):
Please report vulnerabilities privately — see [SECURITY.md](SECURITY.md). ```sh
Do not open public issues for security reports. smirk-admin create-admin-wallet --out admin.secret # generates a key, registers the pubkey
# import admin.secret into your NIP-98 signer; it activates on first admin login
```
## Security posture
- **Fail-closed configuration** — the only place that reads the environment;
validates secrets (length + placeholder checks) and feature consistency at boot.
- **Identity at rest is peppered** — `pubkey_hash` / `seed_fingerprint` are stored
as HMACs, so the database is not a seed-existence oracle or a cross-instance
linker.
- **Strict JWTs** — HS256 pinned, zero leeway, audience-separated; admin tokens
are cryptographically distinct from user tokens.
- **Bounded, hardened upstream I/O** — TLS with hostname verification, streaming
size caps, and per-request timeouts on every external call.
- **Adversarially reviewed** — every security-critical subsystem was built clean
(never ported) and put through a multi-agent adversarial review before landing.
Report vulnerabilities privately — see [SECURITY.md](SECURITY.md). Please do not
open public issues for security reports.
## Testing
```sh
cargo test # unit + doc tests (no database needed)
TEST_DATABASE_URL=postgres://… cargo test --tests # + L1 integration against Postgres
```
Integration tests self-skip when `TEST_DATABASE_URL` is unset. They use only
deterministic, non-sensitive test secrets and ephemeral identities — never a
funded or otherwise sensitive wallet seed.
## License ## License
To be finalized (MIT vs AGPL-3.0-or-later). Until a `LICENSE` file is added, [MIT](LICENSE) © Such Software LLC — run it, embed it, modify it. Built for interop.
all rights are reserved.