Config inheritance
Config inheritance is one of Stratum’s defining features. Parent tenants set defaults, children inherit or override them, and locked keys enforce consistency across the tree.
How inheritance works
Section titled “How inheritance works”Config values flow root to leaf. When you resolve config for a tenant, Stratum walks up the ancestry path and merges entries. For each key:
- If the tenant has its own value, that wins (unless the key is locked by an ancestor)
- Otherwise, the nearest ancestor’s value is inherited
- Locked keys cannot be overridden at any depth
Root: max_users = 1000 (unlocked), theme = "dark" (locked) MSP: max_users = 500 ← overrides root Client A: (no overrides) ← inherits max_users=500, theme="dark" Client B: max_users = 100 ← overrides MSPSetting config
Section titled “Setting config”Single key
Section titled “Single key”await stratum.setConfig(tenantId, "max_users", { value: 1000, locked: false,});Via the API:
curl -X PUT http://localhost:3001/api/v1/tenants/TENANT_ID/config/max_users \ -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"value": 1000, "locked": false}'Batch set
Section titled “Batch set”Set up to 200 keys in a single atomic transaction:
await stratum.batchSetConfig( tenantId, [ { key: "max_users", value: 1000, locked: false }, { key: "theme", value: "dark", locked: true }, { key: "api_secret", value: "sk_live_abc", sensitive: true }, ], auditContext);Via the API:
curl -X PUT http://localhost:3001/api/v1/tenants/TENANT_ID/config/batch \ -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "entries": [ {"key": "max_users", "value": 1000, "locked": false}, {"key": "theme", "value": "dark", "locked": true} ] }'If any key is locked by an ancestor, the entire batch rolls back and nothing is written. The API then answers 403 CONFIG_LOCKED, with the per-key result in error.details.
Resolving config
Section titled “Resolving config”Merged view
Section titled “Merged view”Returns the effective config for a tenant with all inheritance applied:
const config = await stratum.resolveConfig(tenantId);Each entry in the result:
{ "max_users": { "key": "max_users", "value": 500, "source_tenant_id": "msp-uuid", // which tenant set this value "inherited": true, // true = came from an ancestor "locked": false }}Inheritance view
Section titled “Inheritance view”Returns the full ancestry-aware view showing where each key is set at every level:
const inheritance = await stratum.getConfigWithInheritance(tenantId);Via the API:
curl http://localhost:3001/api/v1/tenants/TENANT_ID/config/inheritance \ -H "X-API-Key: YOUR_KEY"Locking keys
Section titled “Locking keys”When a key is locked, no descendant can override it:
// Root sets max_users as lockedawait stratum.setConfig(rootId, "max_users", { value: 1000, locked: true,});
// Child tries to override: fails with ConfigLockedError (CONFIG_LOCKED, HTTP 403 from the API)try { await stratum.setConfig(childId, "max_users", { value: 500 });} catch (err) { // ConfigLockedError: Config key "max_users" is locked by ancestor}Deleting config
Section titled “Deleting config”Remove a tenant’s override for a key. If an ancestor has set the key, the tenant reverts to inheriting that value:
await stratum.deleteConfig(tenantId, "max_users");Via the API:
curl -X DELETE http://localhost:3001/api/v1/tenants/TENANT_ID/config/max_users \ -H "X-API-Key: YOUR_KEY"Sensitive values
Section titled “Sensitive values”Mark a config value as sensitive to encrypt it at rest with AES-256-GCM:
await stratum.setConfig(tenantId, "api_secret", { value: "sk_live_abc123", locked: true, sensitive: true,});Sensitive values are encrypted before storage. The encryption key is set via the STRATUM_ENCRYPTION_KEY environment variable.
The flag belongs to the key, not to a single write:
- An override of a key that an ancestor marked sensitive is stored sensitive, even when the write passes
sensitive: false. - A write that leaves out
sensitivekeeps the key’s current flag. A new key starts not sensitive. - An explicit
sensitive: falseclears a flag that the tenant set itself. - When a tenant stores a key as sensitive, the overrides of that key in its descendants are stored sensitive and encrypted in the same transaction.
batchSetConfig follows the same rules for each entry, and rejects a batch that names the same key more than once.
After upgrading from a version that did not apply the flag to overrides, run stratum.applySensitiveConfigFlags() once. See Applying the sensitive flag to existing overrides.
Descendants inherit a sensitive value, but a read of a descendant’s config returns it masked:
const resolved = await stratum.resolveConfig(childId);// resolved.api_secret:// { key: "api_secret", value: null, source_tenant_id: parentId,// inherited: true, locked: true, sensitive: true, masked: true }
// Trusted server code that needs the secret itself:const forServer = await stratum.resolveConfig(childId, { revealSensitive: true });// forServer.api_secret.value === "sk_live_abc123"A tenant’s own sensitive values are decrypted when resolved. The control plane API and the React ConfigEditor reveal an inherited sensitive value only to a key of the tenant that set it; see Config API. See the field-level encryption documentation for key rotation procedures.
Config values
Section titled “Config values”Config values can be any JSON type: strings, numbers, booleans, objects, or arrays:
await stratum.setConfig(tenantId, "features", { value: { dark_mode: true, beta_access: false },});
await stratum.setConfig(tenantId, "retention_days", { value: 90,});
await stratum.setConfig(tenantId, "allowed_domains", { value: ["example.com", "acme.corp"],});Common patterns
Section titled “Common patterns”Feature flags
Section titled “Feature flags”// Root enables a feature for the entire treeawait stratum.setConfig(rootId, "feature_audit_log", { value: true, locked: false,});
// MSP disables it for its subtreeawait stratum.setConfig(mspId, "feature_audit_log", { value: false,});Tiered limits
Section titled “Tiered limits”// Set defaults at rootawait stratum.setConfig(rootId, "max_api_calls", { value: 10000 });await stratum.setConfig(rootId, "max_storage_gb", { value: 50 });
// Premium MSP gets higher limitsawait stratum.setConfig(premiumMspId, "max_api_calls", { value: 100000 });await stratum.setConfig(premiumMspId, "max_storage_gb", { value: 500 });Compliance settings
Section titled “Compliance settings”// Lock encryption requirement across the treeawait stratum.setConfig(rootId, "require_encryption", { value: true, locked: true,});
// Lock data residency regionawait stratum.setConfig(euMspId, "data_region", { value: "eu-west-1", locked: true,});