Platform Tenant, Operating Profile, Capability And Configuration — Source Authority¶
- Status:
CURRENT SOURCE-AUTHORITY MAP / DOCUMENTATION AND OWNER RECONCILIATION ONLY - Current source baseline:
qa@4f6165bc4c53a7d98b0c246392dd9ed5bd75c21c - Working documentation branch:
agent/p6-process-detail-20260807 - Related Phase owners:
P4-001,P4-002,P4-003,P2-007,P5-001,P6-007,P8-001,P12 - Target architecture: React + Next.js adapters over framework-independent TypeScript application/policy contracts, MySQL first, PostgreSQL later
- Effect boundary: repository documentation only; no Runtime, DB, module, config, credential, Wiki, deployment or Production mutation
- Important: this document does not activate P4-003, change a module default, introduce fail-closed rollout or authorize a new configuration table.
1. Authority Decision¶
This platform family is not one boolean, one table or one authorization decision.
Tenant identity
!= Operating Profile
!= Module enablement
!= Named Process Capability
!= Package / Entitlement
!= Config value
!= Credential availability
!= Menu visibility
!= Page/API availability
!= Action authorization
!= Record authorization
One positive result must never imply another.
Example:
module enabled
!= user may see the menu
!= route is role-authorized
!= named workflow is enabled
!= required config is valid
!= credential belongs to this tenant
!= referenced record belongs to this tenant
1.1 Owning authorities¶
| Question | Current / target owner |
|---|---|
| Which tenant is executing? | TenantExecutionContext and entrypoint adapter; legacy scalar CLIENT_ID remains compatibility input |
| Which portable tenant identity applies? | trusted published local↔global mapping; currently unavailable in many paths |
| Which operating model applies? | Product/P6 operating-profile contract; current Runtime has no accepted first-class profile binding |
| Is a module available? | T_MODULE + T_TENANT_MODULE through current module guard or target typed resolution |
| Is one named process behavior enabled? | explicit process-capability definition plus prerequisites; not implied by module enablement |
| What config value applies? | typed config definition plus tenant/published/env/default provenance |
| Is a secret available and correctly owned? | credential owner reference plus allowlisted secret source |
| Can a user enter a page/API? | authentication + menu/page/API/module owners |
| Can a user perform one action on one record? | semantic capability/action authorization plus record predicate |
| Who owns drafts/releases/rollback? | target Control Tower publish lifecycle; Runtime consumes applied local state |
1.2 No new Process ID¶
This family is platform foundation, not one L3 business process. Do not add a generic PROC-PLATFORM-CONFIG merely to register these concerns.
Process packets consume the platform result. P4/P2/P5/P12 own platform implementation and rollout; P6 documents the operating and migration dependencies; P8 documents administrator/support procedures.
2. Current Source Families¶
| Source | Current responsibility | Important limitation |
|---|---|---|
config/config.php |
deployment constants, getClientId(), session/impersonation scalar identity, role/menu helpers, isModuleEnabled() |
deployment Config::CLIENT_ID fallback remains; multiple concerns coexist in a large compatibility owner |
app/platform/tenant/TenantExecutionContext.php |
typed single-tenant actor/subject/provenance/request/credential-owner contract | additive foundation; broad Runtime coverage is incomplete |
app/platform/tenant/tenant_execution_context_adapter.php |
WEB/API, impersonation, admin override, explicit CLI/integration and legacy-default constructors | only selected callers consume strict contexts; global mapping is injected rather than Runtime-owned |
app/platform/tenant/api_strict_principal.php |
safe typed context bridge into API scalar context | route opt-in only |
app/shared/api/runtime.php / router.php |
authentication, optional strict principal, module, menu and numeric-role guards before handler | many routes remain legacy scalar; record authorization stays handler-specific |
app/platform/tenant/module_guard.php |
page/module/config decisions and legacy endpoint map | missing module rows are compatibility-allowed; unmapped pages can remain allowed/unknown |
config/modules.php |
page-slug to module/config prerequisite map | page availability only; not process capability or action authorization |
app/platform/tenant/config_helper.php |
tenant config read, fallback, type heuristic, encrypted secret envelope and provider definitions | missing/empty/error often collapse to caller default; provenance and validity are lost to existing callers |
app/platform/tenant/CapabilityResolution.php |
typed immutable-style resolution result | foundation only |
app/platform/tenant/capability_resolver.php |
typed module/capability/config/secret decision logic | no accepted broad first-party caller activation is proven |
frontend/pages/platform/admin-console/tenants.php |
current tenant/module/config/provider applied-state administration UI | direct applied-state compatibility, not a versioned Control Tower release lifecycle |
app/platform/api_routes.php |
current admin APIs for tenant, module, config and onboarding operations | writes current Runtime state directly; publication/version/approval/rollback semantics are not the primary contract |
.agents/rules/TENANT_CONFIG.md |
current/target concern model, compatibility baseline and resolver contract | technical authority; does not prove caller rollout or current DB parity |
docs/tasks/P4-001.md |
accepted prior source/Local read-only baseline | dated evidence; not a fresh DB or Production snapshot for this wave |
docs/tasks/P4-002.md |
typed context/adapters and selected strict caller handoff | broad caller migration and owner acceptance incomplete |
docs/tasks/P4-003.md |
planned resolver/configuration implementation Task | not activated by this documentation wave |
docs/tasks/P2-007.md |
live-tenant role/API/profile parameterization and Client 1 non-regression Gate | broad default-deny or profile rollout remains prohibited without caller evidence |
3. Current Tenant Execution Context¶
3.1 Legacy scalar resolution¶
Current common WEB/PHP compatibility:
IMPERSONATE.TARGET_CLIENT_ID when present
-> SESSION.CLIENT_ID when present
-> Config::CLIENT_ID deployment fallback
Actor/subject helpers similarly prefer impersonation target identity.
Implications:
- a normal authenticated session has an effective local tenant;
- impersonation changes effective tenant and subject;
- CLI/direct legacy code can silently receive deployment tenant
1unless it uses an explicit strict system binding; - local numeric
CLIENT_IDis not a portable cross-deployment identity.
3.2 Typed context foundation¶
TenantExecutionContext already models:
localTenantId
optional globalTenantId
identityState: MAPPED | LEGACY_LOCAL_ONLY
tenantProvenance
entrypoint
sourceName
requestId
accessMode
actor
optional subject
optional credentialOwnerRef
optional authorizationRef
policyState: STRICT | COMPATIBILITY_ONLY
Supported provenance includes:
SESSION
IMPERSONATION_TARGET
ADMIN_ROUTE_OVERRIDE
EXPLICIT_JOB
INTEGRATION_BINDING
LEGACY_DEFAULT
assertStrictForWrite() rejects a compatibility-only context for a new write path.
3.3 Current request adapter behavior¶
normal WEB/API session
-> strict tenant + user actor/subject
super-admin tenant-scoped request
-> explicit tenant target required
impersonation
-> original super-admin actor
-> target tenant
-> optional target user subject
allowlisted admin API override
-> explicit tenant target + authorization reference
CLI/integration
-> explicit single-tenant binding
legacy CLI default
-> COMPATIBILITY_ONLY
WEB/API ALL_TENANTS is rejected. Cross-tenant fan-out belongs to an explicit job controller that creates one child context per tenant.
3.4 Current strict API rollout¶
A route opts in with:
'strictTenantPrincipal' => true
Then the router performs:
authentication
-> typed strict tenant principal
-> module decision using strict tenant
-> menu-route decision
-> numeric-role compatibility decision
-> handler
-> handler-specific record predicate
Non-opted routes continue to use legacy scalar context. Therefore:
typed foundation exists
!= repository-wide strict tenant execution
4. Operating Profile Authority Gap¶
The Product/process documents use stable operating-profile concepts:
ctc-execution
mk-order
Current Runtime does not expose an accepted first-class tenant→profile binding that every page/API/job consumes.
Current behavior is distributed across:
- numeric Client checks;
- page routes and menu availability;
- module assignments;
- named config toggles;
- order class/direction and process-specific record state;
- document/report compatibility branches;
- current operator knowledge.
Therefore:
documented operating profile
!= first-class Runtime profile assignment
4.1 Target profile definition¶
A target profile definition must be a versioned semantic contract, not a customer ID alias.
Minimum logical fields:
profileKey
version
status
applicable product/value streams
allowed process families
required and optional modules
default named capabilities
terminology set
document-template set
provider/device adapter set
config-definition set
migration compatibility policy
Open ownership decision:
- one primary profile per tenant;
- primary profile plus optional capability composition;
- multiple profile slices by business unit/location.
Do not choose a schema until Product and P4 accept the composition rule.
4.2 Profile non-implications¶
profile assigned
!= module enabled
!= user authorized
!= config valid
!= credential available
!= record eligible
Profile narrows applicable Product/process policy. It does not replace the other gates.
5. Current Module Enablement¶
5.1 Storage¶
T_MODULE
= global module catalog
T_TENANT_MODULE
= tenant-specific applied enablement/tier rows
5.2 Compatibility decision¶
Current isModuleEnabled() and module guard behavior is intentionally rollout-compatible:
explicit enabled row
-> allow
explicit disabled row
-> deny
missing tenant-module row
-> allow
module source/DB error
-> allow compatibility path
Callers commonly see:
reason = enabled_or_unconfigured
This means current resolution cannot distinguish a deliberate enablement from missing configuration at the ordinary boolean caller.
5.3 Page module decision¶
config/modules.php maps page slugs to:
- one module;
- any of several modules;
- all required modules;
- optional required config keys;
- platform-core/no-module cases.
T_MODULE_PAGE can additionally mark a page inactive or supply a registry module. Current behavior includes:
registry lookup error -> deny
registry inactive -> deny
unmapped page -> allowed / unknown
missing module row -> compatibility allow
required config false/missing/error -> deny
5.4 Module does not own capability¶
Examples:
tms.bookingdoes not imply every Booking flag, Packing output, email or rate correction is enabled;oms.inbound-orderdoes not imply Export Brokerage is enabled;yms.scaledoes not imply every flow/stage or reversal action is authorized;addon.quickbooksdoes not imply credentials are valid or the caller may send.
Target callers must consume a named process capability when behavior is narrower than a module.
6. Named Process Capability¶
Current code uses a mixture of:
- module decisions;
- page+config gates;
- direct config booleans;
- numeric Client defaults;
- route-specific conditions;
- record-state guards.
The typed CapabilityResolver already distinguishes PROCESS_CAPABILITY, but broad current caller adoption is not proven.
6.1 Target capability definition¶
Minimum logical fields:
schemaVersion
capabilityKey
ownerModuleKey
ownerProcessId when applicable
applicableProfiles[]
prerequisiteModules[]
prerequisiteCapabilities[]
requiredConfigDefinitions[]
credentialDefinition when applicable
policyState: STRICT | COMPATIBILITY
default policy
allowed resolution sources
safe diagnostic metadata
deprecation/replacement
6.2 Target capability result¶
ENABLED
DISABLED
UNAVAILABLE
with:
source
validity
policyState
compatibilityFallback
stable reason
A caller branches on the typed decision, not on a display message and not on tenant number.
7. Current Configuration Resolution¶
7.1 Storage and fallback¶
Current common resolution:
T_TENANT_CONFIG override
-> caller-specific env fallback when used
-> caller/legacy Config default
tenant_config_get() and typed helpers use request-scope static caches.
Current result often collapses:
missing row
stored empty
source error
invalid typed value
into the same caller default.
Therefore an ordinary current scalar result does not preserve:
- provenance;
- validity;
- source error;
- compatibility fallback use;
- applied release version.
7.2 Current value typing¶
tenant_config_value_type() uses:
- explicit provider definitions when available;
- key-name heuristics;
- allowlisted JSON/int/bool exceptions.
This is a compatibility classifier, not the target definition registry.
Known risk examples from prior accepted evidence:
- a JSON policy can be stored with a string metadata type;
- a URL containing
TOKENcan be misclassified as secret by heuristic; - nonempty JSON text does not prove the expected object/list shape;
- stored empty may mean disabled, missing or intentionally blank depending on caller.
7.3 Numeric tenant default example¶
Current MK document compatibility:
MK_DOCUMENT_WORKFLOW_ENABLED missing
+ effective CLIENT_ID = 2
-> default enabled
This is a current compatibility boundary, not an acceptable target profile definition.
Target:
Operating Profile / named capability definition
+ explicit typed config provenance
7.4 Config definition target¶
Minimum logical fields:
configKey
valueType
secret classification
ownerModuleKey
applicableProfiles[]
allowedSources[]
empty semantics
validation schema/range
JSON shape
default value/source policy
cache/reload behavior
safe redaction
publish metadata requirement
deprecation/replacement
Registered definitions outrank name heuristics.
8. Credential And Secret Ownership¶
Current config helper supports encrypted tenant secret envelopes:
enc:v1:<AES-256-GCM payload>
and provider definitions identify owning modules.
The typed context/resolver foundation models a non-secret credential owner reference:
scope: TENANT | DEPLOYMENT_FALLBACK | NONE
sourceType: TENANT_CONFIG | ENV | EXTERNAL_SECRET_REF
moduleKey
referenceKey
ownerLocalTenantId when tenant-owned
ownerGlobalTenantId when mapped
Rules:
credential exists
!= module enabled
!= capability enabled
!= user authorized
!= provider call permitted
Safe diagnostics expose availability/provenance/owner match, never the credential value.
9. Current Admin Console Boundary¶
The Admin Console currently provides direct applied-state operations for:
- tenant identity fields;
- tenant module enablement and tier;
- tenant config values;
- provider settings;
- onboarding defaults.
Current APIs include:
GET /api/platform/admin/tenants
PATCH /api/platform/admin/tenants/{clientId}
GET /api/platform/admin/tenants/{clientId}/modules
PATCH /api/platform/admin/tenants/{clientId}/modules/{moduleKey}
GET|PATCH /api/platform/admin/tenants/{clientId}/configs
POST /api/platform/admin/tenants/{clientId}/onboarding
Super-admin can target tenants. A regular admin-console user is limited to the effective tenant for module/config access. Dedicated deployment renders the tenant-management surface read-only.
9.1 Current completion¶
API success
+ exact local row reread
= current applied-state update
It does not prove:
Control Tower draft approved
release version published
all deployment targets applied
Runtime caches refreshed
all pages/APIs observe the same state
rollback package exists
field role accepted the change
9.2 Target control-plane separation¶
Control Tower draft
-> review
-> approved release
-> target deployment apply
-> local applied-state reread
-> effective-state diagnostics
-> observation
-> rollback or superseding release
Runtime must not query Control Tower on every request.
10. Current Package / Entitlement Gap¶
Package/tier fields exist in catalog/admin surfaces, but current source evidence does not establish one accepted entitlement owner that determines every module/capability.
Therefore:
package selected
!= module enabled
!= capability enabled
!= billing entitlement accepted
Target P4-003 must decide whether package/tier is:
- onboarding seed only;
- a recurring entitlement source;
- advisory metadata;
- replaced by explicit module/capability releases.
Do not enforce package denial until existing tenants and callers have explicit parity seeds and acceptance.
11. Current Concern Resolution Sequence¶
request/session/integration input
-> effective local tenant scalar
-> optional strict typed context
-> page/API module gate
-> menu/role gate
-> process-specific config or numeric default
-> handler record predicate
-> provider/config helper fallback when applicable
The same concern may currently be resolved more than once by different layers. This can produce:
- page/API parity mismatch;
- visible control with denied API;
- hidden page with callable API;
- missing module row treated as enabled;
- config gate and direct caller fallback disagreement;
- one request using cached pre-write config after an admin update;
- tenant-default fallback in CLI/direct page code;
- credential source differing between callers.
12. Source-Confirmed Gaps¶
| Gap ID | Current gap | Consequence |
|---|---|---|
TPC-GAP-010 |
no accepted first-class Runtime tenant→operating-profile binding | profile behavior remains inferred from tenant/module/config/page/record compatibility |
TPC-GAP-020 |
CapabilityResolver foundation has no proven broad active caller set |
target contract exists without Runtime coverage |
TPC-GAP-030 |
missing tenant-module row and module-source error can allow | enablement provenance is ambiguous and global fail-closed would risk Client 1 outage |
TPC-GAP-040 |
unmapped page can remain allowed/unknown | page inventory is not a complete authorization catalogue |
TPC-GAP-050 |
scalar config helpers collapse missing/empty/invalid/error to defaults | diagnostics and safe fallback retirement are difficult |
TPC-GAP-060 |
type heuristic can disagree with intended definition | masking, validation and provider configuration can be wrong |
TPC-GAP-070 |
numeric Client defaults/branches remain | customer identity still selects Product behavior on some paths |
TPC-GAP-080 |
strict typed context is route/caller opt-in | pages/jobs/imports/provider callbacks can still use compatibility tenant defaults |
TPC-GAP-090 |
current Admin Console edits applied state directly | no single versioned draft/review/publish/apply/rollback history |
TPC-GAP-100 |
config cache invalidation/read-after-write contract is not universal | same-request or long-running caller can observe stale effective state |
TPC-GAP-110 |
package/tier is not proven as entitlement authority | package-driven denial would be speculative |
TPC-GAP-120 |
global tenant mapping and Shared/Dedicated parity are not currently proven | portable identity and cross-deployment replay remain incomplete |
TPC-GAP-130 |
menu/page/API/action/record decisions are separately owned and incompletely inventoried | module/config success can be mistaken for authorization |
TPC-GAP-140 |
no universal effective-state diagnostic read model | support must infer behavior from rows, fallbacks and code |
13. Required Downstream Owners¶
| Work | Owner |
|---|---|
| tenant/global identity and typed context acceptance | P4-002 / platform tenant owner |
| profile/module/capability/config resolver activation | P4-003 after explicit activation |
| data/caller/strict write migration | P4-004 / P5 |
| secrets/provider binding | P4-005 and provider owners |
| live Client 1/2 role/API parity | P2-007 |
| business process applicability | P6 process owners |
| UI/module/access presentation | P7 / current page owners |
| admin/support Wiki | P8 |
| React/Next.js target and one-writer cutover | P12 |
| Control Tower draft/release/apply lifecycle | P3/Control Tower owner |
14. Required Evidence Before Implementation Activation¶
- exact current
qa, deployed source and schema/config identity; - current tenant/profile/module/config applied-state snapshots without secret values;
- current Client 1 and Client 2 module/menu/role/action caller matrix;
- all numeric Client/profile compatibility callers on selected wave;
- strict-context opt-in caller inventory;
- missing/disabled/error/invalid config behavior by selected caller;
- Admin Console before/after/readback and cache/freshness behavior;
- Control Tower mapping and applied-release identity;
- Shared and Dedicated deployment parity;
- provider credential owner and negative cases;
- rollback/observation plan that preserves Client 1.
This source-only wave performs none of those Runtime/DB/browser/provider checks.
15. Current Evidence Ceiling¶
Current source authority: RECONCILED
Prior P4 source/Local evidence: ACCEPTED HISTORICAL INPUT
Fresh DB/config snapshots: NOT RUN
Current role/browser/admin diagnostics: NOT RUN
Control Tower mapping/apply parity: UNKNOWN / NOT RUN
Runtime resolver caller activation: NOT PERFORMED
Wiki current content: NOT READ
P4-003 activation: NO
M4 migration readiness: NOT ESTABLISHED
---¶
Source:
docs/reconstruction/PLATFORM_TENANT_PROFILE_CAPABILITY_CONFIGURATION_SOURCE_AUTHORITY.md— ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.