Decision 0017 — Tenant Runtime Authority¶
- Status:
ACCEPTED - Authority:
RATIONALE_ONLY - Date:
2026-08-25 - Source baseline:
qa@f43fa88678e35427008ff6f8a2b804c7b6f5c495 - Requires:
0016-six-phase-delivery-and-tenant-productization.md - Current Roadmap:
../ROADMAP.md - Active implementation handoff:
../tasks/NS2-012.md
Context¶
Phase 2 cannot resume safely while tenant operating behavior is assembled from unrelated compatibility signals.
Current source confirms:
- the public session emits
profileId: "foundation"rather than a persisted operating-profile assignment; - session capabilities contain platform-administration booleans but not business capability grants;
- a missing
T_TENANT_MODULErow is treated as enabled; - route and page access still use menu URLs as part of business authorization;
@ctc/policyhas capability and surface primitives but no complete tenant Runtime composition;- the Admin Product surface exposes role, tenant, module, config and credential actions for which the
same-origin Platform route has no handler and can return
501 NOT_IMPLEMENTED; - scalar configuration helpers and current Foundation settings do not retain complete definition, source, validity, fallback and dependency diagnostics.
The required operator result is:
The same application and domain command executes for at least two semantic operating profiles from one persisted, server-resolved effective tenant context, without a customer ID, customer name or menu-substring branch.
The decision must provide this immediate Phase-1/Phase-2 consumer without pulling the full versioned Control Plane, third-tenant onboarding and support-console lifecycle forward from Phase 5.
Decision Drivers¶
- one tenant-neutral authority for profile, module, capability, typed config and effective surfaces;
- no new package unless existing semantic owners cannot deliver the result;
- menu visibility must not grant a business command;
- existing Legacy fail-open behavior must remain explicit and caller-bounded during migration;
- record/state authorization remains in the owning business module;
- current MySQL remains authoritative and numeric IDs remain adapter-internal;
- Phase-2 applied-state administration and Phase-5 release management remain separate;
- every mutation used by an exposed Admin surface must have authorization, conflict behavior, authoritative reread and safe diagnostics.
Alternatives¶
Alternative 1 — Repair Current Session, Menu And Config Behavior In Place¶
Patch the hard-coded profile, add missing route handlers and continue resolving each concern directly inside the Web session/route code and current Legacy adapters.
| Measure | Result |
|---|---|
| New packages | 0 |
| New public export paths | 0 |
| Expected production files | approximately 4–7 |
| Expected dependency edges | 0 |
| Migration risk | low initially, high cumulative |
| Removal cost | high because authority remains distributed |
| Immediate consumer | Admin repair only |
| Verification | route tests, but no single resolver contract |
Advantages
- smallest immediate diff;
- repairs visible
501defects quickly; - no package or dependency change.
Disadvantages
- preserves split authority across session, menu, route and config helpers;
- encourages another Web-owned tenant policy layer;
- cannot provide one diagnosable result to Booking, Transload and Phase-2 commands;
- makes later removal of menu-derived and tenant-derived behavior harder.
Disposition: rejected. It fixes symptoms but not the authority defect that blocks Phase 2.
Alternative 2 — Compose Existing Platform Owners¶
Use the existing package boundaries and composition root:
db-mysql observations and applied-state repositories
-> policy definitions and pure tenant Runtime resolution
-> identity-access session/principal envelope
-> platform-administration applied-state use-case contracts
-> Web composition root
-> route/action/surface consumers
| Measure | Result |
|---|---|
| New packages | 0 |
| New public export paths | 0 |
| Expected production files | approximately 8–12 across bounded Tasks |
| Expected root named exports | bounded additions to existing policy and platform-administration roots |
| Expected dependency edges | at most 1: Web -> platform-administration |
| Migration risk | medium and caller-bounded |
| Removal cost | low; compatibility observations are replaceable adapters |
| Immediate consumers | Admin, Booking/Transload acceptance and Phase-2 verticals |
| Verification | pure decision tables plus authenticated route/readback journeys |
Advantages
- matches the accepted Module Registry;
- keeps policy pure and persistence adapter-owned;
- supplies one result to multiple immediate consumers;
- avoids a generic tenant-runtime service or package;
- allows Legacy compatibility to be measured and removed caller by caller.
Disadvantages
- requires coordinated changes across existing owners;
- needs strict rules preventing
policyfrom becoming a loader or business-record owner; - temporarily retains compatibility aliases and numeric DB mappings;
- adds a bounded Web dependency on the existing administration contract package.
Disposition: accepted.
Alternative 3 — Add A New Tenant Runtime Package Or Service¶
Introduce a new @ctc/tenant-runtime package or separate runtime service that owns loading,
composition, caching and administration.
| Measure | Result |
|---|---|
| New packages | at least 1 |
| New public export paths | at least 1 |
| Expected production files | approximately 12–20 before consumers |
| Expected dependency edges | multiple platform and adapter edges |
| Migration risk | high |
| Removal cost | high |
| Immediate consumer | overlaps existing policy and administration owners |
| Verification | new boundary and integration suite required |
Advantages
- one obvious import location;
- could encapsulate caching and loading.
Disadvantages
- duplicates responsibilities already assigned to
policy,identity-access,platform-administration,db-mysqland the Web composition root; - creates a horizontal platform before bounded operator proof;
- increases cyclic-dependency and god-service risk;
- introduces a package and public boundary without a distinct semantic record or effect owner.
Disposition: rejected. Existing owners can deliver the result.
Accepted Authority Contract¶
1. Platform Core¶
@ctc/platform-core owns only the execution envelope and stable primitives:
tenant UUID
actor and subject
role UUIDs and role resolution state
safe profile reference
safe effective capability identifiers
correlation
access mode and provenance
It does not load profile/module/config state, authorize business records or depend on MySQL, HTTP or React.
2. Identity And Access¶
@ctc/identity-access owns authentication, session, principal, role identity, impersonation and
session lifecycle. It stores or receives a safe resolved profile reference and capability set when
issuing/reading a session.
It does not define operating profiles, infer modules, read tenant config or decide business-record eligibility.
3. Policy¶
@ctc/policy is the sole semantic owner of code-owned, versioned definitions and pure resolution for:
operating profile
explicit module state
business capability
typed configuration
credential-reference diagnostics
effective surface
The resolver accepts explicit observations and returns a safe immutable result. It performs no SQL, HTTP, provider call, record mutation or business-module state transition.
The logical result is:
EffectiveTenantRuntime
tenantUuid
profile { key, version, state, source }
modules { key -> state, validity, source, compatibilityFallback, reason }
capabilities { key -> state, prerequisites, source, reason }
configs { key -> safe typed value/status, definitionVersion, validity, source, reason }
credentials { key -> configured/status, owner reference, source, validity }
surfaces { page/action exposure derived from the effective result }
runtimeVersion
diagnostics
Secret values are never present in safe serialization.
4. Platform Administration¶
@ctc/platform-administration owns framework-independent application contracts for current applied
state:
tenant identity/profile assignment
tenant module assignment
role capability assignment
typed config value
credential reference/status
effective-state diagnostics
It owns validation and command/query contracts, not SQL, encryption, React or business records.
5. MySQL Adapter¶
@ctc/db-mysql owns loading and persisting current MySQL observations and applied state, transactions,
UUID/numeric mapping, conflict checks and authoritative reread.
Current compatibility tables remain:
T_CLIENT
T_MODULE
T_TENANT_MODULE
T_TENANT_CONFIG
T_ROLE / T_MENU compatibility
Profile assignment and role-capability assignment are distinct applied-state concerns. They must not be hidden as arbitrary raw config keys or comma-delimited menu authorization. Their exact additive MySQL representation is decided and implemented in the owning bounded Task after schema inspection.
No new customer/cohort-named adapter or export is permitted.
6. Web Composition Root¶
apps/web/src/bootstrap/** composes adapters and policy once for a protected request. Protected
business routes consume the server-resolved effective result; React receives only a safe projection.
The browser, menu tree and page registry are not authority sources.
7. Module-State Semantics¶
New semantic callers use:
ENABLED
DISABLED
UNCONFIGURED
UNAVAILABLE
COMPATIBILITY
Rules:
- an explicit enabled assignment produces
ENABLED; - an explicit disabled assignment produces
DISABLED; - a missing strict assignment produces
UNCONFIGURED, not enabled; - a source error produces
UNAVAILABLE; - current fail-open behavior may produce
COMPATIBILITYonly when an allowlisted definition names the exact caller and fallback reason; - compatibility is never the default for a new semantic route and must decrease by caller.
Existing Legacy helpers are not globally reinterpreted in one change.
8. Business Capability And Record Authorization¶
A business action requires:
authenticated principal
+ tenant effective Runtime
+ named role capability
+ required module/config/credential prerequisites
+ owning-module record and state predicate
A module does not grant every capability. A capability does not bypass tenant or record ownership.
Initial Phase-2 families include platform administration, commercial purchase/sales, fulfillment inbound/outbound, inventory receipt/allocation, Scale ticket and settlement-source actions.
9. Typed Configuration¶
Definitions are code-owned and versioned in @ctc/policy. Values remain current tenant applied state.
A definition specifies:
key
owner module
value type and validation
default and allowed sources
empty semantics
reload behavior
secret classification
deprecation/replacement
Arbitrary SQL, executable expressions and an untyped customer rule language are prohibited.
10. Surface And Menu Compatibility¶
Effective surfaces are derived from profile, module, capability, config and evidence policy.
effective Runtime
-> route/action authorization
-> surface projection
-> menu visibility
A menu may hide or link to a page. It cannot grant a command. Existing menu-based Legacy checks remain explicit compatibility until their bounded route migrates.
11. Request, Session And Cache Behavior¶
Phase 2 uses request-scoped resolution. No shared cross-request tenant policy cache is introduced.
- authentication resolves identity;
- protected requests resolve or validate the current effective Runtime from authoritative applied state;
- the public session returns only a safe profile/capability/runtime-version projection;
- an Admin mutation commits, invalidates any request-local observation, rereads the row and rereads the effective result before reporting success;
- long-lived cache, release switching and multi-deployment invalidation remain Phase 5.
12. Admin Mutation Contract¶
Every exposed Phase-2 Admin mutation requires:
trusted mutation origin
named administration capability
explicit target tenant
foreign-tenant denial
allowlisted input
expected version or updated-at conflict check where applicable
one transaction owner
authoritative row reread
effective-result reread when the value affects Runtime
redacted audit/correlation
stable error classification
A normal exposed action may not terminate in 501 NOT_IMPLEMENTED. An operation outside the bounded
implemented scope is hidden or rendered explicitly unavailable.
13. Phase Boundary¶
Phase 2 owns:
current applied-state Admin contract repair
persisted semantic profile assignment
explicit module state
business capability assignment/enforcement
typed config and effective-state diagnostics
bounded current-tenant and existing-tenant administration
Phase 5 owns:
draft/review/approve/publish
impact preview
cross-deployment apply and drift
release rollback/supersession
configuration-only third-tenant onboarding
template/bootstrap lifecycle
Support/Engineering/QA console separation
mass-action and repair governance
Runtime never queries a Control Plane on every request.
Approved Structural Budget¶
This decision approves no new package, service, Product surface, customer backend name or architecture exception.
Across the bounded Phase-2 foundation Tasks it permits:
- named exports added only to the existing
@ctc/policyand@ctc/platform-administrationroot exports for the accepted contracts; - at most one new package dependency edge:
@ctc/webto the existing@ctc/platform-administrationpackage; - no new
@ctc/db-mysqlcustomer/cohort export path; - one coherent owner movement per Task with before/after complexity reporting.
Each implementation Task must still name the exact exports and paths it changes.
Independent Architecture Review¶
A separate challenge pass attempted to reject the selected option.
| Challenge | Result |
|---|---|
| Can the visible defect be fixed without a stable authority? | Yes temporarily, but menu/profile/module/config authority remains split and Phase-2 commands still cannot consume one result. |
Does policy become a god object? |
Rejected by contract: definitions and pure resolution only; loading, writes and record predicates remain separate. |
| Is a new package required? | No. The Module Registry already assigns every concern to an existing owner. |
| Can menu remain canonical for compatibility? | Only for bounded Legacy callers. New semantic commands require named capabilities. |
| Does this absorb the full Control Plane? | No. Phase 2 stops at current applied state; publication, drift and rollback releases remain Phase 5. |
| Can profile assignment be a raw config key? | No. Profile assignment is a distinct versioned concern and must retain its own validity/provenance. |
| Are customer-specific branches required for the first two cohorts? | No. Semantic profiles plus explicit assignments cover the approved variation. |
| Is the public/dependency delta proportional? | Yes: no new package/export path, bounded root contracts and at most one dependency edge. |
Review result: ACCEPTED.
Decision-time structural effect:
production files changed 0
public exports changed 0
dependency edges changed 0
tenant-specific backend symbols 0
architecture exceptions 0
runtime / DB / deployment effects 0
Consequences¶
Positive¶
- Phase 2 has one stable authority to implement against;
- Admin repair, profile/module resolution, capability enforcement and typed configuration have separate bounded Tasks;
- menu-derived business authorization can be removed without deleting navigation compatibility;
- the same business command can be tested under two semantic profiles;
- full Control Plane work remains value-timed in Phase 5.
Negative¶
- several current compatibility paths remain until migrated caller by caller;
- the minimum foundation spans existing packages and requires disciplined composition;
- session and protected-request resolution must avoid stale or over-trusted browser state;
- additive profile and role-capability persistence still requires exact schema inspection;
- target-role and runtime evidence remain unproven until implementation and acceptance Tasks run.
Implementation Order¶
NS2-012 exposed Admin UI/API contract repair
NS2-013 persisted profile and explicit module state
NS2-014 role business capability and route/action enforcement
NS2-015 typed config, effective-state resolver and diagnostics
NS1-021 integrated Booking/Transload acceptance
NS2-016 current-tenant applied-state Admin
NS2-017 Platform Admin existing-tenant onboarding v1
NS2-018 inbound authority decision
The immediate active implementation is NS2-012. It repairs the currently exposed contract without
implementing profile/capability/config semantics ahead of their owning Tasks.
Revisit¶
Revisit only if evidence proves one of:
- pure policy resolution cannot serve Booking, Transload and Phase-2 commands without dependency cycles;
- profile assignment cannot be represented additively in current MySQL;
- a distinct semantic record/effect owner requires a new package;
- request-scoped resolution cannot meet measured performance;
- Phase-5 publication semantics are required to make an accepted Phase-2 mutation safe.
A revisit requires a superseding ADR and updates to Product Reality, Roadmap, Phase scope, Execution and exact Task/manifest.
Rollback¶
Before runtime implementation, rollback is a documentation revert.
After implementation begins, rollback is caller-bounded:
- restore the prior route/session caller while retaining new additive state;
- keep Legacy module/config behavior available for unmigrated callers;
- revert the exact implementation Task without deleting authoritative tenant data;
- do not restore menu visibility as authority for a migrated business command.
This decision itself performs no runtime, DB, data, config, credential, deployment, route or writer effect.
---¶
Source:
new-system/docs/decisions/0017-tenant-runtime-authority.md— ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.