ADR-039 — No hardcoded identities (users / roles / permissions)¶
Status: PROPOSED — implementation Sprint 56 Date: 2026-04-27 Sprint: 56 (planned) Supersedes: relaxes the "demo personas OK in chart" implicit pattern
Context¶
AKKO is a platform delivered to clients. Each client has :
- Their own Active Directory / LDAP / IdP (Azure AD, Okta, Auth0,
Keycloak self-hosted, OpenLDAP, 389-DS, ...)
- Their own users identified by matricules / employee IDs / corporate
emails (e.g. M123456, john.doe@acme.com, jdupont)
- Their own roles / groups (e.g. AD-DataPlatform-Admins,
service-marketing, dir-finance)
- Their own permission policies (per regulatory framework,
per business unit, per data sensitivity)
The current Helm chart contains hardcoded references to demo personas
(alice, bob, carol, dave, eve) and to a fixed role taxonomy
(akko-admin, akko-engineer, akko-analyst, akko-steward,
akko-viewer). These hardcoded values appear in 12 files for personas
(37 hits) and 10 files for roles (39 hits) — see audit table below.
Founder feedback 2026-04-27 (3 successive messages of the same day) :
-
"Dans la vraie vie, le client va avoir son LDAP... il faut rien hardcoder, ni les users, ni les rôles, ni les droits, ni les autorisations."
-
"Je ne veux pas cette règle spéciale [demo hardcoding OK]. Je veux que le point de vérité soit l'active directory du client, ici notre exemple c'est lldap."
The "demo OK to hardcode" escape hatch is rejected. Even on demo.akko-ai.com the source of truth must be LLDAP (which substitutes for the client's AD in the showcase context).
Decision¶
No hardcoded identities anywhere in the chart-by-default surface. The single source of truth for identities is the customer's AD (or LLDAP on demo). All apps consume identities via Keycloak User Federation → JWT claims, never via init seed scripts that write into their internal DBs.
What "identity" means here¶
- User names (matricules) :
alice,M123456,john.doe@acme.com - Roles / groups :
akko-admin,service-marketing,AD-DataPlatform-Admins - Permissions : column mask, row filter, table grant, dataset access
- Email domain : never used as a primary key, only metadata
Scope of this ADR¶
In scope (must be Helm-templated, default empty/disabled) :
- Init scripts that seed humain users in apps DB (OM, Superset,
Airflow, OPA)
- initialAdmins lists in app configs
- groupRoleMapping for OPA / cockpit / catalog manager
- principalDomain filters
- Hardcoded usernames in OPA Rego policies
- Default secrets / passwords for demo personas
Out of scope (system accounts may stay hardcoded) :
- Service accounts with prefix svc- or suffix -bot
(e.g. svc-aden, ingestion-bot, service-account-akko-aden-bot)
- These are AKKO-internal and don't represent client humans
- Documented as system-only
Chart-default behaviour after this ADR¶
# helm/akko/values.yaml (default)
global:
initialAdmins: [] # empty by default
groupRoleMapping: {} # empty by default
demo:
enabled: false # the akko-demo sub-chart that seeds
# alice/bob/... in LLDAP only
akko-lldap:
enabled: false # the client has their own AD
# helm/examples/values-dev.yaml (demo configuration)
demo:
enabled: true # personas seed only into LLDAP
akko-lldap:
enabled: true # LLDAP acts as AD-substitute on demo
global:
initialAdmins: [admin, alice] # demo admins, observable via JWT
groupRoleMapping:
"akko-admin": "akko-admin"
# ...
# helm/examples/values-customer-template.yaml (Sprint 56 deliverable)
global:
initialAdmins: [] # the client fills in their matricules
groupRoleMapping:
"<THEIR_AD_GROUP_ADMINS>": "akko-admin"
# ...
demo:
enabled: false
akko-lldap:
enabled: false
Consequences¶
Migration cost¶
Audited surface (Sprint 54 close-out 2026-04-27) : - 12 files contain persona names in chart-by-default (37 hits) - 10 files contain hardcoded role names (39 hits)
See Sprint 56 plan for day-by-day breakdown (8 days).
CI enforcement¶
Three new baseline assertions added in Sprint 56 :
1. test_no_hardcoded_personas_in_chart_default — grep chart-default
for alice/bob/carol/dave/eve → must return 0 hits outside demo.*
sub-chart
2. test_chart_default_has_zero_seed_users_in_apps — helm template
default values must produce 0 Job that creates seed users in OM /
Superset / Airflow / OPA
3. test_groupRoleMapping_is_value_driven — chart accepts arbitrary
strings as group names and role names (no string equality with
hardcoded constants)
Customer onboarding¶
A customer onboarding playbook (docs/admin/customer-onboarding.md)
is delivered Sprint 56. New clients can deploy AKKO without any seed
data leak from our demo cluster. Their first login = their AD identity,
no user seeded in our chart.
Demo continuity¶
The akko-demo sub-chart (Sprint 56 deliverable) keeps the showcase working : alice/bob/carol/dave/eve are seeded in LLDAP, then propagated to all apps via Keycloak User Federation + JWT. From the apps' point of view, they're indistinguishable from real client users — exactly what we want for showcasing.
Rollback¶
If Sprint 56 hits an unmovable blocker (e.g. OpenMetadata 1.12 refuses
empty initialAdmins) :
1. Document the blocker in ADR-040
2. Adopt a less-strict pattern (e.g. always seed an akko-system-admin
bot user that's not a human)
3. Plan SCIM 2.0 implementation immediately (Sprint 57+)
Comparison with industry¶
| Vendor | Identity source | Hardcoded users in chart? |
|---|---|---|
| Dremio Cloud | SCIM 2.0 from any IdP | None |
| DataHub | OIDC sub primary, JIT-create | None |
| Snowflake | SCIM connector | None |
| Trino official Helm | empty defaults, customer-driven | None |
| AKKO today (pre-ADR-039) | LLDAP seed + hardcoded refs | 12 files / 76 hits |
| AKKO post-ADR-039 | LLDAP/AD via JWT, JIT-create | 0 (only system bots) |
Implementation¶
See akko-technical-map/sprints/detailed/sprint-56-customer-onboarding-no-hardcoding.md.
Related¶
- ADR-038 : OIDC sub-based user matching (Sprint 55) — sister ADR
feedback_no_hardcoded_users_roles_permissions.md(memory rule)feedback_oidc_sub_is_stable_id.md(memory rule)project_akko_4_architectures_workflows.md(operational cadre)