Config Service — Security Model
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 13 security-compliance-tenancy · 14 compliance-security-extended
1. RBAC / ABAC Matrix
| Operation | Minimum Role | Additional Conditions |
|---|---|---|
GET /internal/config/resolve | Any service JWT (internal) | tenantId in JWT must match nodeId tenant |
GET /internal/config/ui | Any service JWT (internal) | tenantId must match |
GET /internal/config/tokens | Any service JWT (internal) | nodeId (if provided) must belong to tenantId |
POST /api/v1/config/modules/:key/features | TENANT_ADMIN | Tenant-scoped |
POST /api/v1/config/roles | TENANT_ADMIN (custom) / SUPER_ADMIN (system) | isSystem=true requires SUPER_ADMIN |
POST /api/v1/config/roles/:key/inheritance | SUPER_ADMIN | — |
POST /api/v1/config/roles/:key/feature-grants | TENANT_ADMIN | Tenant-scoped |
POST /api/v1/config/users/:userId/overrides | TENANT_ADMIN | Target user must be same tenant |
DELETE /api/v1/config/users/:userId/overrides/:id | TENANT_ADMIN | Same tenant check |
POST /api/v1/config/design-tokens (tenant/module/user scope) | TENANT_ADMIN | — |
POST /api/v1/config/design-tokens (global scope) | SUPER_ADMIN | — |
Create/modify GLOBAL ConfigNode | SUPER_ADMIN | — |
| Hard-delete any ConfigNode | Nobody | Returns 405 Method Not Allowed |
2. Tenant Isolation
| Control | Implementation |
|---|---|
| Row-Level Security | PostgreSQL RLS on all tables; tenant_id = current_setting('app.current_tenant_id') |
| JWT claim enforcement | tenantId extracted from JWT only; rejected if body/query provides a different value |
| Cross-tenant resolution | Returns { effect: "deny", reason: "CROSS_TENANT" } — never leaks data from another tenant |
| GLOBAL and system roles | Visible to all tenants (by design); cannot be mutated by TENANT_ADMIN |
| Outbox / inbox | Partitioned by tenant_id in payload; Relay validates before publishing |
3. Encryption
| Data class | Encryption | Notes |
|---|---|---|
| Config node payload (JSONB) | At rest: PostgreSQL transparent data encryption | No PHI stored here; config metadata only |
| Design token values | At rest: PostgreSQL TDE | Token values are non-sensitive presentation data |
| User override justification text | At rest: PostgreSQL TDE | May contain clinical rationale; classified as metadata |
| Redis cache values | In transit: TLS; at rest: encrypted Redis (per deployment policy) | Cache values may contain resolved permission results |
| Transit (all) | TLS 1.2+ enforced on all internal + external connections | — |
4. Audit Events
All mutations emit CloudEvents to NATS which audit-service persists with 7-year retention.
| Audit trigger | Event subject |
|---|---|
| Feature definition created/updated | config.feature.created.v1 / config.feature.updated.v1 |
| Role created/updated/deleted | config.role.*.v1 |
| Role feature grant created/updated | config.role_grant.*.v1 |
| UI definition created/updated | config.ui_definition.*.v1 |
| User node override created/deleted | config.user_override.*.v1 |
| Design token updated | config.design_token.updated.v1 |
| Resolution calls | 1 % sampled diagnostic events only (not individual audit records) |
Audit records include: actorId, tenantId, timestamp, action, targetId, before/after payload diff.
5. GDPR Participation
| Aspect | Notes |
|---|---|
| Personal data stored | user_id references and justification text in user_node_overrides |
| Data subject rights | Override records linked to user_id; deletion of a user triggers soft-delete of all their overrides |
| Retention | Audit records: 7 years (compliance minimum); config nodes: indefinite until tenant offboarded |
| Data residency | All data stored in tenant's designated region; RLS prevents cross-region leakage |
| Purpose limitation | Config data used only for resolution and audit; not shared with analytics or AI pipelines |
6. Security Hardening
| Control | Detail |
|---|---|
| No secrets in config | All secrets in env vars validated via Zod at startup |
| Rate limiting | Kong applies rate limits on /api/v1/config/*; internal endpoints rate-limited by service mesh |
| Input validation | Zod schemas on all DTOs; unknown fields rejected |
| Cycle detection | Role graph and config node DAG cycle detection at write time |
| Fail-closed on upstream failures | Resolution returns DENY when facility-service or access-policy is unavailable |
No synchronize: true in ORM | All schema changes via explicit migrations |
| OpenTelemetry trace propagation | All inter-service calls include W3C trace context |