How Secrets Are Stored in `credential-vault.json` and the Security Boundary Between Renderer and Main in Apache Maka

Apache Maka stores sensitive secrets in a validated JSON vault file that is accessible only to the Electron main process, enforcing a strict security boundary where the renderer process can request credential operations via IPC but never receives raw secret values back across the process boundary.

Apache Maka implements a hardened credential storage system that keeps API keys and passwords in credential-vault.json while maintaining rigorous isolation between the Electron renderer and main processes. This architecture ensures that untrusted UI code cannot access sensitive data directly, with all persistence logic confined to the trusted main process. The system validates every write operation against strict size and schema constraints before committing secrets to disk.

The credential-vault.json Storage Format

credential-vault.json serves as the on-disk persistence layer for Maka’s runtime-policy system. The file stores encrypted credentials as a structured JSON document with strict validation rules enforced by the CredentialVaultDocumentOwner class in packages/storage/src/runtime-policy/credential-vault-document.ts.

Schema Structure and Hard Limits

The vault document contains a top-level object with three core components: a schema version, a monotonic revision number, and an entries array. The implementation enforces rigid capacity constraints to prevent abuse or accidental bloat:

  • Maximum entries: MAX_VAULT_ENTRIES = 2,048 (line 49)
  • Maximum secret length: MAX_SECRET_LENGTH = 64 * 1024 (64 KB per secret, line 50)
  • Maximum document size: VAULT_DOCUMENT_MAX_BYTES (line 36-43) prevents the JSON payload from exceeding configured byte limits during write operations

When persisting changes, the writeJsonDocument method validates the entire serialized payload against these limits before atomic write, ensuring the vault cannot grow unbounded or corrupt due to oversized secrets.

The CredentialVaultEntry Shape

Each entry within the entries array follows the CredentialVaultEntry interface defined at lines 54-58, containing five fields:

  • locator: Describes the credential’s context (connection ID, web-search provider, or network-proxy scope)
  • credentialId: A UUID that uniquely identifies the specific credential
  • revision: Monotonically increasing integer for optimistic-concurrency checks
  • secret: The raw secret string supplied by the user (up to 64 KB)
  • updatedAt: Millisecond timestamp of the last mutation

The separation of locator and credentialId allows Maka to update credentials in place while maintaining stable references across configuration changes.

Security Boundary Between Renderer and Main Process

Maka leverages Electron’s process architecture to establish a cryptographic security boundary. The renderer process (Chromium-based UI) operates with reduced privileges, while the main process (Node.js) maintains exclusive filesystem access and secret handling capabilities.

Process Isolation Architecture

The renderer process is explicitly prohibited from direct filesystem access. It interacts with secrets exclusively through the runtime-host IPC channel, which forwards high-level requests such as set credential, delete credential, or query status to the main process. Conversely, the main process owns the CredentialVaultDocumentOwner class and performs all validation, size-checking, and JSON serialization before persisting to credential-vault.json.

This design ensures that even if the renderer were compromised via malicious third-party scripts or dev-tools exploitation, the attacker could not extract raw secrets from the local filesystem directly.

IPC Data Flow and Secret Containment

The security boundary operates through a strict three-phase protocol:

  1. Renderer → Main: The renderer sends a request object via ipcRenderer.invoke (e.g., { kind: "set", locator: ..., secret: "..." }) through the maka:set-credential channel
  2. Main Process Validation: The main process calls CredentialVaultDocumentOwner.prepareSet or prepareDelete, runs schema validation (checking version, entry limits, and secret length at lines 32-33 and 41-43), and writes the updated vault using writeJsonDocument
  3. Main → Renderer: The main process returns a snapshot containing only metadata (credentialId, revision, updatedAt, and a configured flag); the secret field is explicitly never serialized back across the IPC boundary

Because the raw secret string remains confined to the main process and is filtered from all IPC responses, the renderer cannot accidentally expose credentials through memory inspection or network logging.

Implementation Examples

Main Process Credential Management

In the Electron main process, the CredentialVaultDocumentOwner handles all vault operations. The following example demonstrates reading the vault and storing a new API key:

// Main-process side (runtime-host)
import { CredentialVaultDocumentOwner } from '@maka/storage/runtime-policy/credential-vault-document.js';

// Read the current vault
const owner = new CredentialVaultDocumentOwner();
const vault = await owner.read('/path/to/workspace');

// Add or update a credential (typically called from IPC handler)
await owner.set('/path/to/workspace', {
  locator: { 
    scope: 'connection', 
    connectionId: 'conn-123', 
    kind: 'api_key' 
  },
  secret: 'my-super-secret',
  expected: null,  // No optimistic-concurrency expectation
});

The set method internally performs byte-size validation against MAX_SECRET_LENGTH and VAULT_DOCUMENT_MAX_BYTES before atomic write.

Renderer Process Interaction

The renderer process uses Electron’s ipcRenderer to request credential operations without touching the filesystem:

// Renderer-process side (frontend)
import { ipcRenderer } from 'electron';

// Ask the main process to store a secret
ipcRenderer.invoke('maka:set-credential', {
  locator: { 
    scope: 'connection', 
    connectionId: 'conn-123', 
    kind: 'api_key' 
  },
  secret: 'my-super-secret',
}).then((result) => {
  // result.snapshot contains only metadata: credentialId, revision, updatedAt
  // The secret itself is NOT included in the response
  console.log('Credential stored, snapshot:', result.snapshot);
});

The renderer receives only the metadata snapshot, confirming successful storage without exposing the sensitive value.

Key Files and Components

File Role
packages/storage/src/runtime-policy/credential-vault-document.ts Implements CredentialVaultDocumentOwner, vault schema validation, and read/write logic (see lines 49-53 for limits, 54-58 for entry shape, 32-43 for validation)
packages/storage/src/runtime-policy/coordinator.ts Mediates requests from the runtime-host to the CredentialVaultDocumentOwner
apps/desktop/src/main/*.ts Registers IPC handlers (maka:set-credential, maka:get-credential-status) that forward renderer calls to the coordinator
apps/desktop/src/renderer/*.ts Calls ipcRenderer.invoke to request credential operations without filesystem access

Summary

  • credential-vault.json stores secrets with strict limits: 2,048 maximum entries and 64 KB maximum secret length, enforced by MAX_VAULT_ENTRIES and MAX_SECRET_LENGTH constants
  • Validation logic lives exclusively in the main process via CredentialVaultDocumentOwner, which checks schema version, entry limits, and document byte size before every write
  • IPC isolation prevents the renderer from accessing the filesystem directly; all credential operations route through maka:set-credential and related channels
  • Secret containment ensures raw secret values never cross the IPC boundary—renderers receive only metadata snapshots containing credentialId, revision, and updatedAt
  • Optimistic concurrency uses the revision field to prevent race conditions during concurrent credential updates

Frequently Asked Questions

What is the maximum size limit for a single secret in Maka?

Maka enforces a hard limit of 64 KB (65,536 bytes) per secret, defined by MAX_SECRET_LENGTH = 64 * 1024 in packages/storage/src/runtime-policy/credential-vault-document.ts. Additionally, the entire vault document cannot exceed VAULT_DOCUMENT_MAX_BYTES, preventing oversized JSON payloads from corrupting the storage file.

Can the renderer process read credential-vault.json directly from the filesystem?

No. The renderer process has no direct filesystem access to credential-vault.json. According to the Apache Maka source code, the renderer must use ipcRenderer.invoke to send requests to the main process, which exclusively owns the CredentialVaultDocumentOwner class and performs all file I/O operations.

How does Maka prevent concurrent modifications to credentials?

Maka implements optimistic concurrency control using the revision field in each CredentialVaultEntry. When updating a credential via prepareSet, the main process checks the expected revision against the current stored value. If the revisions mismatch (indicating another update occurred), the operation fails, preventing lost updates in multi-process scenarios.

Why doesn't the renderer receive the secret back after storing it?

The IPC handlers in the main process explicitly filter the secret field from all responses to the renderer. When maka:set-credential succeeds, the main process returns a snapshot containing only credentialId, revision, updatedAt, and a configured flag. This security measure ensures that compromised renderer code or browser dev-tools cannot leak raw credentials, as the sensitive data never enters the renderer’s memory space.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →