How Runtime Policies Are Encoded and Managed in Apache Maka
Apache Maka stores runtime policies as versioned JSON documents in each workspace, using an immutable mutation pipeline with optimistic concurrency control to ensure type-safe, reproducible policy changes.
Apache Maka's runtime policy system provides a robust foundation for workspace configuration. Understanding how these policies are encoded, stored, and modified is essential for developers extending the platform or integrating with its runtime host.
Runtime Policy Document Structure
Every workspace contains a runtime-policy.json file in its user-data directory. This file follows a strict schema defined in policy-document.ts (lines 45-52) that includes:
- Schema version – for forward compatibility
- Revision number – monotonically increasing integer
RuntimePolicyobject – the actual policy configuration
The schema ensures that policy documents remain parseable across Maka versions while maintaining a clear audit trail of changes.
Reading and Initializing Policies
When a workspace starts, the RuntimePolicyDocumentOwner class (lines 60-84 of policy-document.ts) handles policy loading:
// From policy-document.ts, line 60-84
class RuntimePolicyDocumentOwner {
async loadPolicy(): Promise<RuntimePolicyDocument> {
const doc = await readBoundedJsonDocument(this.filePath);
if (!doc) {
return createDefaultRuntimePolicy(); // line 64
}
return this.validateAndMigrate(doc);
}
}
The readBoundedJsonDocument helper prevents parsing attacks by enforcing size and depth limits. If no policy exists, createDefaultRuntimePolicy() generates a sensible baseline configuration.
The Mutation Pipeline
All policy changes flow through a four-stage pipeline that guarantees atomicity and consistency.
Stage 1: Client Request
UI components invoke the mutateRuntimePolicy RPC method exposed by the Runtime Host. The input includes an expected revision and operation:
// Example: set a new network proxy via mutation
await deps
.client(uiScope)
.mutateRuntimePolicy({
expectedRevision: current.revision,
operation: {
kind: 'set_network_proxy',
value: { uri: 'http://proxy:8080' }
},
});
Stage 2: Preparation
RuntimePolicyDocumentOwner.prepareMutation (lines 99-108) validates the expected revision against the current document. This optimistic concurrency control prevents lost updates when multiple clients modify the same policy:
// Lines 99-108: revision validation
if (input.expectedRevision !== currentDocument.revision) {
throw new PolicyConflictError(
`Expected revision ${input.expectedRevision}, found ${currentDocument.revision}`
);
}
Stage 3: Apply Mutation
The applyMutation function (lines 136-180) pattern-matches on RuntimePolicyMutation.kind:
// Lines 136-180: exhaustive mutation handling
function applyMutation(
policy: RuntimePolicy,
mutation: RuntimePolicyMutation
): RuntimePolicy {
switch (mutation.kind) {
case 'set_network_proxy':
return { ...policy, networkProxy: mutation.value };
case 'set_memory':
return { ...policy, memory: mutation.value };
case 'patch_agent_settings':
return applyAgentSettingsPatch(policy, mutation.value);
// ... additional mutation kinds
}
}
Supported mutation kinds (defined in operations.ts) include:
set_network_proxy– Configure HTTP/HTTPS proxy settingsset_memory– Adjust memory allocation and limitspatch_agent_settings– Partial updates to agent personalization and retention- Additional kinds for credential material and feature flags
Stage 4: Commit
commitMutation writes the updated document atomically:
// Line 127-128: atomic commit with frozen snapshot
await writeJsonDocument(this.filePath, nextDocument);
return deepFreeze(nextDocument); // immutable return value
The deepFreeze call ensures that returned policy objects cannot be accidentally modified, enabling safe sharing across threads and processes.
In-Memory Caching
The Runtime Host maintains two lookup structures in runtime-host-boot.ts:
// Line 857: WeakMap for UI scope-based lookups
runtimePolicyTargets: WeakMap<
DesktopRuntimeHostTargetPolicy,
DesktopRuntimeHostTargetContext
>;
// Line 858: Map for epoch-based retrieval
runtimePolicyTargetsByEpoch: Map<string, DesktopRuntimeHostTargetContext>;
These caches (actively used in lines 610-888) minimize disk I/O while ensuring policy consistency. The WeakMap allows automatic cleanup when UI scopes are garbage collected, preventing memory leaks in long-running sessions.
Batch Operations Example
Multiple settings can be updated atomically using patch_agent_settings:
// Example: patch several settings at once
await deps
.client(uiScope)
.mutateRuntimePolicy({
expectedRevision: current.revision,
operation: {
kind: 'patch_agent_settings',
value: {
personalization: { preferredLanguage: 'en' },
memory: { retentionDays: 30 },
},
},
});
This single mutation updates both personalization preferences and data retention policy without intermediate states.
Querying Current Policy
Clients retrieve the current policy via queryRuntimePolicy:
// Example: read current runtime policy from UI side
const { policy, revision } = await deps.client(uiScope).queryRuntimePolicy();
// Returns frozen {policy, revision} tuple
The returned revision must be used for subsequent mutations to maintain consistency.
Summary
- Policies are stored as versioned JSON documents (
runtime-policy.json) with schema validation - Optimistic concurrency control via revision numbers prevents conflicting updates
- Immutable mutation pipeline (
prepareMutation→applyMutation→commitMutation) guarantees consistency deepFreezeensures safe sharing across UI, Runtime Host, and worker processes- Dual cache structure (
WeakMap+Map) provides fast lookup by UI scope or epoch
Frequently Asked Questions
What happens if two clients try to modify the same policy simultaneously?
The first successful commit increments the revision number. Subsequent attempts with the now-stale expectedRevision receive a PolicyConflictError and must re-query the current policy before retrying. This optimistic concurrency pattern is enforced in prepareMutation (lines 99-108).
Where are runtime policy mutations defined?
All mutation kinds and their type definitions reside in packages/storage/src/runtime-policy/operations.ts. This includes RuntimePolicyMutation discriminated unions and credential material interfaces. The core RuntimePolicy types are re-exported from packages/core/runtime-policy/index.ts.
How does Maka prevent policy documents from growing unbounded?
The readBoundedJsonDocument helper enforces maximum file size and nesting depth limits when parsing. Additionally, the mutation pipeline replaces entire policy sections rather than appending, keeping document size proportional to configuration complexity rather than edit history.
Can runtime policies be migrated across schema versions?
Yes. The RuntimePolicyDocumentOwner class includes validation and migration logic (referenced in line 64's validateAndMigrate). The schema version field enables forward-compatible parsing and automatic upgrades when older documents are encountered.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →