Scopes & Policies

Role-based access control for memories.

Cortex uses a scope-based permission system. Every memory belongs to a scope, and every user has a role that determines which scopes they can access.

Scopes

A scope is a permission boundary. Common examples:

ScopeDescription
engineeringTechnical decisions, API docs, architecture
public-docsPublic-facing documentation
financeFinancial data, metrics, budgets
hrHR policies, team structure
secretConfidential information

Roles (designations)

Members carry a designation that maps to a default clearance level and a starting set of scope access. The workspace owner can change any member's designation and edit the scope matrix at any time.

DesignationDefault clearanceDescription
CEOSeniorWorkspace owner — full access + admin (the single owner)
FounderSeniorCo-founder — full memory access like the CEO
CxOSeniorSenior executive — full memory access
ManagerSeniorAccess to their team's scopes
EmployeeJuniorAccess to their team + public scopes (stored internally as IC)
InternContractorPublic scopes only (stored internally as Contractor)

Employee and Intern are the human-readable labels for the internal role keys IC and Contractor. Only the CEO is the workspace owner for destructive/admin actions; Founder and CxO are senior-clearance but not owners.

Custom designations

Beyond the built-ins, the CEO can define custom designations (e.g. "VP Engineering", "Head of Sales") from the Team page — each with a chosen default clearance (Senior / Junior / Restricted). A new designation is immediately assignable and is seeded with starting scope access matched to its clearance, so a member given the title has real access from the start; the CEO can then fine-tune its scopes in the Policy matrix like any built-in role.

Clearance Levels

Each user also has a clearance level that determines access to content:

LevelRankDescription
Contractor1Basic access
Junior2Standard access
Senior3Extended access
Staff4Broad access
Principal5Near-full access
CEO6Full access + admin

Policy Matrix

Policies map roles to scopes with an allow/deny effect:

graph LR
    subgraph "Policy Matrix"
        R1[Role: CEO] --> S1[finance: allow]
        R1 --> S2[engineering: allow]
        R1 --> S3[secret: allow]
        
        R2[Role: IC] --> S4[engineering: allow]
        R2 --> S5[public-docs: allow]
        R2 --> S6[finance: deny]
    end

Resolution Rules

  1. Deny beats allow — If a role has both allow and deny for the same scope, deny wins
  2. No implicit access — Roles only access scopes explicitly granted
  3. Level check — Even with scope access, clearance level must match

Example Policy Configuration

{
  "policies": [
    { "role": "CEO", "scope_key": "finance", "effect": "allow" },
    { "role": "CEO", "scope_key": "engineering", "effect": "allow" },
    { "role": "CEO", "scope_key": "secret", "effect": "allow" },
    { "role": "Manager", "scope_key": "engineering", "effect": "allow" },
    { "role": "Manager", "scope_key": "finance", "effect": "allow" },
    { "role": "IC", "scope_key": "engineering", "effect": "allow" },
    { "role": "IC", "scope_key": "public-docs", "effect": "allow" },
    { "role": "IC", "scope_key": "finance", "effect": "deny" },
    { "role": "Contractor", "scope_key": "engineering", "effect": "allow" }
  ]
}

Permission Flow

sequenceDiagram
    participant Caller
    participant Policy as Policy Engine
    participant DB as Database

    Caller->>Policy: resolveScopes(user)
    Policy->>DB: Load policy matrix for org
    DB-->>Policy: Policy rows
    Policy->>Policy: computeScopes(role, rows)
    Policy-->>Caller: Allowed scopes
    
    Note over Caller,DB: For each memory...
    
    Caller->>Policy: canRecallPure(user, memory, scopes)
    Policy->>Policy: Check: org match
    Policy->>Policy: Check: status = active
    Policy->>Policy: Check: scope in allowed
    Policy->>Policy: Check: clearance level
    Policy->>Policy: Check: not expired
    Policy-->>Caller: allowed / denied

Denials

Denials are explicit and logged. When a recall is denied:

{
  "allowed": false,
  "reason": "No memories cleared for your role within scopes [engineering]. Content may exist in scopes you are not cleared for.",
  "allowed_scopes": ["public-docs"],
  "audit_id": "uuid"
}

Denials are never silent. Every denied access is logged to the audit trail with full context.