Documentation Index
Fetch the complete documentation index at: https://docs.awaithumans.dev/llms.txt
Use this file to discover all available pages before exploring further.
awaithumans is designed to be self-hostable on infrastructure you control. The security model is deliberately conservative — sane defaults, fail-closed behavior, primitives chosen for the OWASP-recommended path.
Cryptographic primitives
| Primitive | Algorithm | Used for |
|---|
| Password hashing | argon2id (RFC 9106 params) | Operator login |
| Session cookies | HMAC-SHA256 over JSON body, urlsafe-b64 | Dashboard sessions |
| Magic links | HMAC-SHA256 + jti for replay protection | Email action URLs |
| Slack signature | HMAC-SHA256 (Slack’s contract) | Inbound webhooks |
| Outbound webhooks | HMAC-SHA256 with X-Awaithumans-Signature | Temporal/LangGraph callbacks |
| At-rest encryption | AES-256-GCM | Slack OAuth bot tokens |
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.
The task routes enforce this:
| Route | Admin | Operator | Assignee | Other logged-in |
|---|
POST /api/tasks | ✓ | ✓ | ✗ | ✗ |
GET /api/tasks | ✓ all | ✓ all | ✓ scoped | ✓ scoped |
GET /api/tasks/{id} | ✓ | ✓ | ✓ | ✗ |
POST /api/tasks/{id}/complete | ✓ | ✓ | ✓ | ✗ |
POST /api/tasks/{id}/cancel | ✓ | ✓ | ✗ | ✗ |
GET /api/tasks/{id}/poll | ✓ | ✓ | ✓ | ✗ |
GET /api/tasks/{id}/audit | ✓ | ✓ | ✗ | ✗ |
DELETE /api/tasks/{id} | ✓ | ✓ | ✗ | ✗ |
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:
| Endpoint | Limit | Window |
|---|
| Login per-IP | 10 attempts | 5 min |
| Login per-email | 20 attempts | 5 min |
| Setup per-IP | 30 attempts | 5 min |
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
| Secret | Can rotate? | Cost |
|---|
PAYLOAD_KEY | NEVER without coordinated downtime | Invalidates every session, magic link, and Slack OAuth row |
ADMIN_API_TOKEN | Yes | All running agents must redeploy with the new value |
| Slack bot token | Yes | Reinstall the app via Settings → Slack |
| Resend / SMTP credentials | Yes | No data loss; new emails use new creds |
| Verifier API keys | Yes | No data loss; new tasks use new keys |
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-Token and X-Slack-Signature header values
Even if upstream code accidentally logs a credential, it gets [REDACTED] before egress. This is belt-and-braces with vendor-error scrubbing in the verifier subsystem.
CORS
The middleware flips allow_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.1 allowed (dev only)
- Plain
http:// to non-localhost: refused at boot
- Mixed
* + explicit: refused at boot
This is enforced at startup via _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/health is 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.
Everything else requires either the admin token or an operator session.
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
It is NOT designed for:
- 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)
For multi-tenant or large-scale deployments, the Phase-2 hosted product handles those constraints. For now, self-host on a network you control.
Reporting issues
Found a security issue? Please report privately to security@awaithumans.com rather than opening a public GitHub issue. We aim to respond within 48 hours and ship fixes within 7 days for critical issues. See SECURITY.md for the full disclosure policy.