Support Console — runtime, access and recovery contract¶
Status: prepared source implementation; not applied to an actual database or verified in the pinned Web runtime.
Owner Task: NS5-009. Product paths below are relative to new-system/.
Screen and authority¶
/support mounts SupportWorkspace. Settings has a guarded Support Console navigation action; it
uses the existing unsaved-draft decision and selected-tenant rule before navigating. The standard
Web base path is applied to all browser API URLs. No menu/role rows are seeded.
The current authenticated actor, effective subject and tenant come from resolveSemanticRequest.
SYSTEM uses only its already server-resolved selected tenant, never a query-supplied replacement.
A support grant authorizes only the read-only diagnostic path. It neither issues an authentication
session nor changes ordinary business, Admin, Engineering or QA permissions. No implicit SUPER_ADMIN
approval bypass or new role is created. Existing Admin authorization owners remain unchanged;
actual cross-surface role testing remains an acceptance requirement.
Grant management additionally requires the configured current capability and the existing
sessionHasCapabilities action/entitlement decision. Unresolved roles cannot approve grants. A
viewer receives only actor/subject-matching grants in the resolved tenant. Candidate usernames and
approval reasons are available only to an authorized manager. The implementation issues grants to
active users of the target tenant; it does not introduce cross-tenant impersonation or staff accounts.
Explicit deployment input¶
CTC_SUPPORT_POLICY is JSON with the following required fields. There are no seeded/default values:
| Field | Contract |
|---|---|
| approverCapability | A Product-owner-approved existing member of BUSINESS_CAPABILITY_IDS; current persisted assignment/action must allow it |
| maxGrantMinutes | Positive safe integer; limits each grant's start-to-expiry duration |
| auditRetentionDays | Positive safe integer; records a minimum retained-until date, not automatic deletion |
The validated webRuntimeConfig().environment scopes persistent rows. The request cannot choose a
remote environment. Missing/invalid policy, missing schema or audit failures return unavailable;
there is no in-memory, logging-only or grant-all fallback. These configuration values have not been
selected or changed in the target environment in this session.
Public owners and execution¶
@ctc/identity-access/support: existing audited read core plus grant decoding, explicit policy, issue/revoke orchestration and storage port. It owns identity/scope/time and current capability rules.@ctc/observability/support: existing allowlisted diagnostic projection. No raw message, stack, arbitrary metadata or Error.name is returned. The ordinary logger is not used as an audit receipt.@ctc/db-mysql/support: concrete MySQL persistence/query implementation. SQL and numeric aliases remain here. The app imports these admitted subpaths, not cross-package source paths.apps/web/src/bootstrap/supportRuntime.ts: actual session/pool/policy/query/audit composition. It re-resolves the current session and role state around asynchronous diagnostic execution.apps/web/src/transport/supportManagement.tsandsupportConsole.ts: HTTP decoding, audited dispatch, stable errors and no-store responses. Existing trusted mutation-origin logic is reused.
HTTP contract¶
| Endpoint | Method / result |
|---|---|
/api/support/grants |
GET lists at most 200 recent scoped grants, current view identity and manager-only candidates/policy ceiling |
/api/support/grants?grantUuid=UUID |
GET authoritative operation readback; hidden/foreign scopes are not returned |
/api/support/grants |
POST issues one normalized UUID grant and its approval audit atomically |
/api/support/grants |
PATCH revokes one grant using a retained operation UUID; repeats must match the existing receipt |
/api/support/audit |
GET manager-only, 100 records/page, optional grantUuid and UUID cursor; no raw document/reason projection |
/api/support/diagnostics |
GET requires tenantUuid, grantUuid and optional recordUuid; delegates to the acknowledged audited core |
Grant POST fields: tenantUuid, grantUuid, actorUuid, startsAt, expiresAt, reason and scope. Scope is TENANT or RECORDS with unique canonical UUIDs (1–200). The UI offers one optional operation-event record. Tenant/approver come from the current session, and subject is direct-access null. Timestamps are canonical UTC ISO strings. The UI accepts local expiry and converts it; a duration ceiling is not an invented default grant duration. Reason is trimmed, 1–500 characters.
PATCH fields: tenantUuid, grantUuid, operationUuid and reason. The browser retains the same prepared body and UUID while an outcome is uncertain. A successful response is followed by authoritative readback. Failure/timeout/lost acknowledgement does not trigger an automatic second mutation. Manual retry first reads the stored operation again and requires current management authority. A changed tenant, request body or conflicting revoke is not success. Acknowledged writes followed by failed/denied readback remain pending; input is not silently discarded. Pending/dirty work blocks access refresh and has an unload warning. Browser state is not persisted as a credential.
All API responses are private/no-store with Cookie/Authorization Vary and nosniff. Error responses expose fixed codes only: authentication 401, invalid input 400, denied 403, conflicting receipt 409, unavailable/audit/unknown write 503 and cancellation 499. Unsupported methods never return diagnostic data. Origin checks, method checks and invalid authenticated input have bounded audit entries. A rejected request before authentication/config/storage availability cannot manufacture an audit receipt; existing authentication logging remains a separate owner.
MySQL and append-only evidence¶
The sole migration source is the existing repository lineage:
database/migrations/2026-09-12_ns5-009_support_access.sql (outside the current Product root).
It creates only T_SUPPORT_ACCESS_GRANT and T_SUPPORT_ACCESS_AUDIT and two audit DML-blocking
triggers. It seeds no accounts, capabilities, grants or policy. Applying it requires an explicitly
authorized target. Re-running an already-installed trigger is not a destructive drop/recreate path;
the migration owner must inspect prior installation first.
Grant rows use binary UUIDs, environment, actor/subject/tenant indexes, a start/expiry window, byte-stable document text/hash, and revocation operation/hash. Grant+approval audit and revoke+audit share one transaction. After a commit acknowledgement error, fresh pooled readback checks the exact receipt before returning success. A failed rollback destroys the poisoned connection when supported; the normal mysql2 connection supports it. No unknown write is blindly replayed.
Audit rows use a unique event UUID, scoped indexes, document hash and minimum retention timestamp. Writes are awaited and read back, including lost insert acknowledgements. UPDATE/DELETE triggers prevent ordinary DML edits; checksums detect mismatched bytes. These do not protect against an administrator able to drop triggers, truncate/drop tables or rewrite both document and hash. Runtime DB credentials must not have those administrative powers. No purge endpoint, scheduled deletion or legal-hold policy is implemented or implied. Audit reasons remain private security records.
Diagnostic data meaning¶
The actual source is T_OPERATION_EVENT, which is a notification ledger, not a generic job outbox.
The dashboard reports tenant-source DB reachability and unacknowledged notifications in the last
14 days. The queue failure value remains unknown rather than inventing a worker failure count.
At most 100 ERROR/CRITICAL notification records from the same window are returned. Only a safe
error code, event UUID and observation timestamp are projected. No raw payload/message/PII is exposed.
recordUuid means operation-event UUID, not an arbitrary Booking/Invoice/customer UUID. Its
query is tenant-bound and omits aggregate health/checks/counts. It does not promise a universal
business-record drill-down or global service-health conclusion.
The shared ListDetailTemplate preserves a previously read dashboard beside current record detail within the same authenticated grant only. Refresh, denial, failure, expiry, disposal or a changed scope clears the relevant displayed/retained data. Stale async responses cannot resurrect data. The expiry timer re-arms for long windows and rechecks when visibility changes. Revocation is enforced on subsequent/in-flight server reads, not represented as a live push notification to idle browsers.
Verification and release boundary¶
Prepared source tests: 206 PASS on cloud Node22.16.0 / TS5.8.3, synthetic grants/SQL connections and
native Request/Response. Six deliberate mutations were detected and restored. Selected .ts
typing and TSX syntax passed; these do not typecheck the actual Next/bootstrap/shared React dependencies.
The installed browser React16 lacks current Hooks, so browser execution is BLOCKED, not PASS.
No actual MySQL migration, SQL trigger execution, DB privilege review, pinned React19/Web build,
authenticated role/CRUD, independent review or production test was performed.
Before acceptance, use the repository-pinned environment to execute Task/native tests, apply the reviewed schema to an authorized disposable target, exercise grant/read/detail/revoke/audit with secret fixtures and two tenants, test existing Admin capability denial, prove commit-loss recovery and append-only triggers, then clean the exact fixtures and verify desktop/narrow rendering. DB compatibility is BACKWARD_COMPATIBLE_STAGED: existing endpoints/schema are unchanged, and the new feature requires its explicit configuration and schema before use. It is not deployed by preparing source.
---¶
Source:
new-system/docs/pages/SUPPORT_CONSOLE.md— ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.