Configuration Scopes
This document defines the scope boundaries, override order, and change constraints of Servify's current configuration, to advance 11 / T4 configuration-scopes.
Goals:
- Distinguish which configurations can only be system-level
- Distinguish which configurations should sink down to tenant / workspace
- Avoid continuing to mix secrets, provider endpoints, business policy, and runtime switches in the same
config.yml - Provide unified rules for subsequent configuration persistence, audit, and rollback
Current Configuration Sources
The repo currently has 4 kinds of configuration sources:
Code defaults
- Entry:
apps/server/internal/config/config.go - Purpose: guarantee local dev and default startup are runnable
- Limitation: can only carry safe defaults, cannot carry production secrets
- Entry:
System startup config files
- Entry:
config.yml,config.weknora.yml - Load:
apps/server/internal/app/bootstrap/config.go - Purpose: deployment-level infrastructure and system default policy; of these
config.weknora.ymlis mainly for WeKnora compatibility regression; the knowledge-base selection chain is ragflow → dify → weknora → pgvector direct (RAGFlow and WeKnora are the canonical knowledge-base dual paths, Dify retained as orchestration-platform dataset compatibility; the full selection landscape is in KNOWLEDGE_BASE_LANDSCAPE, the RAGFlow integration is in RAGFLOW_INTEGRATION)
- Entry:
Environment variable overrides
- Entry: Viper
AutomaticEnv() - Purpose: container deployment, CI, secret injection
- Limitation: should only override system-level config, should not carry tenant / workspace business policy
- Entry: Viper
Database-stored scoped configuration objects
- Currently existing:
SLAConfigCustomFieldAppIntegration
- Characteristic: already carry
tenant_id/workspace_id, filterable by context
- Currently existing:
There is also one more kind of non-persistent runtime scope input:
tenant_id,workspace_idin JWT / request context- Temporary parameters in AI / knowledge requests, e.g.
knowledge_id - Temporary execution-state parameters in routing, transfer, session
These belong to runtime scope, not long-term configuration.
Scope Model
1. System Scope
Definition:
- Shared by the entire deployment instance
- Only modifiable by ops, deployment processes, or system administrators
- Does not switch with tenant / workspace
Typical content:
serverdatabaseredisjwt.secretlogmonitoring- tracing / exporter endpoint
- Global CORS baseline
- Global rate limiting baseline
- Upload root path, filesystem paths
- AI / knowledge provider base endpoints, client credentials, global timeouts
Storage principles:
- Prefer config file + environment variables
- Secrets are injected only via environment variables or an external secret manager
- Should not be stored in tenant config tables
2. Tenant Scope
Definition:
- Shared by one business tenant within the same deployment
- Takes effect for all workspaces under the tenant
- Allowed to be overridden at finer granularity by workspaces
Typical content:
- Tenant default AI provider selection
- Tenant default knowledge provider / knowledge base namespace
- Tenant default routing / SLA / automation policy
- Tenant default portal branding
- Tenant-level integration switches and capability policy
- Tenant-level security policy supplements, e.g. API surface allowlist, public knowledge base switches
Storage principles:
- Should go into database config objects or a dedicated tenant config model
- Should no longer be hard-coded in
config.yml - If credentials are involved, prefer storing a "secret reference" over plaintext
3. Workspace Scope
Definition:
- Business configuration of a specific workspace under a tenant
- Prioritizes service-team, business-line, and channel-face differences
Typical content:
CustomFieldSLAConfigAppIntegration- Workspace-level routing policy
- Workspace-level AI prompt/profile
- Workspace-level knowledge source selection
- Workspace-level portal appearance, default language, channel switches
Storage principles:
- Should be persisted to the database
- Must carry
tenant_id+workspace_id - Must go through audit
4. Runtime Scope
Definition:
- Temporarily takes effect during a single request, single task, single session execution
- Should not be persisted as long-term configuration
Typical content:
tenant_id/workspace_idin the request context- Whether retrieval / fallback is enabled for this AI request
- Priority, target skills, notes of this transfer
- Time range, filters, sort of this export
Storage principles:
- Exists only in request / job / event / session context
- Not persisted by default
- If it affects high-risk behavior, it should go into the audit log
Configuration Matrix
Infrastructure & Platform
server:systemdatabase:systemredis:systemwebrtc.stun_server:systemjwt.secret/ token lifecycle baseline:systemlog/monitoring/ tracing exporter:systemupload.storage_path:system
Security & Access
security.cors:systemsecurity.rbac.roles:systemsecurity.rate_limitingdefaults:systemsecurity.session_riskbaseline:system, allowstenant/workspaceto refine risk thresholds within an authorized scopesecurity.session_risk_profiles.{environment}:system, used to select the deployment-environment-level risk baseline byserver.environment, then stacked with tenant / workspace overridesecurity.session_ip_intelligenceprovider endpoint / API key / timeout:system- Whether a tenant / workspace allows public knowledge bases, Portal, specific integrations:
tenantorworkspace
AI Provider
- Provider endpoint, API key, client secret, global timeout:
system - Default provider registry and fallback baseline:
system - Tenant default provider selection, default model, default prompt profile:
tenant - Workspace model selection, temperature, tool policy targeting a business line:
workspace - Whether a single request force-disables retrieval / fallback:
runtime
Conclusions:
- Provider credentials should not become workspace configuration
- Workspace can only override "policy selection", cannot directly hold system-level infrastructure parameters
Knowledge Provider
- Provider endpoint, global API key:
system - Tenant default
knowledge_base_id/ namespace mapping:tenant - Workspace default knowledge base, retrieval threshold / topK / strategy:
workspace - Per-query temporary
knowledge_id, temporary retrieval strategy:runtime
Routing Policy
- Global fallback policy and implementation switches:
system - Tenant default queueing/assignment/load policy:
tenant - Workspace channel differences, skill priority, human-handoff threshold:
workspace - Per-session transfer reason / target skills / priority:
runtime
Portal / UX / Business Policy
- System default
portalappearance and locale:system - Tenant branding:
tenant - Workspace portal display, public help center, channel entries:
workspace
Override Order
The unified override order is defined as:
runtimeworkspacetenantsystemcode default
Explanation:
code defaultonly handles fallbacksystemhandles deployment-level defaultstenanthandles tenant-level business defaultsworkspacehandles team/channel-level differencesruntimeonly does temporary override for this execution
Constraints:
- Lower levels cannot override higher-level secrets
runtimecannot break permission boundaries, can only narrow or temporarily select within an authorized scope- With no explicit workspace config, fall back to tenant
- With no tenant config, fall back to system
Current Recommended Landing Points
Content that stays in the system config file
serverdatabaseredisjwtlogmonitoring- Provider endpoint / credentials
- Upload root path
Content that should gradually move out of the system config and into scoped config
portal.brand_name,logo_url, colors, locale- AI provider default model selection and prompt profile
- Knowledge base default mapping
- Routing policy
- Workspace-level integrations enablement
Current code skeleton:
internal/platform/configscope.Resolveralready supportsportal,OpenAI,Dify / RAGFlow / WeKnora knowledge providerwith thetenant -> workspace -> runtimeprovider override orderai.providerselection (openai | anthropic | local) is a single global decision, not going through scope override; the openai-family scope-resolution result (key/base_url/model etc.) is written into each outbound LLM call at the orchestration layer, anthropic only consumes the globalai.anthropicsection,localis a zero-external-dependency extractive-QA baseline (seeconfig.ymlai.provider comment) — switching vendors changes one placeai.provider, unified construction is collected atinternal/platform/llm/factoryinternal/platform/configscope.Resolvernow supports thesession_risksystem -> tenant -> workspace -> runtimerisk-threshold resolution chain- Database-persisted
TenantConfig/WorkspaceConfigGORM providers have been added; the current public portal already integrates this read path - The admin surface has the minimal read/write entry for tenant/workspace scoped config, currently covering
portal/OpenAI/ knowledge provider /session_risk - The scoped config write API now records before/after snapshots; tenant/workspace-level
PUT/rollback/verifyalso strictly requirechange_refandreason, and write these change-control fields into audit request metadata - For
PUT/rollbackhitting the high-risk config cluster, the current API not only requiresapproval_ref, but also requiresapproval_ref + change_refto match a recordedscoped_config.{scope}.approveaudit event, and the approver and the actual executor must be different people; the API still supports JSON body orX-Approval-Refheader; the current high-risk coverage mainly includes provider endpoint, knowledge mapping, session risk core thresholds, and the rollback operation itself - Scoped config also provides tenant/workspace-level change history list metadata, field-path-level diff preview of a single history detail, current/snapshot value pairs with
added/removed/updatedtypes, and a rollback-by-audit-record entry requiring explicit confirmation - The admin surface has gained a post-execution verification entry, able to write
passed/failedverification results for a single update / rollback audit record; besidesstatus/notes/evidence, verification submission also needs to carrychecksaligned withverification_template.checks - Verification is now collected into templated checks:
passedmust carry evidence, and all required check items in the template must be explicitlypassed;failedmust carry notes, and at least one template check item must be markedfailed verification_templatenow returns root-levelchanged_paths, and breaks a single check item down by field risk into portal public surface / locale, OpenAI endpoint / model, knowledge provider endpoint / KB mapping / health check, session risk score / window / concurrency; each check also carriesrisk_leveland thechanged_pathsit hit- Write responses, history lists, details, and verify responses also return
change_control,change_risk, andapproval_policy; of thesechange_controlcarriesapproval_ref,change_riskgivesrisk_level/risk_reasons/changed_paths, andapproval_policynow further distinguishes real approval states like providing a ticket number only, approval event persisted, and latest approver - A unified
governance_status/governance_policyis also returned, combining pre-approval and post-execution verification into a single state machine; the verify response additionally returnssource_governance_status/source_governance_policy - Verification now enforces a minimal two-person review constraint: the reviewer must carry
user_idand must not be the same as the original change executor; history lists, details, and verify responses giveverification_status, the latest verification record, verification history,verification_template, andverification_policycarryingchecks_required/required_check_ids/template_check_count - The
historylist also supports querying the governance queue directly bygovernance_status/risk_level/approval_status/verification_status/needs_action, and additionally returnsgovernance_summaryandapplied_filters, so the admin console can directly render the pending-approval / pending-verification workbench
Recommended positioning of the current database config objects
SLAConfig:workspaceCustomField:workspaceAppIntegration:workspace- To be added later:
TenantConfigWorkspaceConfig- Or finer-grained
AIConfigOverride/RoutingPolicyConfig
Audit and Rollback Constraints
System-level config
- Modification entries should be treated as ops changes
- Must leave traces via deployment records, Git changes, or config-center version records
- Direct plaintext modification of system secrets in admin APIs is forbidden
Tenant / Workspace config
- Must go through the admin write API
- Must record audit logs
- Must support reading the most recent effective version
- Before/after snapshots should be retained around modifications
Runtime config
- Not persisted by default
- If it is a high-risk action, e.g. forcing a provider switch, bypassing fallback, switching knowledge source, it should at least record audit or structured logs
Rollback
- system-level config is handled via config version / release rollback
- tenant / workspace config is handled via database version snapshots or "restore previous version"
- runtime has no general rollback, only re-initiating execution
Current Gaps
TenantConfig/WorkspaceConfigalready exist (internal/models/models.go), and the public portal read path is integrated (see above); later, finer-grainedAIConfigOverride/RoutingPolicyConfigcan be extended on this basis- Viper +
config.ymlstill carries some business defaults that should later sink to tenant/workspace portalreads are partially scoped: the public config route merges system, tenant, and workspace values; a consistent admin UI for managing these overrides is not available- AI / knowledge overrides are partially implemented for OpenAI, Dify, WeKnora, RAGFlow, and the translation quota
limit; this is not a universal per-feature override layer. Routing-policy overrides are not implemented yet, and each provider/config family still needs explicit field-level scope and governance decisions DailyStatsis still a system-level global aggregation, without tenant/workspace dimensions
Template Governance and Environment Strictness (P2-2)
Template drift gate: apps/server/internal/config/config_template_lint_test.go aligns the repo's four templates (config.yml, config.staging.example.yml, config.production.secure.example.yml, config.weknora.yml) with the Config structure's yaml schema:
- Unknown keys fail directly — viper silently ignores keys outside the schema, a typo silently falls back to the default; this gate makes it fail explicitly at CI time. When adding config items, change the structure, change the templates, the test covers it automatically.
- The
databasesection andjwt.secretof the staging / production templates must be explicitly present and use${ENV}placeholders — template absence falls back to code default dev values and gets rejected by the startup validation, the template loses usability; plaintext credentials are not allowed in any template. - Template-intentionally-omitted non-sensitive keys (going through
GetDefaultConfigdefaults) are allowed.
Environment strictness: server.environment supports dev/development, stage/staging, prod/production and case aliases, normalized at load time (canonicalEnvironment), preventing spelling from bypassing strict validation.
development: known-unsafe defaults (dev JWT secret, dev database password, WeKnora default key) only produce warnings, runnable locally.stagingandproduction: any known-unsafe default rejects startup — placeholder credentials (${ENV}expanding to empty when not injected) are also rejected, this is expected behavior: pre-production and production do not allow starting services with placeholder credentials.
Credential injection path: secrets in templates are always ${ENV_VAR} placeholders, the load chain is expandEnvVarsInConfig (expansion before read) → viper parse → normalizeConfig (including environment normalization) → Validate strictness tiering → applyConfigEnvOverrides (OPENAI_API_KEY and other explicit environment overrides). Environment variable overrides are only used for system-level config, do not carry tenant/workspace policy (see "Scope Model").
Subsequent Implementation Order
- Add
TenantConfig/WorkspaceConfigdocumented data models - Gradually migrate
portal, AI provider default, knowledge default, routing policy out ofconfig.yml - Build a unified resolver for config reads:
runtime -> workspace -> tenant -> system -> default - Add audit snapshots and restore entries for config changes