Permission System Architecture

Overview

The permission system enables gateway modules to describe their authorization requirements and exposes REST endpoints for reading and updating user permissions. This document explains the format, registry, backend endpoints, and frontend integration.


Permission Format

  • Pattern: {module}:[{resource-path}]:{action}
  • Resource Path: {resource-type}:{resource-id|*}(/{resource-type}:{resource-id|*})*
  • Module Name: Lowercase alphanumeric with hyphens ([a-z0-9-]+)
  • Action: Lowercase alphanumeric with hyphens
  • Examples:
  • user-containers:[user-container:*]:create
  • user-containers:[user-container:{uuid}]:delete
  • user-containers:[user-container:{uuid}/service:*]:read
  • gateway:[permissions:*]:write

Characteristics:
- Modules define permissions using * for resource IDs to describe capabilities.
- When granting to users, permissions can remain wildcarded or be specialized with specific IDs.
- Format supports hierarchical trees of resources separated by /.


Permission Registry

Purpose

  • Allows gateway modules to register their permission definitions during module loading.
  • Produces a compiled catalog that can be exposed via REST APIs.
  • Ensures consistent metadata (resource path, human-readable description).

Implementation

  • Located at packages/modules/gateway/src/lib/permission-registry.ts.
  • Maintains an in-memory Map keyed by the full permission string.
  • Validates format using the pattern above.
  • Provides accessors:
  • getAll() — All registered permissions (used by /permissions endpoint).
  • getByModule(module) — Filtered view for module-specific tooling.
  • get(permission) — Lookup by full string.

Usage by Modules

  • Each gateway module receives depsExports.gateway.permissionRegistry.
  • During load(), modules register permissions describing their features.
  • Example metadata per permission:
  • resourcePath — e.g., user-container:*
  • action — e.g., create
  • description — Human-friendly label shown in UIs

Gateway API Endpoints

1. GET /permissions

  • Purpose: List all compiled permission definitions.
  • Auth: gateway:[permissions:*]:read
  • Response: { permissions: PermissionDefinition[] }
  • Use Case: Populate dropdowns or documentation of available permissions.

2. GET /permissions/projects/{project_id}

  • Purpose: Retrieve user permissions for a specific project.
  • Auth: gateway:[permissions:*]:read + project access check.
  • Response: { permissions: { [user_id: string]: string[] } }
  • Use Case: Display collaborator permissions in the frontend.

3. PATCH /permissions/projects/{project_id}/users/{user_id}

  • Purpose: Update permissions assigned to a user within a project.
  • Auth: gateway:[permissions:*]:write + project access check.
  • Body: { permissions: string[] }
  • Response: { success: true }
  • Use Case: Grant/revoke permissions from the frontend.

All routes are defined in packages/app-gateway/src/routes/permissions.ts and documented in packages/app-gateway/src/oas30.json.


Frontend Integration

Hooks (from frontend-data)

  • useQueryScope(organization_id) — Loads all permissions via gateway.
  • useQueryProjectUsersScopes(organization_id, project_id) — Retrieves assignments per user.
  • useCollaborators(project_id) — Combines Ganymede project members with gateway permissions.
  • useMutationUserScope(project_id) — Updates user permissions and invalidates caches.

Workflow

  1. OrganizationContext ensures gateway hostname is set for fetchGateway().
  2. Hooks call Gateway endpoints via ganymedeApi.fetchGateway.
  3. UI components render lists of permissions and allow modifications.
  4. Mutations invalidate relevant React Query keys to keep UI consistent.

Availability Requirements

  • Gateway must be running (OrganizationContext handles availability states).
  • User must have gateway:[permissions:*]:read to view, ...:write to modify.

Best Practices

  1. Module Authors:
    - Register permissions during module load to ensure they appear in /permissions.
    - Provide descriptive metadata to help frontend users understand capabilities.
    - Use hierarchical resource paths to enable fine-grained future grants.

  2. Frontend Developers:
    - Treat permissions as opaque strings; rely on metadata for labels.
    - Use provided hooks instead of manual fetches to benefit from caching.
    - Handle loading and unavailable states when gateway is down.

  3. Backend Developers:
    - Use PermissionRegistry for any new module.
    - Protect routes with requirePermission middleware referencing the same strings.
    - Keep OpenAPI specs aligned with endpoint behavior for autogenerated docs.


  • doc/architecture/FRONTEND_ARCHITECTURE.md
  • packages/modules/gateway/src/lib/permission-registry.ts
  • packages/app-gateway/src/routes/permissions.ts
  • packages/frontend-data/src/lib/queries.ts