> ## Documentation Index
> Fetch the complete documentation index at: https://docs.equa.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Access Control Model

> Role-based access control, cascading permissions, organization hierarchy, and data room security

# Access Control Model

> **Status:** DRAFT
> **Owner:** Engineering
> **Last Review:** 2026-02-21
> **Applicable Standards:** SOC 2 (CC6.1, CC6.2, CC6.3) / GDPR (Art. 25, Art. 32)

***

## 1. Purpose

This document describes the access control model that governs who can view, edit, and manage data within the Equa platform. It covers role-based access control (RBAC), the cascading permission model for organization hierarchies, data room access, agent permission controls, unauthenticated guest chat access, and frontend enforcement.

## 2. Scope

| Component       | In Scope | Notes                                                    |
| --------------- | -------- | -------------------------------------------------------- |
| equa-server     | Yes      | Authorization module, permission checks, role management |
| equa-web        | Yes      | Frontend route guards, permission HOC, UI-level gating   |
| equabot-gateway | Partial  | Agent permission proxy that checks tool permissions      |
| PostgreSQL      | Yes      | Role/permission/member junction tables                   |

## 3. RBAC Overview

Source: `equa-server/modules/auth/src/authorization.ts`, `equa-server/modules/common/src/permissions.ts`, `equa-server/modules/common/src/roles.ts`

Equa implements role-based access control (RBAC) with the following characteristics:

* Permissions are granted through roles, not directly to users
* Roles are assigned to members within the context of an organization
* A user can hold different roles in different organizations
* Global roles exist for site-level administration
* Permissions cascade through organization hierarchies

```mermaid theme={null}
erDiagram
    Users ||--o{ Members : "membership"
    Users ||--o{ GlobalRolesUsers : "site roles"
    Members }o--|| Organizations : "belongs to"
    Members ||--o{ MembersRoles : "assigned"
    MembersRoles }o--|| Roles : "role"
    Roles ||--o{ PermissionsRoles : "grants"
    PermissionsRoles }o--|| Permissions : "permission"
    Organizations ||--o{ OrganizationsRoles : "org roles"
    OrganizationsRoles }o--|| Roles : "role"
```

## 4. Permission Definitions

Source: `equa-server/modules/common/src/permissions.ts`

### 4.1 Built-In Permissions (14)

| Permission                | UUID     | Scope                                         |
| ------------------------- | -------- | --------------------------------------------- |
| `viewOrganization`        | Built-in | Read organization details                     |
| `viewCapTable`            | Built-in | Read cap table, shareholdings, security types |
| `viewDocuments`           | Built-in | Read data room files and directories          |
| `viewMembers`             | Built-in | Read member list and profiles                 |
| `editOrganizationDetails` | Built-in | Modify organization settings                  |
| `editCapTable`            | Built-in | Create/modify shareholdings, issue equity     |
| `editDocuments`           | Built-in | Upload, rename, delete files in data room     |
| `editMembers`             | Built-in | Add/remove members, change titles             |
| `deleteDocuments`         | Built-in | Permanently remove files                      |
| `signing`                 | Built-in | Sign documents and agreements                 |
| `fullVoting`              | Built-in | Vote on all resolutions                       |
| `partialVoting`           | Built-in | Vote on subset of resolutions                 |
| `editBilling`             | Built-in | Manage subscription and payment methods       |
| `viewIncentivePlan`       | Built-in | Read ESOP/incentive plan details              |
| `editIncentivePlan`       | Built-in | Create/modify incentive plans                 |

### 4.2 Custom Permissions

Organizations can define custom roles with any combination of the built-in permissions via the `Roles` entity:

| Field         | Type            | Purpose                                           |
| ------------- | --------------- | ------------------------------------------------- |
| `id`          | UUID            | Role identifier                                   |
| `name`        | string          | Display name                                      |
| `description` | string          | Role description                                  |
| `owner`       | UUID (nullable) | Organization that owns this role                  |
| `isShared`    | boolean         | If `true`, role is available across organizations |

Source: `equa-server/modules/persistence/src/schema.ts` (Roles entity)

## 5. Built-In Roles

Source: `equa-server/modules/common/src/roles.ts`

| Role           | Typical Permissions                   | Use Case                               |
| -------------- | ------------------------------------- | -------------------------------------- |
| `admin`        | All permissions                       | Organization administrator             |
| `auditor`      | All view permissions                  | External auditor with read-only access |
| `director`     | View + edit org, cap table, documents | Board member                           |
| `guest`        | `viewOrganization` only               | Minimal access visitor                 |
| `investor`     | View org, cap table, documents        | Equity holder viewing their position   |
| `legal`        | View + edit documents, signing        | Legal counsel                          |
| `partner`      | View + edit org, cap table, members   | Operating partner                      |
| `president`    | All permissions                       | Corporate officer                      |
| `secretary`    | View + edit org, documents, signing   | Corporate secretary                    |
| `treasurer`    | View + edit org, cap table, billing   | Financial officer                      |
| `signatory`    | View org, documents, signing          | Document signatory                     |
| `memberAnnual` | View org, cap table, documents        | Annual meeting participant             |
| `memberRead`   | View org, cap table                   | Read-only member                       |
| `holder`       | View org, cap table                   | Equity holder (minimal)                |

## 6. Cascading Permission Model

Source: `equa-server/docs/cascading-permission.puml`, `equa-server/modules/api/src/site/authorization.ts`

The Equa platform supports nested organization hierarchies (parent-child relationships). When a user views a parent organization, their effective permissions are calculated by combining direct and indirect permissions.

### 6.1 Algorithm

```mermaid theme={null}
flowchart TD
    Start["User views Organization X"] --> DirectCheck["Get Direct Permissions"]
    DirectCheck --> DirectResult["Direct = Roles assigned to user in Org X"]
    Start --> IndirectCheck["Get Indirect Permissions"]
    IndirectCheck --> ChildOrgs["Find child orgs where user is a member"]
    ChildOrgs --> ChildPerms["Get permissions in each child org"]
    ChildPerms --> Intersect["Intersection of all child org permission sets"]
    Intersect --> IndirectResult["Indirect = Intersection result"]
    DirectResult --> Union["Union of Direct + Indirect"]
    IndirectResult --> Union
    Union --> EffectivePerms["Effective Permissions for Org X"]
```

* **Direct permissions:** The union of all permissions granted by roles assigned to the user in the target organization
* **Indirect permissions:** The intersection of permission sets from all child organizations where the user is a member (the user must have the permission in every child org for it to be indirect)
* **Effective permissions:** The union of direct and indirect permissions

### 6.2 Key Function

`getUserDirectAndIndirectPermissions()` in `equa-server/modules/api/src/site/authorization.ts` implements this algorithm. Endpoint handlers call `requirePermissions()` which wraps the endpoint with a permission check before the handler executes.

## 7. Permission Enforcement

### 7.1 Backend Enforcement

Source: `equa-server/modules/auth/src/authorization.ts`

Every API endpoint declares its required permissions. The `vineyard-lawn` framework calls the permission check function before the handler:

| Function                                   | Purpose                                      |
| ------------------------------------------ | -------------------------------------------- |
| `requirePermissions()`                     | Wraps endpoints with permission checks       |
| `canWriteSite()`                           | Checks site-level write permissions          |
| `canReadSite()`                            | Checks site-level read permissions           |
| `defineRelationalAuthorizationEndpoints()` | Applies permission checks to endpoint arrays |
| `requireLoggedInSync()`                    | Verifies user is authenticated               |

Failure returns HTTP 403 Forbidden.

### 7.2 Frontend Enforcement

Source: `equa-web/src/shared/hocs/with-permissions.tsx`, `equa-web/src/app/components/routes/routes.tsx`

| Mechanism          | Implementation                                       | Purpose                                       |
| ------------------ | ---------------------------------------------------- | --------------------------------------------- |
| Route guards       | `protected: boolean` on route definitions            | Redirect unauthenticated users to login       |
| Permission HOC     | `withPermissions([BuiltInPermission.viewDocuments])` | Hide/show components based on org permissions |
| Admin requirement  | `isSiteAdmin` route requirement                      | Gate admin pages                              |
| Email verification | `userIsEmailVerified(user)` check                    | Block unverified users from protected routes  |

<Note>
  Frontend enforcement is a UX convenience only. All security-critical checks are enforced server-side. A user bypassing frontend guards will still be blocked by the backend permission checks.
</Note>

## 8. Data Room Access Control

Source: `equa-server/modules/persistence/src/schema.ts` (DataRoomsMembers entity)

Data rooms provide document storage with per-member access control, separate from the general permission system.

### 8.1 DataRoomsMembers Entity

| Column         | Type        | Purpose                                            |
| -------------- | ----------- | -------------------------------------------------- |
| `dataRoomName` | string (PK) | Data room identifier                               |
| `member`       | UUID (PK)   | Member granted access                              |
| `permission`   | UUID (PK)   | Permission level for this member in this data room |

This is a composite primary key -- the combination of data room, member, and permission is unique. A member can have multiple permission levels in the same data room.

### 8.2 Directory-Level Access

Data rooms use a virtual file system (`DirectoryItems` entity) scoped to organizations:

| Column         | Type            | Purpose                   |
| -------------- | --------------- | ------------------------- |
| `organization` | UUID (PK)       | Organization scope        |
| `parentPath`   | string (PK)     | Parent directory path     |
| `name`         | string (PK)     | Item name                 |
| `file`         | UUID (nullable) | Reference to Files entity |
| `type`         | smallint        | File or folder            |

All file operations require the appropriate document permission (`viewDocuments`, `editDocuments`, `deleteDocuments`).

## 9. Global Roles

Source: `equa-server/modules/persistence/src/schema.ts` (GlobalRolesUsers entity)

For site-level administration, users can be assigned global roles that operate outside the organization context:

| Column | Type      | Purpose               |
| ------ | --------- | --------------------- |
| `user` | UUID (PK) | User with global role |
| `role` | int (PK)  | GlobalRole enum value |

Global roles grant site-wide privileges (e.g., managing all organizations, accessing admin panel). The `isSiteAdmin` check in the frontend route guard uses this.

## 10. Agent Permission Proxy

Source: `equa-server/modules/agent/src/security/permission-proxy.ts`

The AI agent (Equanaut) operates within the same permission model as human users. The permission proxy ensures agents can only execute tools that the owning user has permission for:

| Property   | Value                                                                                                                     |
| ---------- | ------------------------------------------------------------------------------------------------------------------------- |
| Cache TTL  | 60 seconds                                                                                                                |
| Resolution | SQL query joining permissions -> permissions\_roles -> members\_roles -> members                                          |
| Methods    | `checkToolPermission()`, `checkMultiplePermissions()`, `getUserPermissions()`, `hasOrganizationAccess()`, `getUserRole()` |

Each agent tool declares its required permissions. Before execution, the proxy verifies the user has the necessary permissions in the target organization.

### 10.1 Unauthenticated Chat Access (Guest Mode)

Source: `equa-web/src/modules/equanaut/services/api/chat/messages.ts`, `equa-web/src/modules/equanaut/index.tsx`, `equa-web/src/modules/equanaut/services/api/config.ts`

As of spec 042 (Guest Access to Equanaut Sidebar), the Equanaut regular-chat path is accessible to unauthenticated (guest) users. This path operates entirely outside the permission proxy described in Section 10.

| Property           | Value                                                                                                                                                                                                       |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Trigger            | `<EquanautToggle />` rendered in the guest header (`header.tsx`)                                                                                                                                            |
| Chat mode          | Regular chat only (`useChat`); agent mode (`useAgentChat`) requires an authenticated user with an organization                                                                                              |
| Endpoint           | `POST /equanaut-api/api/chat`                                                                                                                                                                               |
| Authentication     | None -- no `Authorization` header, no session cookie, no `credentials: 'include'`                                                                                                                           |
| Backend auth check | None -- the route handler processes the request without verifying identity                                                                                                                                  |
| Tool execution     | Not available -- the guest chat path never invokes the agent tool pipeline or the permission proxy                                                                                                          |
| Organization data  | Not accessible -- `userContext` is `undefined` for guests; the system prompt contains only static platform knowledge                                                                                        |
| Data exposure      | None -- responses are generated from the static Equanaut system prompt (platform feature descriptions, navigation help, terminology); no user data, organization data, or cap table information is returned |

**What guest chat can do:**

* Answer general questions about the Equa platform, features, and navigation
* Explain equity terminology (vesting, cap tables, option pools, etc.)
* Provide onboarding guidance for new visitors

**What guest chat cannot do:**

* Execute agent tools (blocked at the frontend; `useAgentChat` is never selected without a user and organization)
* Access organization data, member lists, documents, or cap tables
* Perform any write operations on the platform
* View or modify any authenticated user's information

**Rate limiting:**

Rate limiting for the unauthenticated chat endpoint is not yet implemented. This was scoped as tasks T004 and T005 in spec 042 and deferred to a future sprint. Until implemented, the endpoint is theoretically vulnerable to abuse (high-volume requests from unauthenticated clients). The practical risk is limited because the endpoint only returns AI-generated text with no data exposure, but cost and availability concerns apply.

<Note>
  This section requires security review sign-off. The unauthenticated endpoint should be re-evaluated when rate limiting (spec 042, T004/T005) is implemented.
</Note>

## 11. Access Control Gaps

<Warning>
  | Gap                                               | Risk                                                                                                            | Recommendation                                                            |
  | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
  | No session concurrency limit                      | A compromised account could have unlimited active sessions                                                      | Implement max active sessions per user with oldest-session eviction       |
  | No IP-based session binding                       | Sessions are not tied to originating IP                                                                         | Consider optional IP binding for high-security organizations              |
  | No permission audit log                           | Permission grants/revocations are not logged to the audit trail                                                 | Log all role assignment changes to EventLogs                              |
  | Shared roles have no approval workflow            | `isShared=true` roles are available across organizations without review                                         | Add approval workflow for shared role creation                            |
  | No time-limited role assignments                  | Roles are permanent until manually removed                                                                      | Support role expiration dates for temporary access (e.g., auditor access) |
  | No rate limiting on unauthenticated chat endpoint | Guest Equanaut chat (`/equanaut-api/api/chat`) has no request throttling; vulnerable to cost/availability abuse | Implement per-IP rate limiting (spec 042, tasks T004/T005)                |
</Warning>

## 12. Regulatory References

| Standard     | Requirement                                                        | Current Status                                                                  |
| ------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| SOC 2 CC6.1  | Logical access security over protected information assets          | **Implemented** -- RBAC with 14 permissions, role-based enforcement             |
| SOC 2 CC6.2  | Prior to issuing system credentials, registered and authorized     | **Implemented** -- email verification + optional 2FA required                   |
| SOC 2 CC6.3  | Access to protected information is removed when no longer required | **Partial** -- soft delete on members, but no automated access review           |
| GDPR Art. 25 | Data protection by design and by default                           | **Implemented** -- permission-based access, minimal default access (guest role) |
| GDPR Art. 32 | Appropriate technical measures for data security                   | **Implemented** -- RBAC, encryption (limited), session management               |

## 13. Revision History

| Date       | Version | Author                    | Changes                                                                                         |
| ---------- | ------- | ------------------------- | ----------------------------------------------------------------------------------------------- |
| 2026-02-21 | 0.1     | Agent (Phase 5 Session A) | Initial draft from code analysis                                                                |
| 2026-02-22 | 0.2     | Agent (Spec 042)          | Added Section 10.1 (Unauthenticated Chat Access) and rate-limiting gap; pending security review |
