Skip to main content

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

PrimitiveAlgorithmUsed for
Password hashingargon2id (RFC 9106 params)Operator login
Session cookiesHMAC-SHA256 over JSON body, urlsafe-b64Dashboard sessions
Magic linksHMAC-SHA256 + jti for replay protectionEmail action URLs
Slack signatureHMAC-SHA256 (Slack’s contract)Inbound webhooks
Outbound webhooksHMAC-SHA256 with X-Awaithumans-SignatureTemporal/LangGraph callbacks
At-rest encryptionAES-256-GCMSlack 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:
  1. Admin bearer token (AWAITHUMANS_ADMIN_API_TOKEN). Highest trust. Held by the agent process. Allows everything.
  2. Operator session (dashboard login). High trust. Allows everything except things the admin gate enforces explicitly (e.g. server config).
  3. Reviewer session (non-operator user). Scoped to tasks they’re assigned. Cannot list / read / complete tasks belonging to others.
The task routes enforce this:
RouteAdminOperatorAssigneeOther 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:
EndpointLimitWindow
Login per-IP10 attempts5 min
Login per-email20 attempts5 min
Setup per-IP30 attempts5 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

SecretCan rotate?Cost
PAYLOAD_KEYNEVER without coordinated downtimeInvalidates every session, magic link, and Slack OAuth row
ADMIN_API_TOKENYesAll running agents must redeploy with the new value
Slack bot tokenYesReinstall the app via Settings → Slack
Resend / SMTP credentialsYesNo data loss; new emails use new creds
Verifier API keysYesNo 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.