No description
- TypeScript 98.1%
- JavaScript 0.9%
- CSS 0.4%
- Shell 0.4%
- Dockerfile 0.2%
|
All checks were successful
Build and Push Docker Image / build (push) Successful in 4m19s
Phase 0 + Phase 1 + Phase 3 + Phase 4 of the edufeed-accounts design
doc (../edufeed-accounts-design.md). Lays down a custodial signing
service for "team / functional account" use cases on top of the
multi-share fork. Existing self-mode keysmith and FROSTR multi-share
flows are untouched — discriminator is users.auth_mode = 'self' |
'custodial'.
Schema:
- ensureAuthModeColumn() adds users.auth_mode at runtime
(createUserTable() also includes it for fresh installs)
- 20260611_0011_team_members_and_invites.sql adds
team_members + invite_tokens + bunker_invites
Crypto + DB helpers:
- src/db/custodial-kek.ts — HKDF-SHA256(master_key, salt, user_id) for
the per-user KEK, with 9 unit tests. Fails fast when
EDUFEED_ACCOUNTS_MASTER_KEY is missing.
- src/db/teams.ts — pure CRUD + token/secret-hash store + consume
helpers for the three new tables. SHA-256 of every token at rest,
expiry checks inside transactions, no plaintext logging.
- src/db/database.ts — listCustodialUsers() for startup auto-boot;
createUser() takes options.authMode.
Endpoints:
- POST /api/auth/signup — owner email+password, generates 1-of-1
FROSTR keyset, encrypts under HKDF KEK, boots ShareRuntime,
attaches NIP-46, returns bunker URI + session cookie. Optional
import_secret_hex preserves an existing npub (browser decodes the
bech32 nsec to hex so the bech32 string never crosses the wire).
- POST /api/team/invite — owner mints a one-shot accept-invite token
- POST /api/team/accept-invite — colleague consumes the token, mints
a bunker_invites secret, returns scoped bunker URI (no auth — the
token IS the auth)
- POST /api/team/revoke — owner revokes a colleague, flips
nip46_sessions.status to 'revoked'
- GET /api/team/members — owner-facing colleague list
- POST /api/admin/teams/create — dev-time fast-path that bypasses
email+password (admin secret gated), useful for spike testing
NIP-46 service extensions:
- handleSocketRequest refuses requests from sessions with
status='revoked' BEFORE upsertSession (which would otherwise flip
status back to 'active' via ON CONFLICT)
- respondConnect looks up bunker_invites by SHA-256(secret); on
match, links the connecting client_pubkey to the right
team_members row and burns the invite
- shouldAutoApprove short-circuits to true for custodial users —
the session being valid IS the authorization
- handleSignEvent solo bypass for threshold=1: decodeShare().seckey
+ nostr-tools.finalizeEvent (Bifrost's threshold protocol waits
forever for peers that don't exist on a 1-of-1 keyset)
- computeSharedSecret solo bypass for threshold=1:
secp256k1.ProjectivePoint.fromHex('02' + peer).multiply(seckey)
returns the X coord as hex — drop-in for deriveSharedSecret. All
NIP-44 + NIP-04 + NIP-59 gift-wrap flows get the fix automatically.
Runtime + startup:
- server.ts auto-boots every auth_mode='custodial' user's runtime on
startup. Without this, every server restart silently kills every
team account's signing service until the owner re-signs-up.
- ShareMonitor.expectsPeers flag (set false for threshold=1 keysets
in ShareRegistry.add); checkActivityTimeout skips its "no peer
activity → recreate node" branch when the flag is false.
Default relay: wss://relay.edufeed.org (was relay.damus.io —
damus rate-limits the Bifrost kind-20004 announces and the multi-user
auto-boot was tripping it).
234/234 existing tests still pass; 9 new tests for the KEK helper.
End-to-end demo verified 2026-06-12 against edufeed-app's
LoginWithBunker.svelte: signup → bunker URI paste → publish event →
event arrives on relay under the team's npub. Gift wrap unwrap works
via the same NIP-44 solo ECDH path.
See edufeed-accounts-design.md §3 "Known caveat" + §13 "Lessons
learned from the first end-to-end demo" for the four bugs caught live
during the first demo (auto-approve, auto-boot, expectsPeers, solo
sign+ECDH).
|
||
|---|---|---|
| .factory | ||
| .forgejo/workflows | ||
| .github | ||
| docs | ||
| frontend | ||
| llm | ||
| packages/umbrel/igloo | ||
| scripts | ||
| src | ||
| static | ||
| tests/routes | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| bun.lock | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| compose.yml | ||
| CONTRIBUTING.md | ||
| Dockerfile | ||
| env.example | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Igloo Server
Server‑based signing device and personal ephemeral relay for the FROSTR protocol. Igloo provides an always‑on signing node with an optional web UI for configuration and monitoring. Built on @frostr/igloo-core.
Looking to deploy quickly? Start with the one-click options in docs/DEPLOY.md (Umbrel App Store or Docker/Compose).
What It Is
- Threshold Schnorr signing for Nostr using your FROSTR shares (k‑of‑n). The full private key is never reconstructed.
- Two modes: Database (multi‑user, encrypted creds, web UI) or Headless (env‑only, API‑first, no UI).
- Includes an in‑memory relay for dev/tests; use production relays in real deployments.
Features
- Always‑on signer built on igloo‑core with multi‑relay support
- Web UI (React + Tailwind) for setup, monitoring, recovery
- Persisted UI event log (DB mode) with pagination and NDJSON export download
- REST + WebSocket APIs with API‑key, Basic, or session auth
- Health monitor + auto‑restart on repeated failures
- Works as a single node or part of a k‑of‑n signer group
- Ephemeral relay for testing and local development (not for production data)
Documentation
- docs/DEPLOY.md — Umbrel, Docker/Compose, and cloud deployment steps
- docs/SECURITY.md — hardening, CSP, headers, and rate limiting guidance
- docs/CONFIG.md — environment variables and operational tuning (CORS vs WS Origin, timeouts, restart/circuit knobs)
- docs/AUTH_MATRIX.md — which endpoints exist in which mode + what bypasses the global auth gate
- docs/PEER_POLICIES.md — peer policy schema, precedence, and persistence
- docs/RELEASE.md — release workflow, automation, and emergency fixes
- docs/openapi/openapi.yaml — OpenAPI spec (served at
/api/docs)
Quick Start
Prerequisites
- Bun runtime (uses Bun APIs like
bun:sqlite). - FROSTR group/share credentials or add them later via UI/API.
Start Locally (Database mode – default)
git clone https://github.com/FROSTR-ORG/igloo-server.git
cd igloo-server && bun install && bun run build
export ADMIN_SECRET=$(openssl rand -hex 32)
bun run start
# http://localhost:8002 → enter ADMIN_SECRET → create admin → Configure tab → add GROUP_CRED + SHARE_CRED
Start Locally (Headless)
export HEADLESS=true
export GROUP_CRED="bfgroup1..." ; export SHARE_CRED="bfshare1..."
export RELAYS='["wss://relay.primal.net","wss://relay.damus.io"]'
export AUTH_ENABLED=false ; export API_KEY=dev-local-key # /api/env still requires auth
bun run start
Configure & Deploy Fast
Pick a Mode
- Database (recommended): multi‑user, AES‑encrypted creds, admin onboarding via
ADMIN_SECRET, SQLite at./data/igloo.db(override withDB_PATH). - Headless: env‑only config, API‑first, UI disabled. Supports
PEER_POLICIESblocks (seedocs/PEER_POLICIES.md) and API key auth.
Deployment Options
Umbrel (1.1.0+)
- In Umbrel, open the App Store → click the
…menu (top‑right) → Community App Stores. - Add
https://github.com/frostr-org/igloo-server-storeand save, then install Igloo Server. - First launch: Umbrel injects
ADMIN_SECRET(its app password) and the package setsSKIP_ADMIN_SECRET_VALIDATION=true, so you go straight to creating the first admin user. Follow the instructions screen, create your admin, then addGROUP_CRED/SHARE_CRED. - If the install reports data-dir permission errors, fix volume ownership once (see Umbrel note in docs/DEPLOY.md) and restart from the Umbrel dashboard.
- Updates arrive via the store; start/stop from the Umbrel dashboard.
Docker / Compose
# one‑off
docker build -t igloo-server .
docker run -p 8002:8002 \
-e NODE_ENV=production -e HOST_NAME=0.0.0.0 \
-e ADMIN_SECRET=... -e AUTH_ENABLED=true -e RATE_LIMIT_ENABLED=true \
-v $(pwd)/data:/app/data igloo-server
# compose (see compose.yml)
docker compose up -d --build
Reverse proxy (nginx) and cloud steps are in docs/DEPLOY.md.
Production Checklist
NODE_ENV=production, persist/app/data, set strongADMIN_SECRET(keep set after onboarding).- Explicit
ALLOWED_ORIGINS(WebSocket Origin checks support@selffor “whatever host the user connects through”; host match, port-agnostic),TRUST_PROXY=truebehind a proxy; forward WS upgrade headers. - Auth on (
AUTH_ENABLED=true), rate limit on (RATE_LIMIT_ENABLED=true); optionalSESSION_SECRET(auto‑gen if absent). - Timeouts: tune
FROSTR_SIGN_TIMEOUTorSIGN_TIMEOUT_MS(1000–120000ms).
API & Docs
- Swagger UI: http://localhost:8002/api/docs (self‑hosted; run
bun run docs:vendorif assets missing). - OpenAPI: docs/openapi/openapi.yaml or
/api/docs/openapi.{json|yaml}. - Auth: API Key, Basic, or session; WS
/api/eventssupports subprotocol hints (apikey.<TOKEN>,bearer.<TOKEN>,session.<ID>). - Validate spec:
bun run docs:validate.
API Keys
- Headless: set a single
API_KEYin env; HTTP cannot rotate it. - Database mode: manage multiple keys via the UI or admin endpoints. Admin APIs accept either
Authorization: Bearer <ADMIN_SECRET>or an authenticated admin session.- CLI helpers:
bun run api:test:get,bun run api:test:ws,bun run api:test:nip(seescripts/api/README.md).
- CLI helpers:
Operations
- Health monitor: periodic connectivity checks; auto‑recreate node on repeated failures; status at
/api/status. - Error circuit breaker:
ERROR_CIRCUIT_WINDOW_MS(default 60000),ERROR_CIRCUIT_THRESHOLD(default 10),ERROR_CIRCUIT_EXIT_CODE(default 1). - Headless env management requires auth even with
AUTH_ENABLED=false(/api/env*).
Security Quick Setup
Production defaults:
AUTH_ENABLED=true
RATE_LIMIT_ENABLED=true
ALLOWED_ORIGINS=https://yourdomain.example
TRUST_PROXY=true
# Provide ADMIN_SECRET; SESSION_SECRET auto‑generates if absent
Data directory hardening (example):
chmod 700 ./data
chmod 600 ./data/igloo.db ./data/.session-secret 2>/dev/null || true
See docs/SECURITY.md for hardening and CSP details.
Troubleshooting
- UI assets missing/outdated: run
bun run buildto regeneratestatic/app.jsandstatic/styles.css. - UI not updating: prod caches assets; rebuild + restart. Dev disables cache.
- Cred/relay issues: verify
bfgroup1.../bfshare1...and reachable relays. - More: docs/SECURITY.md (hardening), docs/DEPLOY.md (proxy/cloud), docs/openapi/openapi.yaml (API).
Development
bun run dev # frontend watch
bun run start # server
bun test # backend tests
Contributing & License
MIT (see LICENSE). PRs welcome—use Conventional Commits and verify: bun run build, bun test, bun run docs:validate.