一键导入
auth-rbac
Manage the Trinity AGI auth service and RBAC system -- database schema, role hierarchy, permission management, JWT flow, audit logging, and API endpoints.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Manage the Trinity AGI auth service and RBAC system -- database schema, role hierarchy, permission management, JWT flow, audit logging, and API endpoints.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Sync files between the host machine and the running Trinity Docker containers — config, agent files, skills, and extensions.
Develop the Trinity AGI Flutter Web Shell -- understand the architecture, A2UI renderer, WebSocket models, dual-client pattern, design tokens, and build workflow.
Operate the Trinity AGI Kubernetes infrastructure -- Helm charts, multi-tenant per-user OpenClaw pods, Vault Agent Injector secrets, minikube dev, and production deployment.
Operate the OpenClaw Gateway that powers Trinity AGI — configure providers, manage sessions, use the CLI, and understand the WebSocket protocol.
Manage the Trinity AGI Docker Compose stack — start, stop, logs, rebuild frontend, and full deploy.
Convert and extract text from PDFs, DOCX, images (OCR), and other document formats using the gateway's built-in document processing stack.
| name | auth-rbac |
| description | Manage the Trinity AGI auth service and RBAC system -- database schema, role hierarchy, permission management, JWT flow, audit logging, and API endpoints. |
| license | MIT |
| compatibility | opencode |
| metadata | {"audience":"developers","workflow":"trinity"} |
The auth-service and RBAC system that powers Trinity AGI's access control. Covers the PostgreSQL schema, role hierarchy, permission enforcement, JWT authentication flow, audit logging, and API endpoints.
Source: src/auth-service/, src/supabase/migrations/, src/rbac/
Browser -> GoTrue (email/password or Keycloak OIDC) -> JWT
-> auth-service /auth/me (Bearer JWT)
-> verifyToken middleware -> resolveRole middleware
-> DB: ensureUserRole() -> effective_permissions() recursive CTE
-> Response: {role, permissions[]}
-> Frontend stores in localStorage + Riverpod AuthState
-> GET /auth/openclaws -> orchestrator (3s timeout) -> enriched list
-> Auto-select first ready instance -> connect gateway WebSocket
Orchestrator dependency: The auth-service makes outbound HTTP calls to the gateway-orchestrator (ORCHESTRATOR_URL) for OpenClaw lifecycle operations. All orchestrator calls use AbortSignal.timeout() to prevent hangs:
fetchAssignedOpenClawsForUser(): 3s timeoutIf the orchestrator is down, /auth/openclaws will fail after 3s instead of hanging indefinitely.
Guest flow: POST /auth/guest issues a limited JWT (1hr TTL, role=guest, safe-tier permissions).
rbac schema in PostgreSQL)| Table | Key Columns | Purpose |
|---|---|---|
rbac.roles | id (UUID PK), name (unique), parent_id (self-FK), description | Role hierarchy |
rbac.permissions | id (UUID PK), action (unique), description | Permission definitions |
rbac.role_permissions | (role_id, permission_id) composite PK | Direct role-permission mapping |
rbac.user_roles | (user_id, role_id) composite PK, granted_by, granted_at | User-role assignment |
rbac.audit_log | id, user_id, action, resource, metadata (JSONB), ip, created_at | Audit trail |
Indexes on audit_log: user_id, action, created_at DESC.
| Function | Returns | Purpose |
|---|---|---|
rbac.effective_permissions(user_id) | TABLE(action TEXT) | Recursive CTE walks role hierarchy via parent_id, returns all inherited + direct permissions |
rbac.has_permission(user_id, action) | BOOLEAN | Thin wrapper around effective_permissions |
rbac.user_role_name(user_id) | TEXT | Returns highest-priority role (superadmin > admin > user > guest) |
superadmin (tier: privileged) -- 0 direct permissions, inherits all
-> admin (tier: privileged) -- 7 direct: skills.manage, terminal.exec.privileged, settings.admin, acp.manage, users.list, users.manage, audit.read
-> user (tier: standard) -- 7 direct: chat.send, memory.write, skills.install, crons.manage, terminal.exec.standard, governance.resolve, acp.spawn
-> guest (tier: safe) -- 8 direct: chat.read, canvas.view, memory.read, skills.list, crons.list, settings.read, governance.view, terminal.exec.safe
The recursive CTE walks UP the parent chain. Admin inherits user + guest = 22 total permissions. Superadmin inherits everything.
| File | Purpose |
|---|---|
001_rbac_schema.sql | Creates schema, tables, functions, indexes |
002_seed_roles.sql | Seeds 4 roles with parent hierarchy |
003_seed_permissions.sql | Seeds 22 permissions + role-permission bindings |
004_seed_default_admin.sql | Documentation: admin created via GoTrue at bootstrap |
src/auth-service/)Stack: Express.js, jsonwebtoken, pg, js-yaml
verifyToken -- Extracts Authorization: Bearer <token>, verifies JWT with JWT_SECRET. Missing token = guest.resolveRole -- Calls ensureUserRole(userId) then getEffectivePermissions(userId).requirePermission(action) -- Checks req.user.permissions.includes(action). 403 + audit log on denial.| Method | Path | Auth | Permission | Purpose |
|---|---|---|---|---|
| GET | /auth/health | none | none | Health check |
| GET | /auth/me | Bearer JWT | none | Current user info + permissions |
| GET | /auth/permissions | Bearer JWT | none | Flat permission list |
| POST | /auth/session | Bearer JWT | none | Exchange JWT for gateway session token |
| POST | /auth/guest | none | none | Issue guest JWT (1hr TTL) |
| GET | /auth/users | Bearer JWT | users.list | List all users with roles |
| POST | /auth/users/:id/role | Bearer JWT | users.manage | Assign role (guest/user/admin) |
| GET | /auth/users/audit | Bearer JWT | audit.read | Paginated audit log |
| GET | /auth/users/roles/permissions | Bearer JWT | users.list | Role-permission matrix |
| PUT | /auth/users/roles/:role/permissions | Bearer JWT | users.manage | Update role permissions |
| Method | Path | Auth | Permission | Purpose |
|---|---|---|---|---|
| GET | /auth/openclaws | Bearer JWT | none (non-guest) | List assigned OpenClaw instances (calls orchestrator, 3s timeout) |
| GET | /auth/openclaws/:id/status | Bearer JWT | access check | OpenClaw pod status |
| GET | /auth/openclaws/:id/config | Bearer JWT | access check | Get OpenClaw config (openclaw.json) |
| PATCH | /auth/openclaws/:id/config | Bearer JWT | access check | Update OpenClaw config |
| GET | /auth/openclaws/:id/delegation-token | Bearer JWT | access check | Get delegation token |
| POST | /auth/openclaws/create | Bearer JWT | admin+ | Create new OpenClaw instance |
| DELETE | /auth/openclaws/:id | Bearer JWT | admin+ | Delete OpenClaw instance |
| POST | /auth/openclaws/:id/assign | Bearer JWT | admin+ | Assign OpenClaw to user |
| POST | /auth/openclaws/:id/unassign | Bearer JWT | admin+ | Unassign OpenClaw from user |
| GET | /auth/openclaws/fleet/sessions | Bearer JWT | admin+ | Fleet-wide session overview |
| GET | /auth/openclaws/fleet/health | Bearer JWT | admin+ | Fleet-wide health overview |
Access check: user must own the OpenClaw (via orchestrator lookup) or be admin+.
| Function | Purpose |
|---|---|
getEffectivePermissions(userId) | Calls recursive CTE, returns action[] |
hasPermission(userId, action) | Boolean check via SQL function |
getUserRoleName(userId) | Highest-priority role name |
assignRole(userId, roleName, grantedBy) | DELETE all + INSERT (single-role enforcement) |
ensureRole(userId, roleName, grantedBy) | INSERT ON CONFLICT DO NOTHING (additive) |
ensureUserRole(userId, defaultRole) | Auto-assign default role on first login |
listUsers() | All users with role names |
getRolePermissionMatrix() | Full matrix for admin UI |
setRolePermissions(roleName, actions) | Transactional replace of role-permission bindings |
writeAuditLog(...) | Insert audit record |
getAuditLog(limit, offset) | Paginated audit query |
| Function | Purpose |
|---|---|
fetchAssignedOpenClawsForUser(userId) | Calls orchestrator GET /users/:id/openclaws with 3s AbortSignal.timeout |
assertOpenClawAccess(req, openclawId) | Verifies user owns the claw or is admin+; throws 401/403 |
Automatically logged: login.success, login.failed, permission.denied, auth.session.create, users.role.assign, role.assigned, role.ensured, permissions.updated
src/rbac/permissions.yaml)Three identical copies at: src/rbac/, src/auth-service/rbac/, src/terminal-proxy/rbac/
The YAML is the declarative specification. The DB is the runtime store. They encode the same information in different formats:
tier field = which role gets this permission (denormalized)role_permissions = many-to-many join rows (normalized)| Tier | Commands |
|---|---|
| safe | status, health, models, skills list [--json], crons/cron list [--json], cat MEMORY.md |
| standard | doctor, skills, cron, clawhub, sessions list, logs, channels, tools, memory, config get, config validate |
| privileged | doctor --fix, configure, onboard, dashboard, config set |
Matching algorithm: Most-specific (longest) prefix match wins across all tiers. doctor --fix matches privileged even though doctor is standard.
On auth-service startup (ensureDefaultSuperadmin()):
ENABLE_DEFAULT_SUPERADMIN envDEFAULT_SUPERADMIN_EMAIL / DEFAULT_SUPERADMIN_PASSWORDSUPERADMIN_ALLOWLIST for allowed user IDsensureRole(userId, 'superadmin') to assign all hierarchy roles| Variable | Default | Purpose |
|---|---|---|
| AUTH_SERVICE_PORT | 18791 | Express listen port |
| JWT_SECRET | (= SUPABASE_JWT_SECRET) | JWT verification secret |
| SUPABASE_AUTH_URL | http://supabase-auth:9999 | GoTrue API base URL |
| ENABLE_DEFAULT_SUPERADMIN | true | Bootstrap admin on startup |
| DEFAULT_SUPERADMIN_EMAIL | admin@trinity.work | Default admin email |
| DEFAULT_SUPERADMIN_PASSWORD | admin | Default admin password |
| SUPERADMIN_ALLOWLIST | (empty) | Comma-separated allowed superadmin IDs |
| POSTGRES_HOST | supabase-db | DB host |
| POSTGRES_DB | supabase | DB name |
| OPENCLAW_GATEWAY_TOKEN | (from .env) | For issuing gateway session tokens |
| ORCHESTRATOR_URL | http://gateway-orchestrator:18801 | Gateway orchestrator base URL (outbound calls) |
| ORCHESTRATOR_SERVICE_TOKEN | (from Vault) | Bearer token for orchestrator API calls |
These queries run against the local Docker Compose stack (trinity-supabase-db).
docker exec trinity-supabase-db psql -U postgres -d supabase -c \
"SELECT ur.user_id, r.name AS role, ur.granted_at FROM rbac.user_roles ur JOIN rbac.roles r ON r.id = ur.role_id ORDER BY ur.granted_at DESC;"
docker exec trinity-supabase-db psql -U postgres -d supabase -c \
"SELECT r.name, p.name AS parent FROM rbac.roles r LEFT JOIN rbac.roles p ON r.parent_id = p.id ORDER BY CASE r.name WHEN 'superadmin' THEN 0 WHEN 'admin' THEN 1 WHEN 'user' THEN 2 WHEN 'guest' THEN 3 END;"
docker exec trinity-supabase-db psql -U postgres -d supabase -c \
"SELECT r.name AS role, p.action AS permission FROM rbac.role_permissions rp JOIN rbac.roles r ON r.id = rp.role_id JOIN rbac.permissions p ON p.id = rp.permission_id ORDER BY CASE r.name WHEN 'guest' THEN 0 WHEN 'user' THEN 1 WHEN 'admin' THEN 2 WHEN 'superadmin' THEN 3 END, p.action;"
docker exec trinity-supabase-db psql -U postgres -d supabase -c \
"SELECT r.name AS role, COUNT(rp.permission_id) AS direct_perms FROM rbac.roles r LEFT JOIN rbac.role_permissions rp ON rp.role_id = r.id GROUP BY r.name ORDER BY CASE r.name WHEN 'guest' THEN 0 WHEN 'user' THEN 1 WHEN 'admin' THEN 2 WHEN 'superadmin' THEN 3 END;"
docker exec trinity-supabase-db psql -U postgres -d supabase -c \
"SELECT created_at, action, resource, ip FROM rbac.audit_log ORDER BY created_at DESC LIMIT 10;"
cd src/terminal-proxy && npx jest rbac-registry.test.js --no-cache 2>&1
cd src/auth-service && npx jest rbac-registry.test.js --no-cache 2>&1
Add a new permission:
src/supabase/migrations/003_seed_permissions.sqlsrc/rbac/permissions.yaml (all 3 copies)src/frontend/lib/core/rbac_constants.dartChange role hierarchy:
Modify parent_id in 002_seed_roles.sql. The recursive CTE automatically handles inheritance.
Check effective permissions for a user:
SELECT * FROM rbac.effective_permissions('user-uuid-here');