Platform Administrator Guide
Overview
This guide covers the Platform Console -- the internal operations dashboard at /platform/* where Cadenora staff manage tenants, users, support sessions, and infrastructure health. The Platform Console operates on the control plane, which is physically separated from the data plane where tenant business data lives.
This separation is enforced at every layer of the stack: URL routing, middleware, database transactions, and storage. A platform admin cannot see individual client records, document content, or tenant business data without explicitly entering a support session.
Audience: Platform Administrators, Support Engineers, Security Team
Table of Contents
- Getting Started
- Tenant Management
- User Management
- Support Sessions
- Impersonation
- System Monitoring
- Security and Compliance
- Configuration
- Operational Procedures
- Troubleshooting
1. Getting Started
1.1 Platform Roles
Three platform-tier roles exist, each scoped to specific operational responsibilities:
| Role | Code | Description | MFA Required |
|---|---|---|---|
| Platform Admin | PLATFORM_ADMIN | Full platform operator: tenant provisioning, global config, user management, system diagnostics, support sessions | Yes (AAL2) |
| Platform Support | PLATFORM_SUPPORT | Support operator: view tenant health and status, create support sessions, run diagnostics | Yes (AAL2) |
| Platform Security | PLATFORM_SECURITY | Security operator: audit review, access reviews, security policy enforcement | Yes (AAL2) |
All platform roles require MFA. You cannot access the Platform Console without an active AAL2 session.
1.2 Logging In
Platform admins authenticate via CadenoraAuth, the in-process identity system (bcrypt + Redis + Prisma). The login flow:
- Navigate to
https://app.cadenora.com/auth/login. - Enter your platform admin email and password.
- Complete TOTP verification (MFA).
- The system creates a session with
actorType: PLATFORMand redirects you to/platform/dashboard.
Your session cookie (cadenora_sid) contains a minimal JWT for Edge validation. The full session, including your role, groups, and actor type, is stored in Redis.
Legacy routes: If you navigate to /admin/*, the middleware automatically redirects to /platform/* via a 301 redirect. This is part of the route consolidation completed in v8.2.
1.3 Platform Dashboard Overview
The dashboard at /platform/dashboard is your landing page. It provides:
- Active Tenants count -- Total organizations with
ACTIVEstatus. - Total Users count -- Users across all tenants (aggregate integer, no individual records).
- Active Support Sessions count -- Currently active support connections.
- Recent Tenants -- Last 5 tenants created, showing name, status, and creation date.
- Recent Support Sessions -- Last 5 support sessions with tenant name, reason, and timestamps.
- Quick Actions -- Links to Manage Tenants, Support Sessions, and View All Users.
1.4 Platform Console Navigation
| Page | Route | Purpose |
|---|---|---|
| Dashboard | /platform/dashboard | System health and tenant overview |
| Tenant List | /platform/tenants | View and manage all tenants |
| New Tenant | /platform/tenants/new | Provision a new tenant workspace |
| Tenant Detail | /platform/tenants/{id} | Configuration and status of a specific tenant |
| Platform Users | /platform/users | Manage platform-level user accounts |
| Support Dashboard | /platform/support | Support sessions and impersonation tools |
| System Status | /platform/system | Infrastructure health and service status |
| Redirect Analytics | /platform/analytics/redirects | Analytics for legacy route redirects |
2. Tenant Management
2.1 Tenant List
Navigate to /platform/tenants to see all tenants. The list displays:
| Column | Source | Description |
|---|---|---|
| Name | tenant.name | Clickable link to tenant detail |
| Subdomain | tenant.subdomain | The tenant's URL slug (e.g., acme-corp) |
| Status | tenant.status | DRAFT, ACTIVATING, ACTIVE, SUSPENDED |
| Industry | configuration.industryTemplate | The industry template selected at onboarding |
| Admin Email | tenant.adminEmail | The provisioning admin's email |
| Users | _count.users | Internal staff count (integer) |
| Projects | _count.projects | Container count (integer) |
| Client Orgs | _count.clientOrganizations | Client organization count (integer) |
| Client Members | _count.clientMembers | Client member count (integer) |
| Invites | _count.invites | Invitation count (integer) |
| Created | tenant.createdAt | Tenant creation timestamp |
All client-related columns are aggregate counts only. Individual client records (names, emails, organization details) are never displayed to platform admins. This follows the control plane / data plane separation model.
2.2 Tenant Detail
Click a tenant name to access the detail page at /platform/tenants/{id}. This page provides nine stat cards in a grid layout:
| Card | Data |
|---|---|
| Status | Badge: ACTIVE, DRAFT, SUSPENDED |
| Users | Internal staff count with max users limit |
| Projects | Container count |
| Documents | Document count |
| Client Orgs | Client organization count (integer only) |
| Client Members | Client member count (integer only) |
| Invitations | Invitation count (integer only) |
| Storage Used | storageUsedGb / storageQuotaGb |
| Created | Creation and activation timestamps |
Additional sections on the detail page:
- Tenant Settings -- Tier, Admin Email, MFA policy, password policy, session timeout, onboarding email delay.
- Industry Configuration -- Template (immutable after onboarding), current theme, terminology labels.
- Internal Users (last 10) -- Staff names, emails, and roles. Only users with
FIRM_ADMINorPROJECT_MANAGERroles;INVESTORrole users are excluded from this view. - Recent Projects (last 10) -- Project names, statuses, and creation dates. Metadata only, no content or financials.
- Activity Log (last 20) -- Audit log entries showing action, resource type, and timestamp. No content or PII details.
- Danger Zone -- Delete tenant button (requires confirmation and audit justification).
2.3 Provisioning a New Tenant
Navigate to /platform/tenants/new or click "Create New Tenant" from the tenant list.
Step 1: Basic Information
| Field | Required | Validation |
|---|---|---|
| Organization Name | Yes | 2-80 characters |
| Subdomain | Yes | 3-63 characters, lowercase alphanumeric and hyphens only |
| Admin Email | Yes | Valid email format |
| Description | No | Max 500 characters |
The subdomain is auto-generated from the organization name. The system checks subdomain availability in real time via GET /api/platform/tenants/check-subdomain.
Step 2: Industry Template Selection
Select one of the five industry templates. Each template pre-configures:
| Template | Container Term | Client Term | Portal Name | Default Theme |
|---|---|---|---|---|
| Financial Services | Project | Investor | Investor Portal | Executive |
| Real Estate Development | Development | Investor | Investor Portal | Ember |
| Property Management | Property | Resident | Resident Portal | Horizon |
| Legal Services | Matter | Client | Client Portal | Corporate |
| General / Other | Project | Client | Client Portal | Minimal |
You can clear the selection to let the tenant choose during onboarding instead.
Step 3: Submit
Clicking "Create Tenant" triggers the following backend flow:
POST /api/platform/tenantscreates aTenantrecord inDRAFTstatus.- The system redirects to the new tenant's detail page.
- From the detail page, click "Activate" to trigger the Temporal
ProvisionTenantWorkflow.
Provisioning Workflow (Temporal):
The ProvisionTenantWorkflow executes the following activities in sequence:
ValidateTenant-- Checks subdomain uniqueness and email validity.CreateTenantRecord-- CreatesTenant+TenantConfigurationwith the selected industry template.ApplyTemplate-- Creates default document categories for the industry.CreateAdminUser-- Creates theUserrecord with bcrypt-hashed password andAuthIdentity(provider:local) via CadenoraAuth.CreateAuditLog-- Logs the provisioning event.SendWelcomeEmail-- Sends a setup-password token email to the admin.
Tenant status transitions: DRAFT -> ACTIVATING -> ACTIVE.
If any step fails, the saga compensation mechanism rolls back partial state. The workflow uses WorkflowIDReusePolicy: RejectDuplicate to prevent double-click activation.
2.4 Tenant Status Lifecycle
| Status | Meaning | Actions Available |
|---|---|---|
DRAFT | Tenant created but not activated | Activate, Edit, Delete |
ACTIVATING | Provisioning workflow in progress | View status (polling) |
ACTIVE | Fully operational | View details, Suspend, Enter support session |
SUSPENDED | Temporarily disabled | Reactivate, View details |
2.5 Activating a Tenant
From the tenant detail page, click "Activate Tenant". This transitions the tenant from DRAFT to ACTIVATING and starts the Temporal provisioning workflow.
The activation button calls POST /api/platform/tenants/{tenantId}/activate. While the workflow is running, the tenant detail page polls for status updates. Login routes detect PROVISIONING_IN_PROGRESS status and return a 503 with a user-friendly message if someone tries to log in before provisioning completes.
2.6 Suspending a Tenant
From the tenant detail page, click "Suspend Tenant" (available when status is ACTIVE). This calls POST /api/platform/tenants/{tenantId}/suspend.
When suspended:
- Users cannot log in to the tenant workspace.
- Existing sessions are revoked.
- Data is preserved (not deleted).
- Support sessions can still be created for investigation.
2.7 Tenant Configuration
The tenant detail page shows the industry configuration section:
- Industry Template -- Set at onboarding, immutable afterward. Determines default categories, terminology, and theme.
- Theme -- Can be changed by the tenant's Firm Admin at
Settings > Branding, or overridden by a Platform Admin viaPATCH /api/platform/tenants/{tenantId}/configuration. - Terminology -- The 14 dynamic label fields (9 raw + 5 computed). Customizable by the tenant. The platform view shows the current terminology values.
3. User Management
3.1 Platform Users Page
Navigate to /platform/users to see all users with platform roles. This page lists PLATFORM_ADMIN, PLATFORM_SUPPORT, and PLATFORM_SECURITY users.
3.2 Cross-Tenant User Visibility
Platform admins can view a tenant's internal staff (users with FIRM_ADMIN or PROJECT_MANAGER roles) via the tenant detail page. This is limited to the last 10 staff members, showing only name, email, and role.
Platform admins cannot see individual investor/client users (INVESTOR role) from the platform interface. Client user data is end-customer PII belonging to the tenant firm. To see individual client records, you must enter a support session.
3.3 What Platform Admins See vs. Do Not See
Visible to platform admins:
| Data | Source | Risk Level |
|---|---|---|
| Tenant metadata (name, subdomain, status) | Tenant model | None |
| Industry configuration | TenantConfiguration model | None |
| Admin email (provisioning contact) | Tenant model | None |
| Aggregate counts (users, projects, docs, clients) | _count integers | None |
| Internal staff names and emails (last 10) | User model (staff roles only) | Low |
| Project metadata (name, status, date) | Project model (last 10) | Low |
| Audit log metadata (action, type, timestamp) | AuditLog model (last 20) | Low |
Not visible to platform admins (requires support session):
| Data | Reason |
|---|---|
| Individual client names and emails | End-customer PII; PIPEDA/GDPR exposure |
| Client organization details | Tenant business relationship data |
| Document content or previews | Tenant business data (data plane) |
| Client-document grants | Reveals client engagement patterns |
| Investor profiles | Sensitive financial PII |
| Invitation details (individual emails) | PII exposure |
3.4 User Provisioning by Platform Admin
Platform admins can provision users for any tenant using the Tenant Provisioning approach:
- Navigate to
/platform/tenants. - Select the target tenant.
- The provisioning workflow creates the admin user automatically.
For additional users within a tenant, you must either:
- Enter a support session (SUPPORT_ADMIN mode) and use the tenant's user management interface.
- Have the tenant's Firm Admin invite users themselves.
3.5 Password Reset Assistance
If a user cannot log in and the tenant's Firm Admin is unavailable:
- Enter a SUPPORT_ADMIN session for the tenant (see Section 4).
- Navigate to the tenant's user management at
/t/{slug}/users. - Locate the user and trigger a password reset email.
- The system sends a setup-password token to the user's email.
- End the support session when finished.
4. Support Sessions
4.1 What Support Sessions Are
Support sessions are time-boxed, audit-logged access grants that allow platform operators to access a specific tenant's data. They are the controlled escalation mechanism that bridges the control plane / data plane boundary.
Without a support session, platform admins see only aggregate counts and metadata. With a support session, you operate within the tenant's workspace with either read-only or delegated admin access.
4.2 Support Session Modes
| Mode | Effective Role | Can Read Data | Can Modify Data | Typical Use |
|---|---|---|---|---|
READ_ONLY | SUPPORT_READONLY | Yes | No (blocked by requireWriteAccess()) | Investigating issues, viewing tenant configuration |
DELEGATED_ADMIN | SUPPORT_ADMIN | Yes | Yes (all admin-level mutations) | Fixing data issues, performing actions on behalf of tenant |
4.3 Creating a Support Session
Navigate to /platform/support and click "New Support Session". The dialog requires:
| Field | Required | Validation |
|---|---|---|
| Tenant | Yes | Select from active tenants (UUID) |
| Mode | Yes | READ_ONLY or DELEGATED_ADMIN |
| Reason | Optional (recommended) | Max 1000 characters; reference a support ticket |
| TTL (Hours) | Optional | 1-4 hours; defaults to 2 hours |
Security requirements:
- You must have
PLATFORM_ADMINorPLATFORM_SUPPORTrole. - TOTP step-up verification is required (fresh MFA within the last 5 minutes).
- All creation requests are audit-logged with your identity, IP address, user agent, mode, reason, and TTL.
What happens on creation:
POST /api/platform/support/sessionsvalidates your identity and step-up status.- A
SupportSessionrecord is created with a SHA-256 hashed session token. - Your Redis session is updated with
supportContext(tenant ID, slug, mode, expiry). - An audit log entry is created:
SUPPORT_SESSION_CREATED. - You are redirected to
/t/{slug}/-- the tenant's workspace, now accessible with your support role.
Visual indicator: The UI should display a banner or badge indicating you are in support mode, showing the tenant name, mode, and remaining time.
4.4 Operating Within a Support Session
Once inside a support session:
- READ_ONLY mode: You can browse the tenant workspace, view documents, users, projects, audit logs, and settings. Any attempt to create, update, or delete data returns a 403 error. The
requireWriteAccess(context)guard blocks all mutations. - DELEGATED_ADMIN mode: You have full admin access within the tenant. You can manage users, upload documents, change settings, and perform any action a
FIRM_ADMINcould.
Audit trail during support sessions: Every action you take is logged with dual-identity attribution:
actorId-- Your platform admin user ID.supportSessionId-- The support session ID.auditorUserId-- Your platform admin user ID (for cross-reference).
4.5 Support Session Time Limits
| Setting | Value |
|---|---|
| Default TTL | 2 hours |
| Minimum TTL | 1 hour |
| Maximum TTL | 4 hours |
| Auto-expiry | Session automatically becomes invalid after expiresAt |
When a session expires, the middleware detects the expired supportContext and redirects you back to the Platform Console. Your access to the tenant workspace is immediately revoked.
4.6 Ending a Support Session
You can end a support session before its natural expiry:
- Navigate to
/platform/support. - Find the active session in the list.
- Click "End Session" (or use the session banner's end button).
DELETE /api/platform/support/sessions/{sessionId}is called.- The session record is updated with
revokedAtandrevokeReason. - An audit log entry is created:
SUPPORT_SESSION_ENDED. - Your Redis session is cleared of the
supportContext.
4.7 Support Dashboard
The Support Dashboard at /platform/support shows:
- Active Sessions -- Count of currently active (not revoked, not expired) sessions.
- Total Sessions -- Count of all sessions (last 50).
- Revoked Sessions -- Count of manually ended sessions.
- Session Table -- Columns: Support User, Tenant, Mode, Status (Active/Expired/Revoked), Created, Expires, Reason.
4.8 Namespace Enforcement
The middleware at src/middleware.ts enforces that platform admins cannot access /t/{slug}/* or /i/{slug}/* routes without an active support session. If you try to navigate to a tenant workspace URL without a valid supportContext:
- You are redirected to
/platformwith an error message (tenant_access_required). - The attempted access is logged.
5. Impersonation
5.1 When to Use Impersonation
Impersonation sessions are a deeper access level than support sessions. Use impersonation when you need to:
- Reproduce a user-specific bug -- see the workspace exactly as a specific user sees it.
- Debug permission issues -- verify what a particular user can and cannot access.
- Perform actions on behalf of a user -- when the user cannot do so themselves (e.g., accessibility issues, emergency).
Impersonation is more invasive than a support session because you inherit the target user's specific permissions rather than operating as a generic admin.
5.2 Who Can Impersonate
| Role | Can Impersonate | Conditions |
|---|---|---|
PLATFORM_ADMIN | Yes | TOTP step-up required; must have active support session or tenant context |
FIRM_ADMIN | Yes | Only within their own tenant; TOTP step-up required |
PLATFORM_SUPPORT | Yes | Only when an active support session exists for the target tenant |
5.3 Creating an Impersonation Session
Prerequisites:
- You need an active support session for the target tenant (if you are a platform operator), or you need to be a
FIRM_ADMINwithin the tenant. - You need fresh TOTP step-up verification (within 5 minutes).
API call:
POST /api/platform/impersonations
Required fields:
| Field | Required | Description |
|---|---|---|
targetUserId | Yes | UUID of the user to impersonate |
tenantId | Yes | UUID of the tenant (must match your support session context) |
reason | Yes | Justification for the impersonation (mandatory, cannot be empty) |
What happens:
- The system validates your role, step-up status, and that the target user exists within the specified tenant.
- An
ImpersonationSessionrecord is created with a SHA-256 hashed session token. - Your Redis session is updated with
impersonationContext(target user info, expiry). - An audit log entry is created:
IMPERSONATION_STARTEDwith dual-identity logging (your ID asauditorUserId, the impersonated user's ID asactorId).
5.4 Impersonation Session Model
| Field | Description |
|---|---|
id | Session UUID |
tenantId | The tenant being accessed |
adminUserId | Your platform admin user ID |
impersonatedUserId | The user you are impersonating |
sessionTokenHash | SHA-256 hash (never stored in plaintext) |
reason | Required justification text |
ipAddress | Your IP at session creation |
userAgent | Your browser at session creation |
expiresAt | Fixed at 1 hour from creation |
actionsPerformed | Counter of actions taken during the session |
5.5 Audit Implications
Every action performed during an impersonation session creates an audit log entry with:
actorId-- The impersonated user's ID (the user whose permissions you are using).onBehalfOfId-- The impersonated user's ID.auditorUserId-- Your platform admin user ID (the person actually performing the action).impersonationSessionId-- The session ID for traceability.supportSessionId-- The parent support session ID (if applicable).
This dual-identity logging ensures that audit reviewers can distinguish between actions taken by the actual user and actions taken by a platform operator on their behalf.
5.6 Ending an Impersonation Session
To exit impersonation:
- Call
POST /api/platform/impersonations/exit. - The
ImpersonationSessionrecord is updated withendedAt. - Your Redis session is cleared of the
impersonationContext. - You return to the support session context (or platform context if no support session was active).
Impersonation sessions automatically expire after 1 hour.
6. System Monitoring
6.1 System Status Page
Navigate to /platform/system to see real-time health monitoring for all core services. The page displays a card for each service with:
- Status indicator -- Green (healthy), yellow (degraded), red (unhealthy).
- Latency -- Response time in milliseconds (for services that responded).
- Last Check -- Timestamp of the most recent health probe.
- Overall Status -- "All systems operational" or count of services experiencing issues.
6.2 Monitored Services
The health endpoint at GET /api/platform/health checks five core services:
| Service | Health Check Method | What It Probes |
|---|---|---|
| PostgreSQL | SELECT 1 query | Database connectivity and query latency |
| Redis | HTTP probe to Redis endpoint | Session store and cache availability |
| SeaweedFS | HTTP probe to S3 endpoint | Document storage availability |
| Quickwit | GET /api/v1/cluster | Full-text search engine availability |
| Temporal | SDK health check (gRPC) | Workflow orchestration availability |
All health checks run in parallel with a timeout guard (defined in TIMEOUTS.HEALTH_CHECK_MS).
6.3 Gatus Integration
Cadenora includes a Gatus service (gatus) for continuous health monitoring and alerting. Gatus provides:
- Status page -- Publicly accessible uptime dashboard.
- SMTP alerts -- Email notifications when services go down or recover.
- Historical data -- Uptime history and incident tracking.
Gatus runs as a Docker service alongside the application stack.
6.4 Temporal UI
The Temporal UI (accessible via the temporal-ui service) provides detailed monitoring of workflow execution:
- Workflow list -- All running and completed workflows with status.
- Workflow detail -- Step-by-step execution history for each workflow.
- Pending tasks -- Tasks waiting for worker pickup.
- Activity history -- Detailed logs for each activity execution.
This is particularly useful for debugging provisioning failures, document processing issues, and invitation delivery problems.
6.5 Key Metrics to Monitor
| Metric | Where to Check | Alert Threshold |
|---|---|---|
| Service health | /platform/system | Any service unhealthy |
| Active support sessions | /platform/support | Unusual number of concurrent sessions |
| Failed provisioning jobs | Temporal UI | Any workflow in FAILED state |
| Storage usage per tenant | Tenant detail page | Approaching storageQuotaGb |
| Pending invitations | Tenant detail (invitation count) | Growing count may indicate delivery issues |
| AI provider health | Tenant AI settings | Provider connection failures or timeouts |
| AI rate limit exhaustion | Tenant AI settings | Users frequently hitting rate limits |
| AI monthly spend | Tenant AI settings | Approaching or exceeding monthly budget |
Note: AI features are configured per tenant. Each tenant can use the platform default provider or bring their own API key. Platform admins should be aware of AI cost tracking, especially for tenants using the platform default provider, as their usage contributes to the platform's aggregate AI spend.
6.6 Console Workspace
The Console is an internal operations dashboard deployed alongside the main Cadenora application. It provides operational visibility and management tools for the Cadenora platform team.
Architecture:
- The Console is a separate pnpm workspace member within the Cadenora monorepo -- same git repository, independent build and deploy cycle.
- It has zero cross-dependencies with the main application. Console packages do not import from the main app's
src/directory, and vice versa.
Components:
| Component | Package | Framework | Port |
|---|---|---|---|
| Console Web | @cadenora/console-web | Next.js 14, React 18, TailwindCSS 3, Tanstack Query 5 | 3100 (dev) |
| Console API | @cadenora/console-api | Hono 4, Prisma 6 (10 models), ioredis 5 | 4000 |
Deployment:
- Deployed to k3s in the
cadenora-consolenamespace (separate from the maincadenoranamespace). - Console Dockerfiles build from the monorepo root (required for
turbo prune). - Build and deploy via
console/scripts/deploy.sh deploy.
Documentation:
- Developer guide:
console/docs/DEVELOPER_GUIDE.md - User guide:
console/docs/USER_GUIDE.md - Architecture:
console/CADENORA_CONSOLE_ARCHITECTURE_V1.1.md
Dual Prisma isolation:
The Console API generates its Prisma client to src/generated/prisma (not @prisma/client). This prevents overwriting the main application's Prisma client types.
7. Security and Compliance
7.1 Five-Layer Defense Model
As a platform admin, you should understand the five layers that protect tenant data:
| Layer | Component | What It Does |
|---|---|---|
| 0 | Plane Separation | /platform/* vs /t/{slug}/* vs /i/{slug}/* -- physically separate URL namespaces |
| 1 | URL + Session + Context | Middleware extracts scope from URL, validates JWT session, loads tenant config |
| 2 | Application | requireApiAuth(), requireWriteAccess(), Zod validation, request.clone() before auth |
| 3 | Database (RLS) | PostgreSQL RLS via set_config('app.tenant_id'), composite PKs enforce tenant isolation |
| 4 | Storage | SeaweedFS keys prefixed with v1/{tenantId}/, server validates key structure |
| 5 | Search | Quickwit tenant_id field with mandatory filter on all queries |
7.2 Audit Log Access
Audit logs are accessible at two levels:
Platform level (aggregate):
- Tenant detail page shows the last 20 audit log entries for a specific tenant (action, resource type, timestamp only -- no content details).
Tenant level (full detail, requires support session):
- Enter a support session for the tenant.
- Navigate to
/t/{slug}/audit-logs. - Filter by date range, action type, user, or resource.
- Export as CSV for regulatory inspection.
7.3 Cross-Tenant Event Review
The audit log model includes a tenantId field on every entry. For cross-tenant event review:
- Support session audit events include both
tenantIdandsupportSessionId, making it possible to trace all actions performed during a specific support session across the audit log. - Impersonation session events include
impersonationSessionIdandauditorUserIdfor the same purpose. - Platform-level events (tenant creation, activation, suspension) are logged with the platform admin's identity.
7.4 Compliance Reporting
Cadenora supports two major Canadian regulatory frameworks:
NI 31-103 (Canadian Securities):
- 7-year document retention minimum, enforced technically.
- 1-year post-project-closure extension.
- Legal hold overrides (documents preserved indefinitely until hold removed).
- Retention enforcement via
RetentionEnforcementWorkflow(Temporal).
PIPEDA (Privacy):
- Mandatory MFA for admin roles (based on 23andMe precedent, PIPEDA Findings #2025-001).
- Aggregate-only visibility for platform admins (data minimization).
- Time-boxed support sessions with audit trails (justified access).
- Data access, correction, and deletion request handling (30-day response window).
7.5 Security Alerts
Monitor for:
- Unusual support session patterns -- Multiple concurrent sessions, sessions created outside business hours, sessions with unusual tenant targets.
- Failed step-up attempts -- Multiple failed TOTP verifications may indicate compromised credentials.
- Provisioning failures -- Failed workflows may indicate infrastructure issues or configuration drift.
- Unhealthy services -- Any service on the System Status page showing
degradedorunhealthy.
7.6 Token Security
All security-sensitive tokens use proper cryptographic handling:
| Token Type | Algorithm | Storage |
|---|---|---|
| Support session tokens | SHA-256 hash | Only hash stored in sessionTokenHash column |
| Impersonation session tokens | SHA-256 hash | Only hash stored in sessionTokenHash column |
| Invitation tokens | SHA-256 hash | Only hash stored; cleartext sent once via email |
| Share tokens | AES-256-GCM encryption | Encrypted blob with configurable expiry |
| Session cookies | JWT (HS256) | Minimal claims; full session in Redis |
8. Configuration
8.1 Platform-Level Settings
Platform-level settings are managed via environment variables and the deployment configuration. These are not editable via the Platform Console UI:
| Setting | Environment Variable | Description |
|---|---|---|
| App URL | NEXT_PUBLIC_APP_URL | Base URL for the application |
| Session secret | SESSION_SECRET | JWT signing secret for session cookies |
| JWT secret | JWT_SECRET | JWT signing key for edge middleware |
| Auth secret | AUTH_SECRET | Authentication secret for CadenoraAuth |
| Storage endpoint | STORAGE_ENDPOINT | Internal SeaweedFS endpoint |
| Public storage endpoint | STORAGE_PUBLIC_ENDPOINT | Public-facing presigned URL endpoint |
| Quickwit endpoint | QUICKWIT_ENDPOINT | Full-text search engine endpoint |
| Temporal address | TEMPORAL_ADDRESS | Workflow orchestration server |
| Redis URL | REDIS_URL | Session store and cache |
8.2 Per-Tenant Configuration
Tenant configuration is managed via the TenantConfiguration model and can be viewed (but rarely modified) from the platform:
- Industry Template -- Set at onboarding, immutable. Determines default categories, terminology, and theme.
- Theme -- Can be changed by the tenant via Settings > Branding. Platform admins can override via API.
- Terminology -- 14 dynamic label fields. Customizable by the tenant.
- MFA Policy (
mfaRequired) -- Whether MFA is required for all users in the tenant. - Password Policy (
passwordExpireDays) -- Password expiration period. - Session Timeout (
sessionTimeoutMinutes) -- Idle session timeout. - Storage Quota (
storageQuotaGb) -- Maximum storage allocation.
To modify tenant configuration from the platform:
PATCH /api/platform/tenants/{tenantId}/configuration
Content-Type: application/json
{
"themeId": "EXECUTIVE",
"terminology": { ... }
}- Max Users (
maxUsers) -- Maximum number of users for the tenant. - Onboarding Email Delay (
onboardingEmailDelayHours) -- Delay before sending onboarding emails.
8.3 Default Templates
The five industry templates are defined in src/lib/config/industry-templates.ts. Each template specifies:
- Default terminology (9 raw fields).
- Default theme.
- Default document categories (6 per template, plus the system "Uncategorized" category).
These templates are code-level constants, not runtime configuration. Modifying them requires a code change and redeployment.
8.4 Email Configuration
Email delivery is handled via SMTP (configured in environment variables) and orchestrated through Temporal workflows:
- Invitation emails -- Triggered by
SendInviteWorkflow. - Welcome emails -- Triggered during
ProvisionTenantWorkflow. - Reminder emails -- Triggered by
RecipientReminderWorkflow. - Password reset emails -- Triggered by the auth flow.
Email templates use the tenant's terminology variables for industry-appropriate language.
Email delivery is orchestrated through Temporal workflows. If emails fail, check the workflow status in Temporal UI for error details and retry information.
9. Operational Procedures
9.1 Onboarding a New Tenant
Complete procedure:
- Gather information from the client: organization name, desired subdomain, admin email, industry.
- Navigate to
/platform/tenants/new. - Fill in the organization name, subdomain, and admin email.
- Select the appropriate industry template (or leave blank for the tenant to choose during onboarding).
- Click "Create Tenant".
- From the tenant detail page, click "Activate Tenant".
- Monitor the provisioning workflow status (should complete within 30-60 seconds).
- Verify the admin received the setup-password email.
- Confirm the tenant admin can log in at
https://{subdomain}.cadenora.com/auth/login. - Record the onboarding in your operational log.
If provisioning fails:
- Check Temporal UI for the failed workflow and its error details.
- Common causes: subdomain already taken (race condition), database connectivity issue, email delivery failure.
- The saga compensation should have rolled back partial state. Verify by checking the tenant detail page.
- If cleanup is incomplete, contact the engineering team.
9.2 Resetting a User's Password
When the tenant admin is available:
- Direct them to the user management page in their workspace (
/t/{slug}/users). - They can trigger a password reset email from there.
When the tenant admin is unavailable:
- Create a SUPPORT_ADMIN session for the tenant.
- Navigate to
/t/{slug}/users. - Find the user.
- Trigger a password reset (sends a setup-password token email).
- End the support session.
- Log the action in your support ticket.
9.3 Investigating a Reported Issue
Standard investigation workflow:
- Create a READ_ONLY support session for the affected tenant.
- Navigate to the relevant area (e.g., document detail, project settings, user list).
- Check the audit log at
/t/{slug}/audit-logsfor relevant events. - If you need to make changes, end the READ_ONLY session and create a DELEGATED_ADMIN session.
- Make the necessary changes (all changes are audit-logged).
- End the support session.
- Document findings and actions in the support ticket.
9.4 Handling Data Access Requests (PIPEDA)
When a user submits a data access request:
- Verify the requestor's identity through an independent channel (phone, video).
- Create a READ_ONLY support session for the tenant.
- Locate the user's data: profile, audit logs, documents uploaded by the user, project memberships.
- Generate a data export (if the tenant has this capability enabled).
- Deliver the export via a secure channel (encrypted download link, 7-day expiry).
- Log the request fulfillment in the audit trail.
- Respond within 30 days (PIPEDA requirement).
9.5 Handling Data Deletion Requests
- Verify the requestor's identity.
- Check for NI 31-103 retention obligations -- documents within the 7-year retention period cannot be deleted.
- Check for active legal holds -- held documents cannot be deleted by anyone.
- If deletion is blocked by regulatory retention, inform the user and offer anonymization instead.
- If deletion is permitted, proceed through the tenant's user management interface (requires SUPPORT_ADMIN session).
- Audit logs are retained but anonymized (user identity replaced with "Deleted User [uuid]").
- Respond within 30 days (PIPEDA requirement).
9.6 Emergency: Suspending a Compromised Tenant
If you suspect a tenant account is compromised:
- Navigate to the tenant detail page.
- Click "Suspend Tenant" -- this immediately blocks all logins.
- Create a READ_ONLY support session (suspension does not prevent support access).
- Review the audit log for suspicious activity.
- Check recent login events for unfamiliar IP addresses or user agents.
- If individual users are compromised, disable their accounts and revoke all sessions.
- Coordinate with the tenant admin (via a verified channel) before reactivating.
- Document the incident per the incident response procedure.
10. Troubleshooting
10.1 Cannot Access Platform Console
Symptom: Navigating to /platform/* redirects to /unauthorized.
Diagnosis:
- Verify your user account has
actorType: PLATFORMin the session. - Verify your role is one of
PLATFORM_ADMIN,PLATFORM_SUPPORT, orPLATFORM_SECURITY. - Check that you authenticated via CadenoraAuth with the correct platform admin email.
Solution:
- If your session is stale, log out and log back in.
- If your role was recently assigned, the session may not reflect it yet. Log out, clear cookies, and log in again.
- Check Redis connectivity and
SESSION_SECRET/AUTH_SECRETenv vars.
10.2 Support Session Creation Fails
Symptom: "Failed to create support session" error when clicking "New Support Session".
Diagnosis:
- Check that TOTP step-up is working (your MFA code must be valid and recent).
- Verify the target tenant exists and has a valid status.
- Check the browser console for API response details.
Solution:
- If step-up fails, verify your device clock is synchronized (TOTP is time-sensitive, +/- 30 seconds).
- If the tenant ID is invalid, refresh the tenant list.
- If the API returns 500, check application logs for database connectivity issues.
10.3 Tenant Provisioning Stuck in ACTIVATING
Symptom: Tenant stays in ACTIVATING status indefinitely.
Diagnosis:
- Check Temporal UI for the
ProvisionTenantWorkflowexecution status. - Look for failed activities in the workflow history.
- Common failure points: database connectivity issue (CreateAdminUser), email delivery failure (SendWelcomeEmail).
Solution:
- Check database and Redis connectivity; the workflow should retry automatically on transient failures.
- If a specific activity failed permanently, check the activity error message in Temporal UI.
- If saga compensation ran, the tenant may have been rolled back to
DRAFT. You can re-attempt activation. - If the state is inconsistent (partially provisioned), contact engineering for manual cleanup.
10.4 Service Shows Unhealthy on System Status
Symptom: One or more services show red "unhealthy" status.
Diagnosis by service:
| Service | Check | Common Cause |
|---|---|---|
| PostgreSQL | Database container running, connections available | Container restart, disk full, connection pool exhausted |
| Redis | Redis container running, memory available | OOM kill, container restart |
| SeaweedFS | S3 endpoint responsive | Container restart, disk full |
| Quickwit | Cluster API responsive | Index corruption, container restart |
| Temporal | SDK health check responsive | Server restart, worker disconnected |
General solution:
- Check Docker container status: verify the service container is running.
- Check container logs for error messages.
- If a container crashed, check if it auto-restarted (Docker restart policy).
- For persistent issues, check disk space and memory on the host.
10.5 User Reports "Account Locked"
Symptom: User sees "Account locked due to too many failed login attempts."
Details:
- Default lockout threshold: 5 failed attempts within 15 minutes.
- Auto-unlock after 30 minutes.
Solution:
- If the user can wait, the account auto-unlocks after 30 minutes.
- For immediate unlock, enter a SUPPORT_ADMIN session for the tenant and unlock via user management.
- If repeated lockouts occur, investigate for potential brute-force attacks -- check audit logs for suspicious IP addresses.
10.6 Invitation Email Not Delivered
Symptom: Tenant admin reports that invitation emails are not being received.
Diagnosis:
- Check the Temporal
SendInviteWorkflowstatus for the specific invitation. - Verify SMTP configuration is correct in the environment.
- Check email delivery logs (if available).
- Ask the recipient to check spam/junk folders.
Solution:
- If the workflow failed, check the error in Temporal UI and retry.
- If SMTP is down, fix the configuration and the workflow should retry.
- If the email was delivered but filtered, ask the recipient to whitelist
@cadenora.com. - As a workaround, the tenant admin can resend the invitation (generates a fresh token and email).
10.7 Build or Deployment Issues
For general application issues:
# Clear build cache and rebuild
rm -rf .next node_modules/.cache && pnpm install && pnpm build
# Fix rootless Docker health checks (Ubuntu 24.04+)
sudo ./scripts/fix-rootless-docker-healthcheck.sh
# Full environment reset (development only)
./scripts/manage-tunnel-env.sh stop && docker system prune -f && ./scripts/manage-tunnel-env.sh startRemember: Never use docker compose directly on servers. Use ./scripts/deploy-demo.sh or ./scripts/deploy-from-registry.sh for deployments.
Appendix A: Platform API Reference
Authentication and Session
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/platform/users/me | Get current platform user info |
| GET | /api/platform/health | System health check (all services) |
| GET | /api/platform/diagnostics | System diagnostics |
| GET | /api/platform/diagnostics/auth | Authentication diagnostics |
Tenant Management
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/platform/tenants | List all tenants |
| POST | /api/platform/tenants | Create a new tenant (DRAFT) |
| GET | /api/platform/tenants/{tenantId} | Get tenant details |
| GET | /api/platform/tenants/{tenantId}/status | Get tenant status |
| POST | /api/platform/tenants/{tenantId}/activate | Activate a DRAFT tenant |
| POST | /api/platform/tenants/{tenantId}/suspend | Suspend an ACTIVE tenant |
| PATCH | /api/platform/tenants/{tenantId}/configuration | Update tenant configuration |
| GET | /api/platform/tenants/check-subdomain | Check subdomain availability |
| POST | /api/platform/tenants/provision | Provision tenant (alias for activation workflow) |
| GET | /api/platform/provisioning-jobs/{jobId} | Get provisioning job status |
User Management
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/platform/users | List platform users |
| GET | /api/platform/users/{id} | Get user details |
Support Sessions
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /api/platform/support/sessions | Create a support session |
| GET | /api/platform/support/sessions/active | Get active support sessions |
| DELETE | /api/platform/support/sessions/{sessionId} | End (revoke) a support session |
Impersonation
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /api/platform/impersonations | Start an impersonation session |
| GET | /api/platform/impersonations/active | Get active impersonation sessions |
| POST | /api/platform/impersonations/exit | End an impersonation session |
| DELETE | /api/platform/impersonations/{sessionId} | End an impersonation session by ID |
Invitations
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/platform/invitations | List platform invitations |
| GET | /api/platform/invitations/{id} | Get invitation details |
| POST | /api/platform/invitations/{id}/resend | Resend an invitation |
Analytics
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/platform/analytics/redirects/stats | Redirect analytics statistics |
| GET | /api/platform/analytics/redirects/trends | Redirect analytics trends |
| GET | /api/platform/analytics/redirects/top-paths | Top redirected paths |
Appendix B: Data Visibility Quick Reference
Principle: Platform admins operate on the control plane. They see tenant metadata and aggregate counts. They never see individual client records, document content, or end-customer PII without an explicit support session.
Always visible:
- Tenant name, subdomain, status, industry template, theme, admin email
- Aggregate counts (users, projects, documents, client orgs, client members, invites)
- Internal staff list (last 10, FIRM_ADMIN and PROJECT_MANAGER roles only)
- Storage metrics (used vs. quota)
- Security posture (MFA policy, password policy, session timeout)
Never visible without support session:
- Individual client names, emails, or organization details
- Document content, previews, or file names
- Client-document access grants
- Investor profiles (accreditation, preferences)
- Invitation recipient emails
Requires SUPPORT_ADMIN for modification:
- Tenant user accounts (create, disable, role change, MFA reset)
- Tenant documents (upload, delete, change visibility)
- Tenant projects (create, edit, delete)
- Tenant settings (branding, categories, policies)
Appendix C: Related Documentation
| Document | Path | Purpose |
|---|---|---|
| Architecture Spec | docs/architecture/CADENORA_ARCHITECTURE_V8.6.md | Canonical system architecture |
| Product & System Design | docs/CADENORA_PRODUCT_AND_SYSTEM_DESIGN.md | Product-level design reference |
| Platform Admin Visibility | docs/developer/PLATFORM_ADMIN_VISIBILITY.md | Control plane / data plane separation details |
| User Management Guide | docs/admin/USER_MANAGEMENT.md | User lifecycle management |
| Compliance Guide | docs/admin/COMPLIANCE.md | NI 31-103 and PIPEDA compliance |
| Admin Documentation Index | docs/admin/README.md | Admin documentation hub |
| Firm Admin Guide | docs/guides/firm-admin-guide.md | Tenant-level admin operations |
| Deployment Guide | docs/operations/DEPLOYMENT.md | Deployment procedures |
| Upload Fix Documentation | docs/developer/UPLOAD_FIX_DOCUMENTATION.md | Body clone pattern for POST routes |
This document was compiled from the live codebase, platform admin pages, API routes, and architecture documentation. Last updated March 5, 2026. For the canonical architecture specification, see docs/architecture/CADENORA_ARCHITECTURE_V8.6.md.