# Granular Permissions V2 Plan

This document defines a concrete permission matrix and a low-risk migration path from the current role model to a more granular model.

It is based on current behavior in:

* `src/lib/permissions.ts`
* `src/lib/app-shell.ts`
* `src/app/dashboard/layout.tsx`
* `src/app/api/{atlas,locus,orbis,civitas,org}/**`

## Goals

* Add explicit module access permissions for all four modules:
  * `Atlas`
  * `Locus`
  * `Orbis`
  * `Civitas`
* Align UI visibility and API enforcement so direct API calls cannot bypass hidden navigation.
* Split broad org-admin access (`editOrgSettings`) into scoped admin capabilities.
* Preserve current customer behavior during rollout (no surprise lockouts).

## Current Gaps (Baseline)

* No `Locus` module access permission exists.
* `Atlas` access is tied to edit (`canAccessAtlas` currently aliases `editAtlas`).
* Several `Atlas` read endpoints allow any org member (no module permission check).
* `Civitas` has mixed behavior:
  * org-member access on some endpoints,
  * `accessCivitas`/`manageCivitas` checks on others,
  * owner-self exceptions in ticket flows.
* `editOrgSettings` is too broad and controls many unrelated settings/admin surfaces.
* `organizations.modules` supports `atlas/orbis/civitas`, but not `locus`.

## Proposed Permission Catalog (V2)

### Core Admin Permissions

* `manageUsers` (existing): user status, role assignment, removal, MFA reset.
* `inviteMembers` (existing): invite/provision users.
* `manageTeams` (existing): teams CRUD and membership assignment.
* `viewBilling` (existing): billing visibility.
* `manageBilling` (existing): billing actions and subscription changes.
* `manageRoles` (new): role CRUD + default invite role.
* `manageOrgSecurity` (new): org security policy management.
* `viewOrgAuditLogs` (new): audit-log read/export.
* `editGeneralSettings` (new): org-wide non-module settings.

### Module Access Permissions (Required)

* `accessAtlas` (new)
* `accessLocus` (new)
* `accessOrbis` (existing key; rename from `accessOrbis` only in labels if desired)
* `accessCivitas` (existing)

### Module Capability Permissions

* `editAtlas` (existing): Atlas structure/layout mutations.
* `manageLocusPolicies` (new): Locus policy/admin controls.
* `manageOrbisSchedules` (existing): elevated Orbis scheduling authority.
* `processCivitasTickets` (new): org-level ticket queue processing.
* `manageCivitas` (existing): Civitas profile/documents admin authority.

### Module Settings Permissions

* `editAtlasSettings` (new)
* `editLocusSettings` (new)
* `editOrbisSettings` (new)
* `editCivitasSettings` (new)

## Enforcement Matrix (Target)

### App Navigation And Pages

| Surface                     | Current                             | Target                                                  |
| --------------------------- | ----------------------------------- | ------------------------------------------------------- |
| `/app/atlas`                | `canAccessAtlas` (`editAtlas`)      | `accessAtlas`                                           |
| `/app/locus`                | org membership                      | `accessLocus`                                           |
| `/app/orbis`                | `accessOrbis`                       | `accessOrbis`                                           |
| `/app/civitas`              | org membership + plan/module checks | `accessCivitas` + plan/module checks                    |
| App shell module visibility | `atlas/orbis/civitas` only          | `atlas/locus/orbis/civitas` + matching `access*` checks |

### Atlas APIs (`/api/atlas/**`)

| Route group                                                    | Current           | Target                                                       |
| -------------------------------------------------------------- | ----------------- | ------------------------------------------------------------ |
| Read endpoints (`hqs`, `floors`, `versions`, `elements/usage`) | org membership    | `accessAtlas`                                                |
| Mutations (`POST/PATCH/DELETE/publish/restore`)                | `editAtlas`       | `editAtlas` (and implicitly requires `accessAtlas`)          |
| Debug route                                                    | `editOrgSettings` | `manageRoles` or `manageOrgSecurity` (or keep internal-only) |

### Locus APIs (`/api/locus/**`)

| Route group                                                  | Current                         | Target                                           |
| ------------------------------------------------------------ | ------------------------------- | ------------------------------------------------ |
| All Locus endpoints                                          | org membership + business rules | `accessLocus` + existing business rules          |
| Locus policy/settings changes (currently under org settings) | `editOrgSettings`               | `manageLocusPolicies` and/or `editLocusSettings` |

### Orbis APIs (`/api/orbis/**`)

| Route group                      | Current                       | Target                                             |
| -------------------------------- | ----------------------------- | -------------------------------------------------- |
| General Orbis access             | `accessOrbis`                 | `accessOrbis`                                      |
| Elevated schedule administration | `manageOrbisSchedules`        | `manageOrbisSchedules`                             |
| Team fallback behavior           | mixed permission + membership | keep, but document explicitly in permission policy |

### Civitas APIs (`/api/civitas/**`)

| Route group                             | Current                                   | Target                                                                            |
| --------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------- |
| `tickets` scope=`mine` + self submit    | mostly org membership                     | `accessCivitas`                                                                   |
| `tickets` scope=`organization`          | `accessCivitas`                           | `processCivitasTickets`                                                           |
| `tickets/[ticketId]` processing actions | `canProcess`/owner checks                 | owner self-flow with `accessCivitas`; org processing with `processCivitasTickets` |
| `users` list + `public-holidays`        | org membership                            | `accessCivitas`                                                                   |
| `settings` docs view/update             | GET org membership; PATCH `accessCivitas` | GET `accessCivitas`; PATCH `editCivitasSettings` or `manageCivitas`               |
| user profile/documents endpoints        | `manageCivitas`                           | `manageCivitas`                                                                   |
| uploads                                 | mixed by scope                            | keep scope rules; require base `accessCivitas` for ticket and organization scopes |

### Dashboard + Org APIs

| Surface                            | Current                                                            | Target                                                               |
| ---------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------- |
| Role management (`/api/org/roles`) | GET: `editOrgSettings` or `manageUsers`, writes: `editOrgSettings` | `manageRoles`                                                        |
| Org security settings              | `editOrgSettings`                                                  | `manageOrgSecurity`                                                  |
| Org audit logs                     | `editOrgSettings`                                                  | `viewOrgAuditLogs`                                                   |
| General settings                   | `editOrgSettings`                                                  | `editGeneralSettings`                                                |
| Module settings routes             | `editOrgSettings`                                                  | corresponding `edit*Settings` permission                             |
| Users dashboard visibility         | `manageUsers`/`manageCivitas`/`inviteMembers` mix                  | keep behavior; optionally add a dedicated `viewUsersDirectory` later |

## Legacy To V2 Mapping (Backfill Rules)

Apply these rules to every existing role document once new keys are introduced.

| Legacy key             | V2 mapping                                                                                                                                                                        |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `manageUsers`          | `manageUsers = true`                                                                                                                                                              |
| `inviteMembers`        | `inviteMembers = true`                                                                                                                                                            |
| `manageTeams`          | `manageTeams = true`                                                                                                                                                              |
| `manageBilling`        | `manageBilling = true` and `viewBilling = true`                                                                                                                                   |
| `viewBilling`          | `viewBilling = true`                                                                                                                                                              |
| `editAtlas`            | `accessAtlas = true`, `editAtlas = true`                                                                                                                                          |
| `accessOrbis`          | `accessOrbis = true`                                                                                                                                                              |
| `manageOrbisSchedules` | `manageOrbisSchedules = true`, `accessOrbis = true`                                                                                                                               |
| `accessCivitas`        | `accessCivitas = true`, `processCivitasTickets = true`                                                                                                                            |
| `manageCivitas`        | `accessCivitas = true`, `processCivitasTickets = true`, `manageCivitas = true`                                                                                                    |
| `editOrgSettings`      | set true for: `manageRoles`, `manageOrgSecurity`, `viewOrgAuditLogs`, `editGeneralSettings`, `editAtlasSettings`, `editLocusSettings`, `editOrbisSettings`, `editCivitasSettings` |
| (no legacy key)        | `accessLocus = true` for all existing roles to preserve current behavior                                                                                                          |

## Migration Plan (Phased)

### Phase 0: Schema Preparation

* Add new permission keys and labels in `src/lib/permissions.ts`.
* Add new org module flag `modules.locus` in admin org schemas and defaults.
* Keep legacy checks active; do not enforce new keys yet.

### Phase 1: Compatibility Helpers

* Add helper functions that support legacy behavior during transition:
  * `canAccessAtlas` should allow `accessAtlas || editAtlas`.
  * `canProcessCivitasTickets` should allow `processCivitasTickets || accessCivitas || manageCivitas`.
  * Keep existing `editOrgSettings` checks temporarily as compatibility OR conditions.
* Wire app-shell module access object to include `locus` and `canAccessLocus`.

### Phase 2: Data Backfill

* Run idempotent role migration job:
  * Query all `roles` docs.
  * For each role, compute V2 permissions by applying mapping table.
  * Write merged permission object and `updatedAt`.
  * Emit audit event `role.permissions.v2.backfill`.
* Add dry-run mode and per-org summary metrics before full write.
* Implementation endpoint:
  * `POST /api/admin/platform-ops/permissions-v2-backfill`
  * Guardrails: same-origin + App Check + master-admin access.
  * Payload:
    * `dryRun?: boolean` (default `true`)
    * `organizationId?: string | null`
    * `limit?: number`
    * `includeRoleSamples?: number`

### Backfill Operations Runbook

Preferred execution path (avoids App Check issues from Postman):

1. Sign in as a master admin and open `/admin/platform?section=platform-ops`.
2. In **Permissions v2 backfill**, run **dry-run** first.
3. Validate:
   * `rolesFailed = 0`
   * expected `rolesWouldChange` volume
   * `changedRoleSamples` looks correct
4. Run **apply** with confirmation enabled.
5. Run **dry-run** again and confirm:
   * `rolesWouldChange = 0`
   * `rolesFailed = 0`

Notes:

* Postman calls to this endpoint usually fail because the route requires both same-origin and App Check.
* The admin panel uses `fetchWithAppCheck()` in-browser, so the request includes a valid App Check token for your active session.
* Optional filters:
  * `organizationId`: backfill one org at a time
  * `limit`: bounded staged rollout
  * `includeRoleSamples`: sample size for changed roles review

### Phase 3: Enforcement Alignment

* Update API guards to target permissions in this order:
  1. Atlas reads -> `accessAtlas`
  2. Locus routes -> `accessLocus`
  3. Civitas read/submit surfaces -> `accessCivitas`
  4. Civitas queue/process actions -> `processCivitasTickets`
  5. Org settings/audit/security/roles -> new scoped org permissions
* Enforce strict V2 checks (no env-driven compatibility toggles).

### Phase 4: UI And Role Editor

* Update role editor permission groups (`src/components/dashboard/org-settings.tsx`):
  * Core Admin
  * Module Access
  * Module Capabilities
  * Module Settings
* Ensure every permission in API has a visible and explainable toggle.

### Phase 5: Legacy Cleanup

* Remove all legacy fallback checks and temporary rollout env toggles.
* Keep `editOrgSettings` as a scoped V2 permission (`View organization details`) used by home widgets and related visibility surfaces only.
* Keep only scoped V2 permission checks in helpers, APIs, and role-editor docs/copy.

## Recommended Test Matrix

* Unit:
  * `src/lib/permissions.test.ts`
  * `src/lib/app-shell.test.ts`
* Route tests to update/add:
  * Atlas GET routes should reject when lacking `accessAtlas`.
  * Locus routes should reject when lacking `accessLocus`.
  * Civitas `tickets` scope split (`mine` vs `organization`) by `accessCivitas` and `processCivitasTickets`.
  * Org settings routes per new `edit*Settings`.
  * Roles/security/audit routes per new scoped org permissions.
* Regression:
  * Existing `Admin` and `Member` orgs after migration.
  * Default invite role behavior for `register` and `sso-session` flows.

## Implementation Order (Suggested)

1. Add permission keys + helper functions + tests (no behavior change).
2. Add `modules.locus` support in admin + app shell.
3. Ship backfill script and run dry-run in staging.
4. Enable enforcement route-by-route behind a feature flag.
5. Final cleanup and docs refresh.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xdbx.gitbook.io/axon/getting-started/granular-permissions-v2-plan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
