S-02: Extract extract_credential_raw_id() helper in services/passkey.py — replaces 2 inline rawId parsing blocks in passkeys.py S-03: Add PasskeyLoginResponse type, use in useAuth passkeyLoginMutation S-04: Add Cancel button to disable-passwordless dialog W-03: Invalidate auth queries on disable ceremony error/cancel Perf-2: Session cap uses ID-only query + bulk UPDATE instead of loading full ORM objects and flipping booleans individually Perf-3: Remove passkey_count from /auth/status hot path (polled every 15s). Use EXISTS for has_passkeys boolean. Count derived from passkeys list query in PasskeySection (passkeys.length). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UMBRA Backend
FastAPI backend for the UMBRA life management application with async SQLAlchemy, PostgreSQL, multi-user RBAC, and comprehensive security.
Features
- FastAPI with async/await and Pydantic v2
- SQLAlchemy 2.0 async engine with
Mapped[]types - PostgreSQL 16 via asyncpg
- Alembic database migrations (001-061)
- Authentication: Argon2id passwords + signed httpOnly cookies + optional TOTP MFA + passkey (WebAuthn/FIDO2)
- Multi-user RBAC: admin/standard roles, per-user resource scoping
- Session management: DB-backed sessions, sliding window expiry, concurrent session cap
- Account security: Account lockout (10 failures = 30-min lock), CSRF protection, rate limiting
- APScheduler for background notification dispatch
Project Structure
backend/
├── alembic/versions/ # 61 database migrations
├── app/
│ ├── models/ # 21 SQLAlchemy 2.0 models
│ ├── schemas/ # 14 Pydantic v2 schema modules
│ ├── routers/ # 17 API routers
│ ├── services/ # Auth, session, passkey, TOTP, audit, recurrence, etc.
│ ├── jobs/ # APScheduler notification dispatch
│ ├── config.py # Pydantic Settings (env vars)
│ ├── database.py # Async engine + session factory
│ └── main.py # FastAPI app + CSRF middleware
├── requirements.txt
├── Dockerfile
├── alembic.ini
└── start.sh
Setup
1. Install Dependencies
pip install -r requirements.txt
2. Configure Environment
Copy .env.example to .env and configure:
DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/umbra
SECRET_KEY=generate-a-strong-random-key
ENVIRONMENT=production
# WebAuthn / Passkeys (required for passkey auth)
WEBAUTHN_RP_ID=your-domain.com
WEBAUTHN_RP_NAME=UMBRA
WEBAUTHN_ORIGIN=https://your-domain.com
3. Run Migrations
alembic upgrade head
4. Start Server
uvicorn app.main:app --host 0.0.0.0 --port 8000
API Routes
All routes require authentication (signed session cookie) except /api/auth/* and /health.
| Prefix | Description |
|---|---|
/api/auth |
Login, logout, register, setup, status, password, TOTP, passkeys |
/api/admin |
User management, system config, audit logs (admin only) |
/api/todos |
Task management with categories and priorities |
/api/events |
Calendar events with recurrence support |
/api/event-invitations |
Event invitation RSVP and management |
/api/event-templates |
Reusable event templates |
/api/calendars |
Calendar CRUD |
/api/shared-calendars |
Calendar sharing with permission levels |
/api/reminders |
Reminder management with snooze |
/api/projects |
Projects with tasks, comments, and collaboration |
/api/people |
Contact management |
/api/locations |
Location management |
/api/connections |
User connections (friend requests) |
/api/notifications |
In-app notification centre |
/api/settings |
User preferences and ntfy configuration |
/api/dashboard |
Aggregated dashboard data |
/api/weather |
Weather widget data |
Authentication
UMBRA supports three authentication methods:
- Password (Argon2id) - Primary login method
- TOTP MFA - Optional second factor via authenticator apps
- Passkeys (WebAuthn/FIDO2) - Optional passwordless login via biometrics, security keys, or password managers
Passkey login bypasses TOTP (a passkey is inherently two-factor: possession + biometric/PIN).
Security
- CSRF protection via
X-Requested-Withheader middleware - All Pydantic schemas use
extra="forbid"(mass-assignment prevention) - Nginx rate limiting on auth, registration, and admin endpoints
- DB-backed account lockout after 10 failed attempts
- Timing-safe dummy hash for non-existent users (prevents enumeration)
- SSRF validation on ntfy webhook URLs
- Naive datetimes throughout (Docker runs UTC)
Docker
The backend runs as non-root appuser in python:3.12-slim:
docker build -t umbra-backend .
docker run -p 8000:8000 --env-file .env umbra-backend
In production, use Docker Compose (see root docker-compose.yaml).