Skip to content

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_MODULE row is treated as enabled;
  • route and page access still use menu URLs as part of business authorization;
  • @ctc/policy has 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 501 defects 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 policy from 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-mysql and 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 COMPATIBILITY only 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/policy and @ctc/platform-administration root exports for the accepted contracts;
  • at most one new package dependency edge: @ctc/web to the existing @ctc/platform-administration package;
  • no new @ctc/db-mysql customer/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.