Cryptographic primitives
All HMAC keys are HKDF-derived from a single root
PAYLOAD_KEY, with channel-scoped salts so the same key never signs two different primitives. Compromising one downstream key can’t be lifted to another.
Authorization model
Three trust tiers:- Admin bearer token (
AWAITHUMANS_ADMIN_API_TOKEN). Highest trust. Held by the agent process. Allows everything. - Operator session (dashboard login). High trust. Allows everything except things the admin gate enforces explicitly (e.g. server config).
- Reviewer session (non-operator user). Scoped to tasks they’re assigned. Cannot list / read / complete tasks belonging to others.
For
GET /api/tasks (list), non-operator sessions are server-side-scoped to assigned_to_user_id == claims.user_id. The client can’t override the filter via query params.
Rate limiting
/api/auth/login and /api/setup/operator both have in-process sliding-window limiters:
Successful login resets the per-email counter so legit users don’t lock themselves out after fat-fingering their password.
In-process means single-uvicorn-worker only; multi-replica deployments need a shared store (Redis) — planned post-launch.
Secret rotation
PAYLOAD_KEY rotation is a Phase-2 feature gated on a versioned-key derivation scheme. For v0.1, treat it as install-once-and-never-rotate.
Logs
The root logger has a scrubber filter that redacts:- OpenAI / Anthropic style keys (
sk-...) - Stripe-style scoped keys (
sk_live_...) - Bearer tokens (
Bearer ...) - Google API keys (
AIza...) - Password fields in JSON / form-style serialization
X-Admin-TokenandX-Slack-Signatureheader values
[REDACTED] before egress. This is belt-and-braces with vendor-error scrubbing in the verifier subsystem.
CORS
The middleware flipsallow_credentials ON the moment the origin list isn’t a bare *. To prevent operators from accidentally enabling credential-bearing CORS to unsafe origins:
- Bare
*is allowed (credentials forced off — read-only public access) - Explicit
https://origins allowed http://localhost/http://127.0.0.1allowed (dev only)- Plain
http://to non-localhost: refused at boot - Mixed
*+ explicit: refused at boot
_validate_cors_origins(). Bad configs fail loud — the server won’t start.
What NOT to expose
- The dashboard at
/is fine to expose; it requires login. /api/setup/*is unauthenticated by design (first-run bootstrap). After setup completes (any user exists), it returns 409. Pre-setup, the bootstrap token gates it; rate-limited per-IP./api/healthis unauthenticated. Wire it to your load balancer’s health check./api/channels/slack/interactions,/api/channels/slack/oauth/*,/api/channels/email/action/*— all self-authenticate via signed payloads. Don’t put behind your auth proxy; you’d break the integrations.
Threat model
awaithumans is designed for:- Small to medium teams (1–50 reviewers)
- Single tenant (one team’s tasks, no multi-customer isolation)
- Self-hosted on infrastructure the operator trusts
- Public-internet multi-tenant SaaS (separate hosted product, post-launch)
- Hostile insider threat (an operator with admin token can do anything)
- Sub-second-latency review at >1k tasks/sec (single-process scheduler)