| CC6.1 Logical access: identification and authentication | Per-person sign-in: scrypt password hashes with per-hash salt and stored parameters, constant-time compare, equal work for unknown emails; opaque random session tokens stored only as SHA-256, expiring and revocable; roles read from the person on every request | Implémenté | No passkeys/WebAuthn (planned). No email verification at invite. |
|---|
| CC6.1 | Two-step sign-in (TOTP) with sealed seeds (AES-GCM), hashed one-time recovery codes, replay protection, org policy for owners/admins (optionally approvers), audited admin reset | Implémenté | Not tested against every authenticator app. |
|---|
| CC6.1 | Single sign-on with OpenID Connect (Google Workspace, Microsoft Entra, Okta, generic): authorization code + PKCE (S256), single-use state bound to the browser by an HttpOnly cookie, nonce, issuer/audience/expiry/not-before checks, signature against the issuer's JWKS (asymmetric algorithms only, none/HMAC refused, key type must fit the algorithm), JWKS cache with rotation and a refetch cooldown, per-org email-domain allowlist, verified email required, linking only to people already invited (or auto-create if an owner switches it on, never as owner), subject-based identity after the first link, optional group-to-role mapping (approver/admin only), owner can require SSO with a named break-glass owner | Implémenté | Not run against a real Google/Entra/Okta tenant. Domain ownership is not verified (two orgs claiming one domain makes SSO ambiguous and is refused, but the first claim is trusted). No SCIM provisioning or deprovisioning: removing someone at the IdP does not end an existing Badgeye session before it expires (session_hours). Microsoft sends no email_verified; the tenant-specific issuer is the control. |
|---|
| CC6.1 | Brute-force defence: exponential backoff per email + IP pair, per-IP cap, stored in the database so every serverless instance agrees; the limit cannot be used to test guesses; SSO start and callback are rate limited per IP | Implémenté | |
|---|
| CC6.2 Registration and removal of users | Invite by an owner/admin creates a person with a one-time temporary password that must be changed; only an owner can add an owner; MFA reset by an owner/admin is audited | Implémenté | No user deprovisioning API or console action beyond ending sessions (planned). No periodic access review tooling (organizational + planned). |
|---|
| CC6.3 Role-based access, least privilege, segregation of duties | Roles owner/approver/admin enforced in the API; the real signed-in person (not a picker) approves; the service token is for machines and refused from browsers; org-scoped queries; SSO settings are owner-only; signed audit export is owner/admin-only | Implémenté | Roles are coarse (three). Fine-grained permissions planned. |
|---|
| CC6.1, CC6.6 | Secrets never reach the model or logs: Blindfold vault with AES-256-GCM, decrypt only inside the fill, redaction of recordings/logs/model requests, fingerprint scan before anything leaves the agent; SSO client secrets and the audit signing key are sealed with AES-GCM under a key derived from BADGEYE_AUTH_SECRET | Implémenté | BADGEYE_AUTH_SECRET lives in the environment, not an HSM/KMS (planned). Changing it makes sealed values unreadable (re-enter the SSO secret; audit key: see below). |
|---|
| CC6.6 Boundary protection | Browser egress gate (public http(s) only, DNS answers checked, per-task site pin); agent is outbound-only; OIDC discovery/JWKS/token fetches refuse non-https and (in production) non-public addresses, no redirects, 1 MB cap, 5 s timeout | Implémenté | DNS rebinding between the SSO address check and the connection is not closed. Run behind an egress firewall for strong guarantees. |
|---|
| CC6.7 Transmission and movement of data | TLS on the hosted control plane (platform); session cookie HttpOnly, SameSite=Lax, Secure in production; zero secrets on the agent-cloud wire | Partiel | Agent mTLS and certificate rotation planned. Transport security of the hosting platform is theirs. |
|---|
| CC6.8 Prevention of unauthorized software | Skill versions are immutable and promoted only on a 100% replay; signed installers | Partiel | Signed installers are planned (roadmap gap 2). |
|---|
| CC7.1 Detection of vulnerabilities / configuration changes | Egress/injection eval set, leak tests in CI; dependency licence review | Partiel | No automated dependency vulnerability scanning in CI, no third-party penetration test (planned). |
|---|
| CC7.2 Monitoring for anomalies | Every action, sign-in, failed sign-in, SSO failure with reason, MFA reset, settings change and export is an audit row; SIEM export | Implémenté | No built-in alerting rules; SIEM is the route (customer-run). |
|---|
| CC7.2, CC7.3 Tamper-evident record | Audit log is append-only (ORM guard + SQLite/Postgres triggers, including TRUNCATE on Postgres) and hash-chained per org; GET /v1/audit/verify recomputes the chain. Signed export: a date range as JSONL + CSV with a manifest signed with the org's Ed25519 key; badgeye audit verify <export> checks signature, every hash, every link, the CSV against the JSONL and the anchors offline (no database, no network) and names the first bad row; altering, deleting, inserting or reordering any row fails. Daily anchor: the chain head is recorded and signed once a day (scheduler job at 00:10 local, and on demand), shown on the Audit page and carried in exports | Implémenté | The signing key lives with the service: an attacker who controls both the database and BADGEYE_AUTH_SECRET could re-sign a rewritten history. The defence is to pin the public key and keep exported anchors/heads outside the system (customer copies, SIEM). Key rotation and external timestamping (RFC 3161 or a public transparency log) are planned. A date range is exported as the contiguous run of rows from the first to the last row dated in the range. |
|---|
| CC7.4 Incident response | Kill switch per org/employee; operator command to switch off SSO (badgeye sso disable --org); MFA reset | Partiel | Written incident-response plan and on-call are organizational. |
|---|
| CC8.1 Change management | Versioned database migrations (Alembic, verified against the models in tests), CI suite on SQLite and Postgres, immutable skill versions with replay-gated promotion | Partiel | Code-review and branch-protection policy is organizational. |
|---|
| CC9.2 Vendor and business-partner risk | Model providers are the customer's own plans; documented licences | Partiel | Subprocessor list and vendor reviews are organizational. |
|---|
| CC6.1, CC7.2 Initiator on every audit row | Every audit row records initiator_kind (person, watcher, schedule, director, handoff, outside_input, system) and initiator_id, set once where the work starts (scheduler job, watcher event, delegated task, person) and carried in a context the model cannot change; both fields are part of the row hash for new rows, older rows keep their original hash input so existing chains and exports still verify; the Audit page and GET /v1/audit/events?nobody_watching=true list work no person started | Implémenté | Console-click and API calls by a person take the person from the actor; wiring the signed-in person into every request scope is left to the API layer. Durable outbox redelivery loses the ambient initiator and records system. |
|---|
| CC7.2 Audit-first | An action.attempted row is committed before any executor runs; if it cannot be written the action is not executed | Implémenté | Installed by the policy module's wiring on the team service. |
|---|
| CC6.1, CC6.3, PI1.2 Rule policy | Deny-before-allow rules over website, button label, command/tool, initiator, department and amount; layers org, department, AI employee, person; the strictest effect wins; locked team rules cannot be edited or loosened by members; a malformed rule or a failed evaluation refuses; evaluated before the limits engine and can only tighten it (never lifts a limit, approval, NEVER_TYPES or human gate); every refusal is audited with the rule id; Preview shows which rule would decide | Implémenté | Facts other than action type, system and amount (website, label, tool, command, department) come from the action payload and are only as good as the code that fills them. |
|---|
| PI1.2 Auto-review (hook) | A second-opinion hook for in-limit writes that can only escalate to approval; failure, timeout or an unreadable answer counts as ask | Implémenté | No reviewer model is connected by default. |
|---|
| CC6.1, C1.1 Postgres row-level security | RLS policies on every org-scoped table keyed on current_setting('app.org_id'), a non-owner badgeye_app role without BYPASSRLS, set_config(..., true) (SET LOCAL) per transaction so it is safe behind the Supabase transaction pooler; no setting means no rows; cross-org reads, updates, deletes and inserts fail; run_as_system is the named scope for sweeps | Partiel | Off until the deployment connects as badgeye_app (set its password and URL); the request layer must call bind_org(session, org_id); tables added by later migrations need apply_rls again; SQLite has no RLS. |
|---|