Skip to content

PBAC Onboarding Guide

This document explains how access control works in practical terms. It covers how requests are evaluated, how company and project permissions work together, how visibility and quotas are enforced, and how policy changes are tracked.

How a Request Flows Through the System

When a user makes an API call, here is what happens:

sequenceDiagram participant User participant Gateway participant PolicyVault participant Service User->>Gateway: API Request + JWT Gateway->>Gateway: Validate JWT Gateway->>PolicyVault: Check Access PolicyVault-->>Gateway: Allow / Deny Gateway->>Service: Forward Request (if allowed) Service-->>Gateway: Response Gateway-->>User: Final Response

Responsibility Model

  • Gateway → Enforces decisions
  • PolicyVault → Makes decisions
  • Services → Execute business logic only

Services must not independently decide authorization.


3. The Access Hierarchy

Everything in the system follows a strict structure.

--- config: layout: elk --- graph LR Tenant --> Company Company --> Project Project --> Resource

Tenant

Derived from the Keycloak realm. Provides complete isolation between customers.

Company

Represents an organization. Governs users, projects, and company-level quotas.

Project

A workspace inside a company (or personal). Contains resources and members.

Resource

Files, folders, templates --- any object requiring access control.

Every access decision walks down this hierarchy.


4. Company-Level Access

Company scope answers:

"Are you allowed to operate inside this organization?"

Available Scopes

  • Owner
  • Admin
  • Editor
  • Viewer
  • Member

What Company Scope Controls

  • Managing company users
  • Managing company projects
  • Managing company quotas
  • Organization-level settings

If the user is not a company member, access is denied immediately.


5. Project-Level Access

Project role answers:

"What are you allowed to do inside this workspace?"

Available Roles

  • Owner
  • Admin
  • Contributor
  • Viewer
  • Custom

What Project Role Controls

  • Viewing project resources
  • Creating or editing resources
  • Managing project users
  • Managing project quotas
  • Sharing resources

For company projects, access requires both company and project approval:

--- config: layout: elk --- flowchart LR CompanyCheck --> ProjectCheck --> Decision

For personal projects, only the project check applies.


6. Resource Visibility and Sharing

Passing project access does not guarantee visibility of all resources.

Resources can be shared in two ways:

  • Anyone → visible to all project members
  • Personal → visible only to specific users + project owner

Visibility Evaluation

--- config: layout: elk --- flowchart TD ProjectAccess --> CheckSharingScope CheckSharingScope -->|Anyone| Visible CheckSharingScope -->|Personal & Listed| Visible CheckSharingScope -->|Personal & Not Listed| Hidden

Folder Inheritance

If a folder is shared, all child resources inherit its visibility.

Important principle:

  • Roles grant ability
  • Sharing grants visibility

Both must pass.


7. Quota Enforcement

Authorization includes usage limits.

Supported Quota Types

  • Compute Credits
  • Storage Size
  • API Calls
  • Custom Types

Before resource consumption, the system evaluates:

--- config: layout: elk --- flowchart TD Request --> CheckCompanyRemainingResources CheckCompanyRemainingResources --> CheckProjectRemainingResources CheckProjectRemainingResources -->|Enough Capacity| Allow CheckProjectRemainingResources -->|Exceeded| Deny

If either company or project capacity is exceeded, the request fails with AccessLimitExceeded.

Quota validation is mandatory before resource allocation.


8. Core Use Cases

8.1 User Requests a Model

  1. User sends request with JWT
  2. Gateway validates JWT
  3. Gateway calls PolicyVault
  4. Company → Project → Resource checks
  5. If allowed, request proceeds
--- config: layout: elk --- sequenceDiagram autonumber participant U as User participant G as API Gateway participant PV as PolicyVault participant MR as Model Registry U->>G: GET /projects/{projectId}/models/{modelId} (JWT) G->>G: Validate JWT + extract tenantId,userId G->>PV: CheckResourceAccess(tenantId,userId,projectId,modelResource,Read) PV->>PV: Company check (if company project) PV->>PV: Project role check PV->>PV: Resource visibility filter (Anyone/Personal + folder inheritance) PV-->>G: Allow or Deny (reason) alt Allowed G->>MR: Forward request MR-->>G: Model payload G-->>U: 200 OK else Denied G-->>U: 403/401 + error code end

8.2 Admin Adds User to Company

  1. Admin submits request
  2. Gateway verifies Admin scope
  3. PolicyVault updates User and Company atomically
  4. Events are recorded
  5. Versions increment
sequenceDiagram autonumber participant A as Admin User participant G as API Gateway participant PV as PolicyVault participant ES as Event Store participant PR as Projections A->>G: POST /companies/{companyId}/users (JWT) G->>PV: CheckCompanyAccess(tenantId,adminUserId,companyId,Admin) PV-->>G: Allow G->>PV: AddUserToCompany(companyId,targetUserId,scope) PV->>PV: Validate command idempotency + business rules PV->>ES: Append events (CompanyUserAdded + UserCompanyAdded) ES-->>PV: Ack + new versions PV->>PR: Update read models (memberships/scopes) PV-->>G: Success G-->>A: 200 OK

8.3 User Shares a Resource

  1. Project Admin sets resource to Personal scope
  2. PolicyVault updates sharing configuration
  3. Events are recorded
  4. Only listed users can access the resource; others do not see it
sequenceDiagram autonumber participant U as Project Admin/Owner participant G as API Gateway participant PV as PolicyVault participant ES as Event Store participant PR as Projections U->>G: POST /projects/{projectId}/share (JWT) G->>PV: CheckProjectAccess(tenantId,userId,projectId,Admin) PV-->>G: Allow G->>PV: ShareResource(projectId,resourceKey,Personal,[userIds]) PV->>PV: Apply sharing rules + validate resourceKey PV->>ES: Append events (ResourceShared/ScopeUpdated) ES-->>PV: Ack + new project version PV->>PR: Update sharing index (resource -> scope + allowlist) PV-->>G: Success G-->>U: 200 OK

8.4 Quota Check Before Resource Allocation

  1. Service requests quota validation
  2. PolicyVault calculates remaining capacity
  3. If sufficient → allow
  4. If insufficient → deny
sequenceDiagram autonumber participant S as Service (e.g., Generation) participant PV as PolicyVault participant PR as Usage Projections S->>PV: CheckQuota(tenantId,userId,companyId,projectId,Credit,amount) PV->>PR: Read company usage + limits PV->>PR: Read project usage + limits PV->>PV: Compute remaining capacities alt Enough capacity PV-->>S: Allow + remaining else Exceeded PV-->>S: Deny (AccessLimitExceeded) + remaining end

8.5 Convert Personal Project to Company Project

  1. Company Admin initiates conversion
  2. PolicyVault verifies authority
  3. Project is linked to company
  4. Quotas initialized
  5. Events emitted
sequenceDiagram autonumber participant A as Company Admin participant G as API Gateway participant PV as PolicyVault participant ES as Event Store participant PR as Projections A->>G: POST /projects/{projectId}/convert?companyId={companyId} (JWT) G->>PV: CheckCompanyAccess(tenantId,adminUserId,companyId,Admin) PV-->>G: Allow G->>PV: ConvertProjectType(projectId,Company,companyId) PV->>PV: Set CompanyId + initialize limits from company defaults PV->>ES: Append events (ProjectTypeConverted + CompanyProjectAdded) ES-->>PV: Ack + new versions PV->>PR: Update projections (project->company link, quotas initialized) PV-->>G: Success G-->>A: 200 OK

9. Event Sourcing and Audit

Every significant change in the policy system—such as adding a user, modifying a role, sharing a resource, or updating quotas—is recorded as an immutable event, creating a complete and authoritative history. Instead of storing only the latest state, the system preserves the full sequence of changes, allowing the current state to be reconstructed by replaying that history.

flowchart LR Command["Command (Intent)"] Event["Event (Fact)"] Version["Aggregate Version Update"] Projection["Read Model Update"] Command --> Event Event --> Version Event --> Projection
  1. A Command is issued

    Someone intends to change something. For example:

    • Add user to company
    • Change project role
    • Share resource
    • Update quota
  2. An Event is produced

    If the command is valid, the system records what happened as an event:

    • CompanyUserAdded
    • ProjectRoleChanged
    • ResourceShared
    • QuotaLimitUpdated
  3. The Aggregate Version is incremented

    Each company, project, or user maintains a version number. Each new event increases this version. This ensures secure concurrency and avoids conflicting updates.

  4. Projections update read models

    Background processes consume events and optimize read models. PolicyVault uses these read models for fast access checking.

Every change to policy is recorded as an event, so we always know what changed, who changed it, and when. Since the current state is built from those events, we can rebuild it at any time if needed. Version numbers prevent conflicts when multiple updates happen at once, and commands are designed to reject duplicate or invalid changes. Together, this gives us a clear audit trail, protects against race conditions, and allows us to confidently explain why a specific access decision was made.


10. Multi-Tenancy and Isolation

Tenant ID is extracted from the JWT.

All entities are tenant-scoped:

  • Users
  • Companies
  • Projects
  • Resources
  • Events

No cross-tenant access is permitted.

All queries must include tenant context.


11. Internal Service Security

  • Even internal services authenticate using OAuth2 Client Credentials.
  • There is no implicit trust inside the cluster.
  • Defense-in-depth applies at every layer.

12. Engineering Rules

  • Never bypass PolicyVault for authorization decisions.
  • Never allow services to infer permissions.
  • Always scope by tenant.
  • Never assume project membership implies visibility.
  • Always check quotas before resource consumption.
  • Never mutate policy state without emitting events.