Back to RegistryFuelVM Access Matrix v1
Purpose
Define a consistent dual-plane access model for all non-WordPress tools:
- Internal plane (tool-to-tool, trusted automation)
- External plane (customer/integrator access)
Default policy: deny by default. Access is granted only via explicit scopes.
1) Planes and Trust Boundaries
Internal Plane
- For service-to-service automation inside FuelVM
- Can include high-impact operations (write/delete/admin)
- Uses machine identities (service tokens), never user API keys
- Higher limits and broader data visibility
External Plane
- For customer/integrator API access
- Whitelisted operations only
- Read-heavy + safe writes only
- Strong quotas, redaction, and strict tenancy checks
2) Scope Model (Capabilities)
Scopes are granular permissions attached to tokens.
Core scopes:
reports.read
reports.generate
reports.export
sites.read
sites.update
connectors.read
connectors.run
admin.connectors.write
admin.system.full (internal only)
audit.read
Rules:
- Tokens can only call endpoints mapped to one of their scopes.
- Scopes are additive.
admin.system.full is forbidden for external tokens.
3) Token Classes
- internal_service: for app-to-app calls, short-lived preferred
- external_api_key: for customer/integrator use, revocable and scoped
- operator_admin: human operator/admin actions (MFA required)
Required token metadata:
token_id
tenant_id (required for external)
plane (internal | external)
scopes[]
created_at, expires_at
last_used_at
status (active | suspended | revoked)
4) Endpoint Permission Matrix Template
Use this table in every app:
| Endpoint | Method | Internal Scopes | External Scopes | External Allowed? | Notes |
|---|---|---|---|---|---|
| /api/v1/reports | GET | reports.read | reports.read | Yes | Tenant-filtered results only |
| /api/v1/reports/generate | POST | reports.generate | reports.generate (optional) | Conditional | External rate-limited hard |
| /api/v1/reports/:id/export | POST | reports.export | reports.export | Yes | Watermark/branding rules apply |
| /api/v1/connectors/sync | POST | connectors.run | — | No | Internal only |
| /api/v1/connectors/config | PUT | admin.connectors.write | — | No | Internal only |
| /api/v1/admin/system | POST | admin.system.full | — | No | Break-glass only |
| /api/health | GET | any valid token | optional public | Yes | No sensitive internals in payload |
5) Security Controls (Mandatory)
-
Deny-by-default router
- Unmapped endpoints return 403.
-
Tenant isolation
- External token may access only matching
tenant_id data.
-
Field-level redaction
- External responses remove secrets/internal diagnostics.
-
Rate limits by plane
- Internal: high, burst-tolerant
- External: strict per-token + per-IP
-
Key hygiene
- Rotation support
- Immediate revoke + kill switch
- Expiration defaults for external keys
-
Audit logging
- Every privileged call logs
token_id, scope used, actor, endpoint, outcome.
-
Admin protection
- MFA + step-up verification for operator admin actions.
6) External Operation Policy
Default external policy:
- Allowed: read reports, generate scoped report jobs, export reports
- Disallowed: connector credentials management, system/admin controls, cross-tenant access
For any external write operation, require:
- explicit idempotency key
- strict input validation
- bounded execution time
- audit trail entry
7) Rollout Plan (All Non-WP Apps)
- Add
plane, scopes, tenant_id to auth middleware
- Implement endpoint-to-scope mapping table
- Enforce deny-by-default + tenant checks
- Add rate-limit profiles for internal/external
- Add audit logs + revoke flow
- Publish app-specific permission matrix
Priority order:
seo-report-app
brand-radar
wp-engine-dashboard
8) Go/No-Go Checklist
Go live only if all are true:
- [ ] Endpoint matrix complete and reviewed
- [ ] No external access to admin/system endpoints
- [ ] Tenant isolation tests passing
- [ ] Rate limits verified under load
- [ ] Audit logs queryable by token_id and tenant_id
- [ ] Key revoke works immediately
9) Non-Negotiables
- No shared admin keys between internal and external planes
- No wildcard external scopes
- No silent fallback from denied to allowed
- No cross-tenant read paths, ever