Architecture Network topology, authentication layers, and Supabase as the state store.
Component URL Hosting relay-panelhttps://relay-panel.bitmacro.ioVercel relay-api (Web Server)https://relay-api.bitmacro.ioVercel relay-connect-webSubdomain or path Vercel / self-hosted relay-agent (Relay Server Agent)https://<operator-endpoint>Operator VPS / local hardware Supabase Internal Supabase Cloud
The Relay Server Agent must be reachable by the Web Server (Vercel egress).
If the agent runs on a LAN, expose it via tunnel: WireGuard + Nginx Proxy Manager, Cloudflare Tunnel, or ngrok.
relay-panel only needs access to relay-api. Zero direct access to agents or Supabase.
relay-connect-web proxies all /signer/* calls server-side — the browser never holds RELAY_API_KEY.
Provider Mechanism GitHub OAuth NextAuth.js v5 Session ID providerAccountId (numeric GitHub ID) stored as token.providerUserId → session.user.id
session.user.id becomes X-Provider-User-Id on every server-side call to relay-api.
Header Value Scope X-API-Keyprocess.env.RELAY_API_KEYAll requests from panel X-Provider-User-IdGitHub numeric ID Filters relay_configs rows by user
Alternative for clients with a NextAuth session: Authorization: Bearer <JWT> decoded via JWT_SECRET.
v0.2 (multi-relay): Bearer token per relay instance, stored in RELAY_INSTANCES[].token and mirrored in relay_configs.token. Path prefix: /:relayId/.
v0.1 (legacy): Single RELAY_AGENT_TOKEN env var; no path prefix.
/health — no auth required on any version.
All persistent state lives in Supabase. The Relay Server Agent is completely stateless.
Column Type Description iduuid PK — gen_random_uuid() nametext Display name endpointtext Agent base URL tokentext Bearer token for agent agent_relay_idtext Logical relay ID for multi-relay agent (v0.2) provider_user_idtext GitHub user ID (NextAuth) user_iduuid Nullable, legacy
Index: idx_relay_configs_provider_user_id
Sessions for NIP-46 pairing: links provider_user_id, relay_config_id, app pubkey, bridge_wss, pairing_secret, and status.
Status Meaning pendingURI generated, signer not yet paired activePairing confirmed via POST /signer/session/:id/complete revokedManually revoked
Migrations:
20260324140000_relay_nip46_sessions.sql — table creation
20260325130000_relay_nip46_sessions_grants.sql — PostgREST role grants (anon / authenticated / service_role); required to avoid 42501 on INSERT from relay-api
Variable Description RELAY_AGENT_TOKEN(v0.1) Single Bearer token RELAY_INSTANCES(v0.2) JSON array: [{id, token, strfryConfig, strfryDb, whitelistPath?}] ALLOWED_ORIGINSCORS origins; default includes relay-panel.bitmacro.io
Example RELAY_INSTANCES:
[
{ "id" : "public" , "token" : "tok_pub" , "strfryConfig" : "/etc/strfry-public.yml" , "strfryDb" : "/var/lib/strfry/public.lmdb" },
{ "id" : "private" , "token" : "tok_priv" , "strfryConfig" : "/etc/strfry-private.yml" , "strfryDb" : "/var/lib/strfry/private.lmdb" },
{ "id" : "paid" , "token" : "tok_paid" , "strfryConfig" : "/etc/strfry-paid.yml" , "strfryDb" : "/var/lib/strfry/paid.lmdb" }
]
Variable Description RELAY_API_KEYShared key with relay-panel and relay-connect-web JWT_SECRETNextAuth JWT decode SUPABASE_URLSupabase project URL SUPABASE_SERVICE_ROLE_KEYFull access to relay schema
Variable Description NEXTAUTH_SECRETNextAuth secret GITHUB_CLIENT_IDGitHub OAuth app GITHUB_CLIENT_SECRETGitHub OAuth secret NEXT_PUBLIC_API_URLWeb Server base URL RELAY_API_URLServer-side override (avoids cold-start latency) RELAY_API_KEYShared with relay-api
Variable Description RELAY_API_URLWeb Server base URL RELAY_API_KEYShared key SIGNER_PROVIDER_USER_IDGitHub ID for the operator account NEXT_PUBLIC_RELAY_BRIDGE_WSSForce NIP-46 bridge wss:// NEXT_PUBLIC_NIP07_METADATA_RELAYSComma-separated relays for kind 0 after NIP-07 NEXT_PUBLIC_RELAY_CONFIG_IDPin a specific relay_configs UUID SIGNER_PROXY_TIMEOUT_MSProxy timeout (ms)
Surface Notes relay-agent CORS may allow https://relay-panel.bitmacro.io and http://localhost:3000 for testing; the panel does not call the agent directly in production. relay-panel Session cookies on its own origin; all relay-api calls are server-side . relay-api Panel and connect proxies call from servers, not from arbitrary browser origins exposing secrets.
Secrets:
Never ship RELAY_API_KEY to the browser; relay-connect-web only attaches it on Next.js server routes.
Pairing for NIP-46 uses a per-session pairing_secret verified on POST /signer/session/:id/complete.