# RBAC Matrix — Vigilant Entities / Amwāl OS

> Role / capability matrix. Source of truth for access decisions.
> Update whenever a new privileged route is added.

## Roles (least → most privileged)

| Role           | Scope             | Description                                          |
|----------------|-------------------|------------------------------------------------------|
| `guest`        | self only         | Auto-provisioned on magic-link / signup; minimal access |
| `startup_member` | startup-scoped  | Member of a startup record; can edit own startup     |
| `founder`      | startup-scoped    | Owner of a startup record                            |
| `investor`     | self + investments| View own portfolio + opportunity pipeline            |
| `holding_admin`| tenant-scoped     | Tenant administrator; full tenant CRUD               |
| `system_admin` | platform-wide     | Cross-tenant operator                                |
| `MAKER`        | platform-wide     | Singleton; matches `MAKER_EMAIL` env. Audit-cloaked. |

> `MAKER` is NOT a separate database role — it's the user whose email equals
> the `MAKER_EMAIL` env var, additionally holding `system_admin`. Cloaking
> is computed at write time inside `audit_service.log_event`.

## Capability matrix (selected)

Legend:  ✓ = allowed   ✗ = denied   ▲ = own resource only

| Capability                          | guest | member | founder | investor | h-admin | s-admin | MAKER |
|-------------------------------------|:-----:|:------:|:-------:|:--------:|:-------:|:-------:|:-----:|
| Sign up / sign in                   | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| Read own profile                    | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| Update own profile                  | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| Delete own account (GDPR)           | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✗     |
| Export own data (GDPR)              | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| View own devices                    | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| Revoke own device                   | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| Read public opportunities           | ✓     | ✓      | ✓       | ✓        | ✓       | ✓       | ✓     |
| Create startup                      | ✗     | ✗      | ✓       | ✗        | ✓       | ✓       | ✓     |
| Edit own startup                    | ✗     | ▲      | ▲       | ✗        | ✓       | ✓       | ✓     |
| Edit any startup in tenant          | ✗     | ✗      | ✗       | ✗        | ✓       | ✓       | ✓     |
| View tenant audit log               | ✗     | ✗      | ✗       | ✗        | ✓       | ✓       | ✓     |
| Verify audit chain (own tenant)     | ✗     | ✗      | ✗       | ✗        | ✓       | ✓       | ✓     |
| Verify audit chain (any tenant)     | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Manage tenant branding              | ✗     | ✗      | ✗       | ✗        | ✓       | ✓       | ✓     |
| Manage tenant billing               | ✗     | ✗      | ✗       | ✗        | ✓       | ✓       | ✓     |
| Manage tenant users                 | ✗     | ✗      | ✗       | ✗        | ✓       | ✓       | ✓     |
| Create new tenant                   | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Cross-tenant admin                  | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| GO LIVE flag flip                   | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Brute-force unlock any account      | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Impersonate another user            | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Read other users' devices           | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Manage pricing plans                | ✗     | ✗      | ✗       | ✗        | ✗       | ✓       | ✓     |
| Manage maker pricing console        | ✗     | ✗      | ✗       | ✗        | ✗       | ✗       | ✓     |
| Maker action visible in audit log   | n/a   | n/a    | n/a     | n/a      | n/a     | n/a     | ✗ (cloaked) |

## Enforcement points

- **Route-level**: `Depends(get_current_user)` + `require_roles(...)` decorator.
- **Query-level**: every `db.<collection>.find(...)` for tenant-scoped data
  injects `tenant_id` from the authenticated user — never from query string.
- **Audit-level**: writes to `audit_logs` short-circuit when actor is Maker.
- **JWT-level**: `did` claim enforces per-device revoke. `password_changed_at`
  invalidates all tokens issued before a global rotation.

## Role transition rules

- `guest → startup_member` — automatic on accepting startup invite.
- `guest → investor` — explicit signup choice (validated server-side).
- `guest → founder` — explicit signup choice + email verification.
- `* → holding_admin` — only the existing tenant `holding_admin` or `system_admin`
  can grant this via `routes_admin_roles`.
- `* → system_admin` — env allowlist (`SUPER_ADMIN_EMAILS`) only; promoted
  on every login if the email matches.
- `system_admin → MAKER` — env (`MAKER_EMAIL`); requires .env change + redeploy.

## Audit signatures

Every privileged action produces an audit log row of the form:
```
{
  actor_user_id, actor_email, actor_role,
  action,             # verb + namespace, e.g. "tenant_user_promoted"
  resource_type,      # "user", "tenant", "opportunity", ...
  resource_id,
  severity,           # "info" | "warning" | "critical"
  metadata,           # action-specific structured detail
  prev_hash,          # hash-chain link
  entry_hash,
  chain_index
}
```

The Maker is exempt from `audit_logs` writes but is recorded in
`account_activity` (a personal, Maker-only-visible feed).

## Last reviewed

- 2026-02-12
- Next: 2026-05-12 (quarterly)
