Files
helexa/helexa-upstream.example.toml
rob thijssen d94c62c143
All checks were successful
CI / Format (push) Successful in 44s
CI / CUDA type-check (push) Successful in 1m40s
CI / Clippy (push) Successful in 3m14s
CI / Test (push) Successful in 6m29s
CI / Build cortex SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
feat(B4): /web/v1 account API + silent fingerprint multi-account abuse
The human-facing account surface the helexa.ai frontend (F4) consumes,
on top of B2's authz surface. Email+password auth with JWT sessions
(distinct from inference API keys); plain-JSON errors (the #63 envelope
stays on the authz surface).

- Auth lifecycle: register → email-verify → login → password-reset
  (request/confirm). argon2id passwords; verify/reset via single-use
  sha256-hashed email tokens; register and reset-request always return
  202 (no account enumeration). Email via a pluggable EmailSender (lettre
  Smtp + dev Log transport).
- API keys: create (sk-helexa-<base62(32 OsRng)>, raw shown once, stored
  as sha256 + non-secret prefix), list (prefix never the secret), archive,
  PATCH per-key limit (percent|hardcap). Protected by a JWT session
  middleware.
- Account balance endpoint (allocation total/spent/reserved).
- Silent fingerprint abuse: register captures the browser fingerprint;
  >= threshold (default 5) accounts sharing one fingerprint are silently
  deactivated + flagged — registration still returns a normal 202, and a
  deactivated account's key resolves as an ordinary 401 at the authz
  surface (no "banned" signal anywhere).
- crypto: argon2 hash/verify + CSPRNG token/key minting (base62). config
  gains [auth] + [email]. CORS on the app for the browser SPA.

Validated against a throwaway Postgres 16: verify-once, full lifecycle
(register→verify→login→create key→account→list→authz resolve→archive→401),
and 5-same-fingerprint → all accounts silently deactivated + no-clue 401.
8 unit + 11 gated integration tests; all skip cleanly without
UPSTREAM_TEST_DATABASE_URL so CI stays green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F6o3ddqmYNh9kzdwq6eowh
2026-06-23 10:28:20 +03:00

53 lines
1.9 KiB
TOML

# helexa-upstream.example.toml — mesh-level account/authorization authority
#
# Copy to helexa-upstream.toml and adjust. Env overrides use the UPSTREAM_
# prefix with __ separators, e.g. UPSTREAM_DB__URL=postgres://...
[server]
# Plaintext listener; edge nginx terminates TLS (consistent with the stack).
listen = "0.0.0.0:8090"
[db]
# PostgreSQL connection URL. Required.
url = "postgres://helexa:helexa@localhost/helexa_upstream"
# max_connections = 16
[grant]
# Flat free token grant every email-verified account receives (the floor of
# the hybrid allocation; single-use top-up codes extend it).
# free_token_grant = 1000000
[abuse]
# When this many accounts share one registration fingerprint, all are
# silently deactivated (no notice to the user).
# fingerprint_account_threshold = 5
# -- Client auth: credentials operators' cortexes present to /authz/v1.
# Each token maps to an operator_id (served-usage attribution). When no
# tokens are configured the authz surface is OPEN (dev only). Distinct from
# end-user API keys, which ride inside the resolve request body.
# [[client_auth.tokens]]
# token = "replace-with-a-strong-shared-secret"
# operator_id = "lair-cafe"
[authz]
# Open reservations older than this are swept (released), self-healing a
# reservation whose settle/release from a cortex was lost.
# reservation_ttl_secs = 120
# sweep_interval_secs = 60
[auth]
# HMAC secret for signing web-session JWTs. MUST be overridden in prod via
# UPSTREAM_AUTH__JWT_SECRET; the built-in default is dev-only.
# jwt_secret = "change-me"
# session_ttl_secs = 604800 # 7 days
# email_token_ttl_secs = 86400 # 24 hours
# Frontend base URL used to build verify/reset links in emails.
app_base_url = "https://helexa.ai"
[email]
# "log" (dev: logs the link) or "smtp".
provider = "log"
# smtp_url = "smtp://user:pass@smtp.example.com:587"
from_addr = "helexa <no-reply@helexa.ai>"