mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 07:21:32 +02:00
docs(examples): operator cookbook + premium-relay spec
examples/: an operator "cookbook" turning the backend's composable knobs (registration gates, relay write policy, restore policy, feature flags) into ready-to-run recipes per persona — personal, public-free (what the reference instance runs), friends, paid-access, community, and paid-relay (the flagship: free wallet + paid premium Nostr posting). Plus LEGAL_AND_OPS.md, a not-legal-advice considerations checklist that points operators at their own counsel and at the project's own published docs as a worked example. Recipes use placeholders only. docs/design/premium-relay.md: the premium-relay feature spec (reconciled to the shipped design — dedicated premium_invoices table, transactional activate).
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# Legal & operational considerations (per recipe)
|
||||
|
||||
> **This is not legal advice.** It's a plain-English checklist of what changes,
|
||||
> legally and operationally, as you pick a heavier recipe — so you know what to take
|
||||
> to your own lawyer. Smirk's own published [Terms](https://smirk.cash/terms) +
|
||||
> [Privacy Policy](https://smirk.cash/privacy) (source in the `smirk-website` repo)
|
||||
> are a useful worked example for a non-custodial, multi-chain, Nostr-enabled wallet
|
||||
> backend — but they are **ours**. Adapt with your own counsel; don't copy blind.
|
||||
|
||||
## The through-line: stay non-custodial
|
||||
|
||||
Every recipe keeps the backend **non-custodial** — you never hold seeds, spend keys,
|
||||
or user funds. That single property is what keeps you out of money-transmitter /
|
||||
custodial-exchange territory. Preserve it in anything you add.
|
||||
|
||||
## By recipe
|
||||
|
||||
**`personal` / `friends`** — lowest surface; you're running infrastructure for
|
||||
yourself or a known group. If you enable the relay, you're carrying (encrypted)
|
||||
messages — see *Running a relay*.
|
||||
|
||||
**`public-free`** — open to the public, so have a Terms + Privacy that disclose what
|
||||
your backend actually receives: Monero/Wownero **view keys** (for LWS scanning),
|
||||
**IP handling** (hash + purpose), the wallet **fingerprint**, and **Nostr identity**.
|
||||
No money changes hands → no payment/consumer-sale angle.
|
||||
|
||||
**`paid-access` / `paid-relay` (you charge a fee)**
|
||||
- You're selling access to **your** service (a fee), not transmitting user funds → you
|
||||
act as a **merchant**, not a money transmitter. Keep it non-custodial and
|
||||
**pull-model** (read invoice status; never hold or route user funds).
|
||||
- Terms should state: what the fee buys, **no refunds** (prepaid access), the service
|
||||
is best-effort and may change, and **fair-use limits** (bandwidth / storage / content)
|
||||
for relay posting.
|
||||
- Treat fees as **business income** (taxes). Confirm your jurisdiction + entity, and
|
||||
have counsel bless the non-custodial → not-an-MSB analysis for *you*.
|
||||
|
||||
**Running a relay (`friends` / `paid-relay` / `community`)** — you're hosting user
|
||||
content, which is intermediary-liability territory:
|
||||
- **Copyright (DMCA):** consider registering a **Designated Agent** with the U.S.
|
||||
Copyright Office and running a notice / counter-notice process with a named contact.
|
||||
- **CSAM:** U.S. providers hosting content have mandatory reporting duties (NCMEC /
|
||||
18 U.S.C. §2258A) — register and have a process. Note that **end-to-end-encrypted**
|
||||
DMs you cannot read are handled differently from plaintext you can.
|
||||
- **Acceptable use + moderation rights:** prohibit unlawful content; reserve the right
|
||||
to rate-limit, refuse, or remove; state that you are **not the author** of user
|
||||
content and don't guarantee delivery/retention.
|
||||
- Smirk's Terms **§6** (reporting, DMCA, acceptable use) and **§9.1** (relay) are a
|
||||
worked example of this posture.
|
||||
|
||||
## Again: a reference, not a template
|
||||
|
||||
Use Smirk's live docs to see *how one operator did it* — then write your own with a
|
||||
lawyer. Requirements vary by country, entity, and exactly what you enable.
|
||||
@@ -0,0 +1,68 @@
|
||||
# Operator cookbook
|
||||
|
||||
`smirk-backend-core` is one binary with **composable knobs** — registration gates
|
||||
(PoW / invite / pay-to-register), an optional Nostr relay with a configurable write
|
||||
policy, restore policy, and per-feature flags. This directory turns those knobs into
|
||||
**ready-to-run recipes**, one per operator persona.
|
||||
|
||||
Each recipe is a directory with:
|
||||
- **`.env.example`** — the tuned config (only the lines that differ from defaults are
|
||||
called out; copy the full `../../.env.example` and apply the deltas).
|
||||
- **`README.md`** — what it's for, what it costs to run, the legal/operational notes.
|
||||
|
||||
> **Status.** `personal/`, `public-free/`, and `paid-relay/` (the flagship — it
|
||||
> uses the shipped [premium-relay feature](../docs/design/premium-relay.md)) have
|
||||
> full `.env.example`s. `friends/`, `paid-access/`, and `community/` are summarized
|
||||
> below and get their own directories on the next pass.
|
||||
|
||||
## Every recipe, the same three steps
|
||||
1. `cp ../../.env.example .env`, then apply the recipe's deltas.
|
||||
2. Generate each secret: `openssl rand -hex 32` (`JWT_SECRET`, `SEED_FINGERPRINT_PEPPER`,
|
||||
`REFRESH_TOKEN_PEPPER`, `IP_SALT`, `ALTCHA_HMAC_KEY`, `ADMIN_*_SECRET`). Fill chain
|
||||
endpoints. `chmod 600 .env`.
|
||||
3. Run the binary (migrations apply on boot). Reverse-proxy TLS; bind the API to
|
||||
`127.0.0.1`.
|
||||
|
||||
**Non-custodial in every recipe** — the backend never holds seeds, spend keys, or
|
||||
funds. **Self-hosting bypasses every gate.** Secrets are never committed.
|
||||
|
||||
## The menu
|
||||
|
||||
| Recipe | For | Registration | Relay | Pitch | Status |
|
||||
|---|---|---|---|---|---|
|
||||
| [`personal/`](personal/) | Just you | open, no gate | off (or open, just you) | Your own sovereign backend | ✅ boots today |
|
||||
| [`public-free/`](public-free/) | A free public instance | **open + PoW** | off (v1) | What `api.smirk.cash` runs | ✅ boots today |
|
||||
| `friends/` | Your circle | **invite-only** | on, `inbox-outbox` | A backend for people you trust | 📝 2nd pass |
|
||||
| `paid-access/` | A paid wallet service | **pay-to-register** | optional | Charge once for a hosted wallet backend | 📝 2nd pass |
|
||||
| `paid-relay/` | Monetize a relay | **open + PoW** (free wallet) | on, **`premium-post`** | Free wallet, paid ($/quarter) Nostr posting | ✅ premium-relay shipped |
|
||||
| `community/` | A project/DAO space | invite | on, `author-allowlist` | A gated relay for a named group | 📝 2nd pass |
|
||||
|
||||
## Recipe summaries (config deltas)
|
||||
|
||||
- **`personal`** — `POW_REQUIRED=false`, all registration gates off,
|
||||
`WALLET_RESTORE_POLICY=unlimited`, `RELAY_ENABLED=false` (or on, just for you),
|
||||
`ADMIN_ENABLED=false`. Minimal footprint; you are the only user.
|
||||
- **`public-free`** — `POW_REQUIRED=true`, gates off, `WALLET_RESTORE_POLICY=bounded`
|
||||
+ PoW-priced, `FEATURE_PRICES=true`, `FEATURE_TIPS=false`, `RELAY_ENABLED=false` at
|
||||
launch. Open to all, spam-gated by compute.
|
||||
- **`friends`** — `REGISTRATION_REQUIRE_INVITE=true` (mint with
|
||||
`smirk-admin mint-invite`), `RELAY_ENABLED=true` + `RELAY_WRITE_POLICY=inbox-outbox`,
|
||||
`WALLET_RESTORE_POLICY=unlimited`. Hand out codes; run a locked-down relay for the group.
|
||||
- **`paid-access`** — `REGISTRATION_REQUIRE_PAYMENT=true` + `PAYMENT_*` (a BTCPay-compatible
|
||||
processor paying *your* wallet). A one-time access fee gates wallet creation. Non-custodial;
|
||||
you never touch funds (pull-model invoice reads only).
|
||||
- **`paid-relay`** — free wallet (open + PoW) **plus** `PREMIUM_ENABLED=true`,
|
||||
`RELAY_ENABLED=true`, `RELAY_WRITE_POLICY=premium-post`, `PREMIUM_AMOUNT` /
|
||||
`PREMIUM_CURRENCY` / `PREMIUM_PERIOD_DAYS`. Recurring revenue for relay posting; reads
|
||||
open (v1). **Content posture:** running a relay carries user content — see the NCMEC/DMCA
|
||||
clauses in the published Terms (§6, §9.1). Spec: [premium-relay](../docs/design/premium-relay.md).
|
||||
- **`community`** — `REGISTRATION_REQUIRE_INVITE=true`, `RELAY_ENABLED=true` +
|
||||
`RELAY_WRITE_POLICY=author-allowlist` (only your registered members publish).
|
||||
|
||||
## Choosing
|
||||
|
||||
- Want **freedom**? `personal` (or `friends` for a group).
|
||||
- Want a **free public good**? `public-free`.
|
||||
- Want it to **pay for itself**? `paid-relay` (recurring, real utility) or `paid-access`
|
||||
(one-time). `paid-relay` is the flagship — recurring revenue tied to a service people
|
||||
actually value, and the live demo runs at `api.smirk.cash`.
|
||||
@@ -0,0 +1,79 @@
|
||||
# ── Recipe: paid-relay ───────────────────────────────────────────────────────
|
||||
# Free wallet (open + PoW) PLUS a paid premium tier for GENERAL Nostr posting to
|
||||
# your relay. Wallet-functional events (encrypted DMs, tips, swap coordination)
|
||||
# stay FREE for registered users; premium unlocks general posting. This is the
|
||||
# flagship monetization recipe — recurring revenue tied to a real service.
|
||||
#
|
||||
# ⚠️ Running a relay means hosting user content — read ../LEGAL_AND_OPS.md
|
||||
# (DMCA designated agent + NCMEC reporting + acceptable-use + moderation).
|
||||
#
|
||||
# Start from ../../.env.example and apply the lines below. openssl rand -hex 32
|
||||
# for each secret.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
SERVER_HOST=127.0.0.1
|
||||
SERVER_PORT=8081
|
||||
PUBLIC_API_URL=https://api.your-domain.example/api/v1
|
||||
ENVIRONMENT=production
|
||||
INSTANCE_ID=paid-relay-1
|
||||
|
||||
DATABASE_URL=postgresql:///smirk_v3_db?host=/var/run/postgresql
|
||||
|
||||
# Secrets (REQUIRED; openssl rand -hex 32 each).
|
||||
JWT_SECRET=
|
||||
SEED_FINGERPRINT_PEPPER=
|
||||
REFRESH_TOKEN_PEPPER=
|
||||
IP_SALT=
|
||||
ALTCHA_HMAC_KEY=
|
||||
ADMIN_KEY_INTEGRITY_SECRET=
|
||||
|
||||
# ── Free registration, PoW-gated (wallet use is always free) ──
|
||||
POW_REQUIRED=true
|
||||
REGISTRATION_REQUIRE_INVITE=false
|
||||
REGISTRATION_REQUIRE_PAYMENT=false
|
||||
|
||||
# ── Relay ON, premium-post policy ──
|
||||
RELAY_ENABLED=true
|
||||
RELAY_MODE=bundled
|
||||
RELAY_URL=wss://relay.your-domain.example
|
||||
RELAY_WRITE_POLICY=premium-post
|
||||
# Blunt cross-ecosystem DM spam (inbound gift-wraps from non-registered authors).
|
||||
RELAY_INBOUND_POW_BITS=8
|
||||
RELAY_ADMISSION_BIND=127.0.0.1:8090
|
||||
# Shared-box resource governance — the relay runs beside the wallet backend + LWS.
|
||||
# Also cap connections / subscriptions / ingest rate in the nostr-rs-relay config
|
||||
# (see deploy/relay); move to RELAY_MODE=external on its own box if load climbs.
|
||||
RELAY_MAX_EVENT_BYTES=65536
|
||||
RELAY_RETENTION_DAYS=30
|
||||
|
||||
# ── Premium tier: tiered discount plans (id:days:amount), reusing the processor ──
|
||||
PREMIUM_ENABLED=true
|
||||
# Price currency: a fiat code the processor converts (so "5" = $5, paid in any coin
|
||||
# your processor offers), or a specific coin (then amounts are in that coin).
|
||||
PREMIUM_CURRENCY=USD
|
||||
PREMIUM_PLANS=quarter:90:5,halfyear:182:9,year:365:15
|
||||
|
||||
# Payment processor (BTCPay-compatible; also serves xmrcheckout/wowcheckout). The
|
||||
# backend only READS invoice status — it never holds funds. Invoices are paid
|
||||
# directly to YOUR wallet.
|
||||
PAYMENT_PROVIDER=btcpay
|
||||
PAYMENT_PROVIDER_URL=https://pay.your-domain.example
|
||||
PAYMENT_STORE_ID=your-store-id
|
||||
PAYMENT_API_KEY=your-processor-api-key
|
||||
PAYMENT_CONFIRMATIONS=1
|
||||
PAYMENT_EXPIRES_MINUTES=60
|
||||
|
||||
# Prices on; tips parked; admin plane optional.
|
||||
FEATURE_PRICES=true
|
||||
FEATURE_TIPS=false
|
||||
ADMIN_ENABLED=false
|
||||
|
||||
# Chain endpoints — see ../../.env.example (XMR/WOW LWS + daemon, GRIN_*, electrum).
|
||||
XMR_LWS_URL=
|
||||
XMR_LWS_ADMIN_URL=
|
||||
XMR_LWS_ADMIN_KEY=
|
||||
XMR_DAEMON_URL=
|
||||
WOW_LWS_URL=
|
||||
WOW_LWS_ADMIN_URL=
|
||||
WOW_LWS_ADMIN_KEY=
|
||||
WOW_DAEMON_URL=
|
||||
@@ -0,0 +1,56 @@
|
||||
# ── Recipe: personal ────────────────────────────────────────────────────────
|
||||
# Your own sovereign backend — one user (you), no gates, your rules.
|
||||
#
|
||||
# Start from ../../.env.example, then apply the lines below (they are what make
|
||||
# this recipe "personal"). Generate each secret with: openssl rand -hex 32
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
SERVER_HOST=127.0.0.1
|
||||
SERVER_PORT=8080
|
||||
PUBLIC_API_URL=https://your-domain.example/api/v1
|
||||
ENVIRONMENT=production
|
||||
INSTANCE_ID=my-personal-node
|
||||
|
||||
# Unix socket + peer auth (run the service as the DB-owning OS user) — no password.
|
||||
DATABASE_URL=postgresql:///smirk?host=/var/run/postgresql
|
||||
|
||||
# Secrets (REQUIRED; openssl rand -hex 32 each).
|
||||
JWT_SECRET=
|
||||
SEED_FINGERPRINT_PEPPER=
|
||||
REFRESH_TOKEN_PEPPER=
|
||||
IP_SALT=
|
||||
ALTCHA_HMAC_KEY=
|
||||
|
||||
# No gates — it's just you.
|
||||
POW_REQUIRED=false
|
||||
REGISTRATION_REQUIRE_INVITE=false
|
||||
REGISTRATION_REQUIRE_PAYMENT=false
|
||||
|
||||
# Your node, your rules — scan as far back as you like on import.
|
||||
WALLET_RESTORE_POLICY=unlimited
|
||||
|
||||
# Prices on; tips parked (v0.3 redesign).
|
||||
FEATURE_PRICES=true
|
||||
FEATURE_TIPS=false
|
||||
|
||||
# Relay off — one person doesn't need a public inbox. Flip RELAY_ENABLED=true
|
||||
# (with RELAY_URL + RELAY_WRITE_POLICY=author-allowlist) if you want your own
|
||||
# DM relay that only you can publish to.
|
||||
RELAY_ENABLED=false
|
||||
|
||||
# Admin HTTP plane off — the smirk-admin CLI (break-glass) is enough solo. Keep
|
||||
# ADMIN_KEY_INTEGRITY_SECRET set so the CLI works.
|
||||
ADMIN_ENABLED=false
|
||||
ADMIN_KEY_INTEGRITY_SECRET=
|
||||
|
||||
# Chain endpoints — point at YOUR nodes / light-wallet servers. See the full list
|
||||
# and formats in ../../.env.example (XMR/WOW LWS + daemon, GRIN_*, BTC/LTC electrum).
|
||||
XMR_LWS_URL=
|
||||
XMR_LWS_ADMIN_URL=
|
||||
XMR_LWS_ADMIN_KEY=
|
||||
XMR_DAEMON_URL=
|
||||
WOW_LWS_URL=
|
||||
WOW_LWS_ADMIN_URL=
|
||||
WOW_LWS_ADMIN_KEY=
|
||||
WOW_DAEMON_URL=
|
||||
# BTC_ELECTRUM_FALLBACKS=..., LTC_ELECTRUM_FALLBACKS=..., GRIN_* — see root example.
|
||||
@@ -0,0 +1,57 @@
|
||||
# ── Recipe: public-free ──────────────────────────────────────────────────────
|
||||
# A free, open public instance — anyone can register and use the wallet; spam is
|
||||
# gated by proof-of-work, not payment. This is (minus secrets) what the reference
|
||||
# instance api.smirk.cash runs.
|
||||
#
|
||||
# Start from ../../.env.example and apply the lines below. openssl rand -hex 32
|
||||
# for each secret.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
SERVER_HOST=127.0.0.1
|
||||
SERVER_PORT=8081
|
||||
PUBLIC_API_URL=https://api.your-domain.example/api/v1
|
||||
ENVIRONMENT=production
|
||||
INSTANCE_ID=public-1
|
||||
|
||||
DATABASE_URL=postgresql:///smirk_v3_db?host=/var/run/postgresql
|
||||
|
||||
# Secrets (REQUIRED; openssl rand -hex 32 each).
|
||||
JWT_SECRET=
|
||||
SEED_FINGERPRINT_PEPPER=
|
||||
REFRESH_TOKEN_PEPPER=
|
||||
IP_SALT=
|
||||
ALTCHA_HMAC_KEY=
|
||||
ADMIN_KEY_INTEGRITY_SECRET=
|
||||
|
||||
# Free registration, spam-gated by compute (ALTCHA proof-of-work).
|
||||
POW_REQUIRED=true
|
||||
REGISTRATION_REQUIRE_INVITE=false
|
||||
REGISTRATION_REQUIRE_PAYMENT=false
|
||||
|
||||
# Bounded restore, PoW-priced by depth (protects your LWS/node from deep backfills;
|
||||
# a user needing more runs their own instance).
|
||||
WALLET_RESTORE_POLICY=bounded
|
||||
WALLET_MAX_RESTORE_DEPTH_DAYS=365
|
||||
WALLET_RESTORE_POW_FREE_DAYS=90
|
||||
WALLET_RESTORE_POW_DAYS_PER_BIT=30
|
||||
WALLET_RESTORE_POW_MAX_BITS=24
|
||||
|
||||
FEATURE_PRICES=true
|
||||
FEATURE_TIPS=false
|
||||
|
||||
# Relay off at launch. To run it, see the `friends` (locked-down) or `paid-relay`
|
||||
# (monetized) recipes.
|
||||
RELAY_ENABLED=false
|
||||
|
||||
# Admin HTTP plane off at launch; enable + bootstrap a key when you need it.
|
||||
ADMIN_ENABLED=false
|
||||
|
||||
# Chain endpoints — reuse your existing daemons / LWS. See ../../.env.example.
|
||||
XMR_LWS_URL=
|
||||
XMR_LWS_ADMIN_URL=
|
||||
XMR_LWS_ADMIN_KEY=
|
||||
XMR_DAEMON_URL=
|
||||
WOW_LWS_URL=
|
||||
WOW_LWS_ADMIN_URL=
|
||||
WOW_LWS_ADMIN_KEY=
|
||||
WOW_DAEMON_URL=
|
||||
Reference in New Issue
Block a user