11 Tenant Auth And Audit
Scope:
- Multi-tenancy and workspace boundaries
- Authentication and authorization convergence
- Audit logging
- Configuration hierarchy governance
- A security baseline aimed at real deployments
T1 tenant-and-workspace-boundaries
- [-] Map the ownership relationships of tenant, workspace, agent, customer, and knowledge base
- [-] Define cross-tenant access prohibition rules
- [-] Define tenant isolation semantics for queries, writes, exports, and background jobs
- [-] Review tenant fields and indexing strategies for core models
Acceptance:
- Any core data object can clearly answer "which tenant/workspace it belongs to"
Current progress:
- The current tenant/workspace ownership status of core objects has been inventoried; see
docs/tenant-workspace-boundaries.md - JWT claims now support normalization and pass through
tenant_id/workspace_id - The auth middleware projects
tenant_id/workspace_idinto the request context for audit, provider calls, and later data filtering - The management surface
/api/*and the protected/api/v1/*now have a unified request-scope guard that rejects requests where the token scope conflicts with the header/query scope, and prevents non-admin/service principals from widening scope through requests KnowledgeDoc,CustomField,SLAConfig, andAppIntegrationnow carry explicit scope fields and filter by context by default at the service/repository layerSLAViolationnow carries explicittenant_id/workspace_idfields, and theslaservice filters by context in the violation creation, listing, resolution, statistics, and monitoring scan pathsShiftSchedulenow carries explicittenant_id/workspace_idfields, and theshiftservice filters by context in the create, list, update, delete, and statistics pathsMacronow carries explicittenant_id/workspace_idfields, and themacroservice filters by context in the list, create, update, delete, and apply entry pathsCustomerSatisfaction/SatisfactionSurveynow carry explicittenant_id/workspace_idfields, and thesatisfactionservice filters by context in the survey send, response, list, statistics, update, and delete pathsSuggestionServicenow has context-filtered similar-ticket/knowledge-base candidate queries and no longer reads scopedTicket/KnowledgeDocacross workspacesGamificationServiceleaderboard aggregation now filtersAgent/Ticket/CustomerSatisfactionby context, avoiding cross-workspace rollups of agent performanceSatisfactionService.GetSatisfactionStatsnow has scoped regression tests, further locking satisfaction category statistics and trend aggregation inside workspace boundariesSession,Message,TransferRecord, andWaitingRecordnow carry explicittenant_id/workspace_idfields and basic indexes, and theconversation/routingrepositories filter by context by default- The legacy
MessageRouterpersistence path now extractstenant_id/workspace_idfromUnifiedMessage.MetadataintoSession/Message, and rejects reusing the samesessionIDacross workspaces Ticketnow carries explicittenant_id/workspace_idfields and basic indexes, and theticketrepository filters by context in the main query, statistics, and create pathsCustomernow carries explicittenant_id/workspace_idfields, and thecustomerrepository scopes reads, lists, statistics, and activity queries through the extended-table scope- The
customermodule activity aggregation chain now has an integration test confirming thatrecent_sessions/recent_tickets/recent_messageskeep filtering by request scope and do not mix other workspaces' customer history into the current timeline - The compatibility layer
CustomerService.GetCustomerStatsnow has scoped regression tests, and fixed a legacy customer statistics join whosecreated_atcondition lacked a table prefix, avoiding ambiguous column errors in scoped statistics during multi-table aggregation Agentnow carries explicittenant_id/workspace_idfields, and theagentrepository filters by context in the create, read, list, and statistics paths- The compatibility layer
AgentService.GetAgentStatsnow has scoped regression tests, confirming the legacy agent statistics entry does not mix other workspaces' agent master data - The management surface
security/users/*andsecurity/tokens/*now additionally validate tenant/workspace ownership against the target user's associatedAgent/Customerscope; cross-scope targets are uniformly treated as not found, preventing enumeration of or accidental operations on other tenants' users/tokens WorkspaceService, theanalyticsmodule aggregation repositories, and the agent transfer runtime load sync paths now filter scoped master data such asSession/Agent/Ticket/Message/Customerby contextanalytics.GetAgentPerformanceStatsand the compatibility layerStatisticsService.GetAgentPerformanceStatsnow have scoped regression tests, and fixed the average resolution time aggregation in the analytics repository to be compatible with sqlite / postgres and other SQL dialects, so the legacy agent performance report is no longer left untestedanalytics.GetTicketCategoryStats/GetTicketPriorityStats/GetCustomerSourceStatsand the compatibility layerStatisticsServiceentries now have scoped regression tests, further closing the cross-workspace leak risk of legacy category, priority, and customer-source aggregationSLAService.GetSLAStatstrend aggregation now has scoped regression tests, confirming the last-7-day compliance view does not mix other workspaces' tickets or violation data- The remaining gap has narrowed further from "core master data unmodeled" to "if tenant-level daily reports are needed later, system-level global rollup tables such as
DailyStatsneed separately designed tenant/workspace dimension versions"; seedocs/tenant-workspace-boundaries.md
T2 auth-and-rbac-convergence
- [x] Inventory the current actual entries for JWT, claims, permissions, and middleware
- [x] Converge the RBAC model and the permission resolution chain
- [x] Distinguish authentication semantics for end-user, agent, admin, and service tokens
- [x] Define different authorization policies for the admin console, public interfaces, and internal interfaces
- [x] Add negative and privilege-escalation tests for key permissions
Acceptance:
- Permission decisions flow through a single testable path and do not depend on scattered handler logic
Current progress:
- JWT validation, claims normalization, role -> permission expansion, and principal kind determination have been converged into
internal/platform/auth internal/middlewarekeeps only compatibility entries and no longer carries independent permission logic- The management surface
/api/*explicitly rejectsend_usertokens; currently onlyagent,admin, andserviceare allowed publicroutes stay anonymous;/api/v1has been split into three surfaces: public realtime, management realtime/AI, and service-only metrics ingest- Surface classification and new-route admission rules are documented; see
docs/auth-surface-policy.md
T3 audit-log-foundation
- [x] Define the audit event model
- [x] Cover key write operations such as ticket changes, routing assignments, configuration changes, and permission changes
- [x] Record actor, tenant, resource, before/after, and request metadata
- [x] Design the query interface and retention policy
- [x] Provide minimal traceability for sensitive operations
Acceptance:
- Key changes are traceable, meeting basic troubleshooting and compliance needs
Current progress:
- A persistent
AuditLogmodel has been added and included in the application migration set - Write requests on the management surface
/api/*are uniformly wired to the audit middleware; successful writes record actor, principal, action, resource, request metadata, and the request payload before/afterhas a middleware extension point; high-value writes such asscoped configandticketupdate/assign/close,agentonline/offline/status switch,customerupdate/note/tag/token revoke, andsecurityuser revoke token now write fine-grained state snapshots- A
GET /api/audit/logsquery interface has been added, supporting filtering by action/resource/principal/actor/success/time-range - The management surface now has
GET /api/audit/logs/:idfor single-record queries, reusing the request scope constraints and supporting reading a specific audit record per tenant/workspace - The management surface now has
GET /api/audit/logs/:id/diff, a generic diff preview interface for field-levelbefore/afterchanges - The management surface now has
GET /api/audit/logs/export, a CSV export interface reusing the list filter parameters and supporting lightweight scope-constrained export - Audit query and retention baselines are documented; see
docs/audit-log-policy.md - A background audit cleanup worker is wired in, batch-deleting expired records on a default 180-day retention window
- Hot/cold tiered archiving has landed (2026-09-28): once
security.audit.archive_dirconfigures an archive directory, the cleanup worker first writes a gzip JSON snapshot of the whole batch to the cold tier before deleting expired records; rows keepprev_hash/entry_hash(the cold tier stays verifiable on the same chain), and archive failure interrupts cleanup without deleting; the default empty value keeps existing behavior; see the policy indocs/audit-log-policy.md
T4 configuration-scopes
- [-] Distinguish system-level, tenant-level, workspace-level, and runtime-level configuration
- [-] Define scopes for AI provider, knowledge provider, routing policy, and similar configuration
- [-] Clarify configuration loading, override, and fallback rules
- [-] Add audit and rollback constraints for configuration changes
Acceptance:
- Configuration is no longer mixed across environment variables, the database, and code defaults
Current progress:
docs/configuration-scopes.mdhas been added, defining the system / tenant / workspace / runtime four configuration layers- A recommended scope matrix is defined for AI provider, knowledge provider, routing policy, portal, and security baseline configuration
- A unified override order is defined:
runtime -> workspace -> tenant -> system -> code default - Audit and rollback constraints for configuration changes are defined, distinguishing system-level release rollback from tenant/workspace configuration restore
internal/platform/configscopehas been added as the unified resolver skeleton, currently covering layered resolution of portal config, OpenAI config, and knowledge provider config- Portal public config reads, AI runtime assembly, and knowledge provider display info in the enhanced health check have switched to the resolver, instead of each stitching system default / runtime override on its own
- The
portal/OpenAI/ knowledge provider resolvers now have tenant/workspace provider interfaces and override order skeletons, currently preferring Dify with WeKnora compatibility retained - Persistent
TenantConfig/WorkspaceConfigmodels and GORM providers have been added, andportaltenant/workspace override reads are wired in - The
OpenAI/ knowledge provider resolvers can now consume the same batch of persistent providers; the management surfaceAIhandler main path and theRuntimeServicemain path both resolve scoped providers by request scope security.session_riskis covered by the same scoped config documentation and resolver; both the auth self-service session list and the management user security session list dynamically resolve tenant/workspace override thresholds by request scope- Calls that carry no scope, such as
context.Background()or anonymous websockets, still naturally fall back to system config - The management surface now has minimal
GET/PUTinterfaces for tenant/workspace scoped config, reading and writingportal/OpenAI/ knowledge provider /session_riskoverrides - Scoped config now has
history/rollbackmanagement interfaces; config writes and restores both writeAuditLogand keep before/after snapshots; rollback runs only when an explicit confirmation parameter is submitted PUT/rollback/verifyfor tenant/workspace scoped config now requirechange_refandreason, supported via JSON body orX-Change-Ref/X-Change-Reasonheaders, which also land in the audit request metadata- High-risk scoped config changes have advanced from "an approval number exists" to "a real approval record exists": when field risk assessment hits a high-risk provider endpoint, KB mapping, session risk threshold, or rollback operation,
PUT/rollbackmust not only submitapproval_refbut also resolve a recordedscoped_config.{scope}.approveaudit event byapproval_ref + change_ref, and the approver and executor must be different people; the interface continues to support JSON body or theX-Approval-Refheader - The management surface now supports
POST /security/config/{scope}/verify/:audit_idfor writing post-execution verification conclusions for update / rollback results; besidesstatus/notes/evidence, the verification body must also submitchecksaligned withverification_template.checks - Scoped config verification has converged from free text to templated check items:
passedmust add evidence, and every required check item in the template must be explicitly markedpassed;failedmust add notes, and at least one template check item must be markedfailed verification_templateis now further broken down by realchanged_pathsand returnschanged_pathsat the template root; individual checks also returnrisk_leveland thechanged_pathsthey cover, directly driving frontend or automation scripts to verify by field risk- The current fine-grained templates cover these high-risk configuration clusters: portal public surface / locale, OpenAI endpoint / model contract, knowledge provider endpoint / knowledge mapping / health check, and session risk score / window / concurrency
- Change governance now has a unified state machine: responses additionally return
governance_status/governance_policy, with current state values covering at leastawaiting_approval,awaiting_verification,verified,verification_failed,approved, andnot_required - Scoped config verification now has a minimal two-person review constraint: the verification reviewer must be an authenticated operator carrying a
user_id, and must be different from the original update / rollback executor - The management surface now returns
change_control/change_risk/approval_policymetadata directly in scoped config write responses, history lists, single-record details, and verify responses;change_controlincludesapproval_ref,change_riskgivesrisk_level/risk_reasons/changed_paths, andapproval_policydeclares whether approval must be added, whether the approval record is persisted, the latest approval audit ID / time / approver, and other real pre-execution approval information - Verify responses also return
source_governance_status/source_governance_policy, for directly judging whether the verified source change has converged - The management surface now returns
operation/ preview / rollback / verify /verification_status/latest_verification/verification_template/verification_policymetadata directly in scoped config history lists, single-record details, and verify responses;verification_policyadditionally declareschecks_required,required_check_ids,template_check_count, and failed-state check constraints, so frontend or automation scripts can render the post-execution verification flow by template GET /security/config/{scope}/historynow works directly as a governance workbench data source, supportinggovernance_status/risk_level/approval_status/verification_status/needs_actionfilters and returninggovernance_summaryaggregate counts plusapplied_filters, for rendering the pending change queue- Still missing are a more general cross-configuration-domain write interface, plus more complete two-person review orchestration, automated acceptance templates, and post-release verification rule orchestration
T5 security-baseline-for-operations
- [x] Inventory high-risk interfaces and high-risk operations
- [x] Add rate limiting, permission fallbacks, and logging for critical operations
- [x] Add minimal standards for token lifecycle, key rotation, and sensitive field masking
- [x] Add a basic security checklist for externally exposed interfaces
Acceptance:
- The project has the minimal security governance skeleton needed before entering real deployment
Current progress:
docs/security-baseline-operations.mdhas been added, inventorying the three high-risk surfaces (management / service / public) and key interfaces- Base routes uniformly mount
RateLimitMiddlewareFromConfig, with global and path-prefix limits keyed by socket peer IP; untrusted request headers cannot rotate identities or bypass the limiter - Management write interfaces are confirmed to uniformly stack
AuthMiddleware,EnforceRequestScope,RequirePrincipalKinds,RequireResourcePermission, andAuditMiddleware - The audit middleware now uniformly masks sensitive fields such as
password,secret,api_key, andtoken, preventing plaintext secrets from landing in audit logs docs/token-lifecycle-and-key-rotation.mdhas been added, defining minimal lifecycles and rotation flows for JWT / provider keys / service tokensconfig.production.secure.example.ymlhas been added, providing a production example template with rate limiting enabled, tightened CORS, and secrets injected via environment variablesdocs/public-surface-security-checklist.mdhas been added, converging the minimal review items for anonymous / externally exposed interfaces and a per-entry checklist- Bootstrap startup security warnings have been added, warning on high-risk configurations such as the default
jwt.secret, open CORS, disabled rate limiting, and empty provider keys - Startup security warnings and
check-security-baseline --strictnow further cover the public / high-risk entry rate limiting baselines: if/public/,/public/kb/,/public/csat/,/api/v1/auth/,/api/v1/ws,/uploads/,/api/v1/metrics/ingest, or/api/has no dedicated path-level rate limit, an explicit warning fires at startup and at the pre-deployment validation stage - The default development config
config.ymlnow passes thesecurity/observabilitystrict baseline checks, but the docs make clear its purpose is still local development and it must not replace the production deployment config template internal/platform/authnow has composable token policy hooks, supporting issued-before invalidation byiatand minimal session version eviction bytoken_version, reserving integration points for later account-level revocation policiesinternal/platform/authnow has a token policy based onuserstable status anduser_auth_sessions, wired into the router auth middleware: non-active users are rejected; settingtoken_valid_after/token_versioninvalidates old tokens; login and refresh now also carrysession_id + session_token_version, so old tokens of the same auth session become invalid after refresh or a targeted revoke/api/v1/auth/login,/api/v1/auth/register, and/api/v1/auth/refreshnow return a separaterefresh_token; the refresh path no longer depends on a still-valid access token; the refresh token carriestoken_use=refreshand the currentsession_token_version; after a refresh the old refresh token becomes invalid immediately, and remains subject to user/session revoke,token_valid_after, andtoken_versionconstraints- The auth issuance chain now injects a unique
jtiinto access / refresh tokens;internal/platform/authhas added a revoke list policy, and the management surface can explicitly add a single JWT to the denylist viaPOST /api/security/tokens/revoke, invalidating it immediately before expiry - The management surface now has
GET /api/security/tokens/revokedfor querying the revoke list, filterable byjti/user_id/session_id/token_use, and supports viewing only denylist records that have not yet expired - The revoked token denylist is wired to a background cleanup worker that automatically purges expired revoke records by token
expires_at, preventing long-term denylist bloat - The agent management surface now has
POST /api/agents/:id/revoke-tokens, which proactively bumps the user'stoken_versionand refreshestoken_valid_afterto invalidate old tokens - The customer management surface now has
POST /api/customers/:id/revoke-tokens, which proactively triggers old token invalidation for scope-constrained customer accounts - The underlying token revocation logic has converged to
internal/platform/usersecurity; the agent/customer management paths reuse the sametoken_valid_after + token_versionupdate implementation - The management surface now has a unified
securitysurface:GET /api/security/users/:idqueries a single user's security status;POST /api/security/users/querybatch-previews users' current security state and the nexttoken_versionafter a revoke;GET /api/security/users/:id/sessionsandPOST /api/security/users/:id/sessions/revokeview and invalidate a single auth session;POST /api/security/users/:id/revoke-tokensandPOST /api/security/users/revoke-tokensexecute single-user / batch user revokes respectively, all protected bysecurity.read/security.writepermissions - The above
security/users/*entries now validate target user scope: if the request context carriestenant_id/workspace_id, only users associated withAgent/Customerin that scope may be operated on, and cross-scope targets uniformly return not found security/tokens/revokeandsecurity/tokens/revokedfollow the same scoped user constraint: scoped requests can only revoke or view token records of users associated withAgent/Customerin their own scope, and cannot operate the denylist across tenants- The management surface now has
POST /api/security/users/:id/sessions/revoke-all, which batch-invalidates all active auth sessions of the same user and supports keeping one session viaexcept_session_id, for "log out all devices" or "kick only other devices" scenarios - The route governance layer now has a code-level
security surface catalogthat validates anonymous / open surfaces against the realBuildRouter()route table;/api/v1/auth/*and/uploads/*are also covered by the same public surface governance and dedicated rate limiting baseline UserAuthSessionnow has minimal client metadata: login and refresh record and updateuser_agent,client_ip, andlast_seen_at, and the management surfaceGET /api/security/users/:id/sessionsshows a basic device view directly- The auth self-service surface now has
GET /api/v1/auth/sessions,POST /api/v1/auth/sessions/logout-current, andPOST /api/v1/auth/sessions/logout-others; logged-in users can view their own auth sessions and perform "log out current session" or "kick other devices" UserAuthSessionnow further carriesdevice_fingerprint:X-Device-IDtakes precedence, otherwise a stable hash is derived fromUser-Agent + ClientIP, making it easy to group repeated refreshes / multiple sessions of the same device- Both the auth / management session lists now return derived security view fields
network_label,location_label,risk_score,risk_level,risk_reasons, plus session-family metrics such asfamily_public_ip_count/family_device_count/active_session_count/family_hot_refresh_count/reference_session_id/ip_drift/device_drift/rapid_ip_change/rapid_device_change/refresh_recency/rapid_refresh_activity; they currently distinguish loopback/private/public, provide minimal location hints for documentation-reserved ranges / shared address space, and turn multiple public IPs under one account, excessive active sessions, device/IP drift relative to the most recently active session, rapid switching within a 24-hour window, and short-window refresh activity into explainable risk hints - The Geo/IP enrichment layer of session risk has been extracted from inline handler rules into an injectable
session IP intelligenceadapter; the default still uses the existing heuristic provider, and both the auth self-service and management user-security sides can plug in a real Geo/IP provider without touching the core risk logic, with an HTTP provider and handler-level automated evidence already in place on both sides - An
HTTP session IP intelligence providerthat can land on real data sources and thesecurity.session_ip_intelligenceconfig item have been added; whenenabled + base_urlis configured, the auth / management session lists pullnetwork_label/location_labelfrom an external endpoint, automatically falling back to local heuristic classification on failure; the adapter now has compatibility regressions for nesteddatapayloads and custom auth headers beyondAuthorization - The session risk heuristic thresholds are now centralized in a unified
session risk policywired to thesecurity.session_riskconfig surface; an environment-level baseline viaserver.environment + security.session_risk_profiles.{environment}has been added, still supporting tenant / workspace / runtime layer-by-layer refinement, with automated coverage on both the auth self-service and management user-security sides - This round's
G2-*closure criteria are met: legacy aggregation boundaries, the HTTP Geo/IP provider adapter, the environment-level session risk policy, and the audit/rollback operational constraints all have code, test, and documentation evidence; plugging in any specific external Geo/IP vendor later should be treated only as provider selection or deployment acceptance enhancement, not as a blocker for this round servify check-security-baseline --strictandscripts/check-security-baseline.shhave been added, upgrading the existing security warning rules into explicit failing checks before deployment, instead of relying only on startup log reminders- T5 has reached the "minimal security governance skeleton" acceptance goal; later enhancement items should be tracked separately and are no longer closure blockers for this round
Example session list response (the auth / management side field sets are identical; is_current is returned only on the auth self-service side):
json
{
"session_id": "sess-current",
"status": "active",
"token_version": 3,
"device_fingerprint": "fp-2d9c6c",
"network_label": "public",
"location_label": "public_unknown",
"risk_score": 5,
"risk_level": "high",
"risk_reasons": [
"public_network",
"multi_public_ip_family",
"ip_drift"
],
"family_public_ip_count": 2,
"family_device_count": 2,
"active_session_count": 2,
"family_hot_refresh_count": 2,
"reference_session_id": "sess-latest",
"ip_drift": true,
"device_drift": false,
"rapid_ip_change": true,
"rapid_device_change": false,
"refresh_recency": "hot",
"rapid_refresh_activity": true
}Explicitly left for separate future items:
- Real Geo/IP intelligence integration
- Stronger time-series / cross-session anomaly detection
- Session risk policies tiered by environment, tenant, or role
- Finer-grained approval / automated review rules for public interfaces
- Fuller user security operation capabilities such as approval rollback