Commit Graph
7 Commits
Author SHA1 Message Date
jwinterm e01f894c82 chore: remove legacy platform name from session test (de-legacy soft=0) 2026-06-22 14:11:09 -04:00
jwinterm 5d70b80aa9 feat: db layer foundation + Nostr-native user queries
- core/crypto/pepper.rs: domain-separated HMAC peppering of identity values at
  rest (kills the seed-existence oracle: stored pubkey_hash/seed_fingerprint are
  non-reproducible from a candidate seed without the server pepper)
- infra/db/mod.rs: Database wrapper holding the pool + peppers (no Debug derive);
  peppering applied at the data boundary so it cannot drift between call sites
- infra/db/users.rs: Nostr-native query set (create/get/update, by pubkey_hash
  /seed_fingerprint [peppered], by username/nostr_pubkey, get-or-create with
  NULL-only backfill, username + nostr_pubkey UNIQUE -> 409). Explicit columns
  (no SELECT *). Dropped all telegram/discord methods.
2026-06-22 14:09:21 -04:00
jwinterm e182972599 feat: session/JWT + PoW gate (config-driven, strict)
core/session.rs:
- strict JWT Validation: HS256 pinned, exp/sub/aud required, leeway 0
- distinct audiences for access vs refresh so neither is accepted as the other
- Platform -> Extension|Web|Nostr (dropped telegram/discord + platform_user_id)
- refresh-token hash is HMAC-peppered (DB compromise is useless without pepper)
- verify_* return literal error messages (no jsonwebtoken Display oracle)

core/pow.rs: ALTCHA gate driven by config::PowConfig (feature-gated, real HMAC
key required, no fallback); issue/verify/required_for + replay/expiry tests.
2026-06-22 13:57:28 -04:00
jwinterm 9ececb03af feat: NIP-98 auth + wallet-signature verification (hardened)
core/crypto/nip98.rs:
- verify_nip98 (login-grade): binds url+method+freshness
- verify_signed_action (state-change): adds single-use nonce + purpose +
  canonical request-descriptor hash + optional target-id/instance-id, each an
  exactly-one tag, constant-time compared, all checked on the signature-
  authenticated event (id recomputed + schnorr-verified before any tag trusted)
- hardening from adversarial review: length-guard pubkey/id/sig before k256
  construction (no panic on short input), saturating freshness window (no
  overflow wrap); regressions for all three

core/crypto/signatures.rs: BIP-137 ECDSA + Ed25519 verification returning
Result<(),AppError> (Ok only on valid; malformed->400, mismatch->401) — closes
the Ok(false) fail-open.

Cargo.toml: overflow-checks=true in release (defense-in-depth for auth paths).
2026-06-22 13:47:20 -04:00
jwinterm 416fa53c17 feat: Nostr-native data models + initial schema
models/db.rs: User keyed by pubkey_hash/nostr_pubkey/username (pubkey_hash and
seed_fingerprint peppered at rest); Wallet, Session (platform=extension|web|nostr),
UserKey, GrinSlatepack, AuditLog. Dropped all telegram/discord/social-identity
columns and every tip/social/swap struct (swaps land as their own feature unit).

migrations/20260622000001_initial_schema: consolidated clean schema; enum types;
UNIQUE on identity columns; FK ON DELETE (CASCADE/SET NULL) for self-service
erasure; indexes on every lookup column. PostgreSQL 13+.
2026-06-22 07:10:55 -04:00
jwinterm f958aaf0f7 feat: config (fail-closed) + error handling
config.rs (keystone, audited):
- single env reader; validate() FAILS CLOSED on weak/placeholder/missing secrets
  (JWT_SECRET >=32 + non-placeholder, required peppers, PUBLIC_API_URL required
  + url-validated + https-in-prod when Nostr identity on, PoW/admin secrets when
  those features enabled, per-enabled-chain infra secrets hard-fail in prod)
- Nostr-native + feature-flagged; NO telegram/discord/trocador; no dev-fallback keys
- typed sections: auth, identity, secrets(peppers), trusted_proxies, features,
  chains, pow, admin, landing, retention; deployment_mode single|fleet
- secret-bearing structs do not derive Debug; 7 fail-closed validation tests

error.rs: CWE-209 LEAKY/SAFE split (log-private + generic public), error_code
mapping, 5 regression tests.
2026-06-22 07:00:09 -04:00
jwinterm 143f00701b chore: initial foundation — server skeleton, autodocs, CI
- minimal Axum server exposing GET /health
- OpenAPI contract generated from handlers (gen_openapi -> openapi.json),
  drift-gated in CI so the spec can never silently diverge from the code
- CI: secret-scan + gitleaks, openapi drift gate, fmt/clippy/build/test/doc-test, cargo-deny
- Cargo.lock tracked for reproducible builds; publish=false; pinned toolchain
- README, SECURITY.md, deny.toml, .env.example
2026-06-22 06:18:57 -04:00