How the Apache Maka Credential Vault Manages Local Secret Storage and Security
The Apache Maka credential vault uses a two-tier architecture that combines a file-based CredentialStore for simple key-value access and a CredentialVaultDocument for versioned runtime policies, enforcing security through strict filesystem permissions (0700/0600), atomic write operations, payload size limits, and optimistic locking via revision numbers.
The Apache Maka credential vault provides a robust local secret management solution designed for desktop applications, CLI tools, and runtime policy engines. This system protects sensitive data like API keys and OAuth tokens through OS-level filesystem isolation and atomic transaction patterns rather than encryption-at-rest. The implementation spans multiple storage layers that work together to prevent race conditions, data corruption, and unauthorized access.
Two-Tier Architecture: File-Based Store vs. Runtime-Policy Vault
CredentialStore: The Plain-File Key-Value API
The CredentialStore component serves desktop and CLI consumers through a straightforward key-value interface. It persists secrets to a plaintext JSON file named credentials.json located within the workspace directory. While the data remains unencrypted, the system relies on strict POSIX permissions and atomic write mechanics in packages/storage/src/credential-store.ts to maintain confidentiality and integrity.
CredentialVaultDocument: Versioned Runtime Secrets
The CredentialVaultDocument manages versioned credential entries consumed by Maka's runtime-policy engine. Stored as credential-vault.json, this structure maintains an array of CredentialVaultEntry objects with unique identifiers, timestamps, and revision tracking. It enforces hard limits on document size (VAULT_DOCUMENT_MAX_BYTES) and individual secret length (64 KB) to prevent denial-of-service attacks.
How the File-Based CredentialStore Works
Creation and Initialization
The factory function createFileCredentialStore(workspaceRoot) initializes a FileCredentialStore instance pointing to <workspaceRoot>/credentials.json. During first access, the system calls ensureSecretDir to create the parent directory with permissions 0700 (owner-only access), establishing the filesystem boundary before any secret data touches the disk.
Atomic Read and Write Operations
Secret retrieval via getSecret(slug, kind) acquires a cross-process lock using withCredentialFileLock before reading the JSON file. The corresponding setSecret(slug, kind, value) method performs a read-modify-write cycle under the same lock, ensuring that concurrent processes cannot corrupt the store.
Filesystem-Level Atomicity
All writes flow through writeSecretFileAtomic, which creates a temporary file with mode 0600, flushes data to disk with fsync, then renames the file atomically. This pattern guarantees that observers never see partially written data, even during system crashes or power failures. The implementation uses withFileUpdateLock from packages/storage/src/file-update-lock.js to serialize access across separate Node.js processes.
Schema Versioning
The on-disk format carries a CREDENTIAL_SCHEMA_VERSION constant (currently version 1). When CredentialStore loads existing data, it validates this version number and throws a hard error if encountering unknown schemas, preventing accidental data corruption during application upgrades.
Runtime-Policy Vault Security Mechanisms
Document Structure and Validation
The CredentialVaultDocument contains a schemaVersion, monotonic revision counter, and a bounded array of entries. Each CredentialVaultEntry records a locator (scope and kind), unique credentialId, secret string, and updatedAt timestamp. The CredentialVaultDocumentOwner.read(root) method in packages/storage/src/runtime-policy/credential-vault-document.ts validates uniqueness constraints and enforces entry count limits using readBoundedJsonDocument.
Optimistic Concurrency Control
Updates to the vault use compare-and-set semantics. The set(root, input) method accepts an expected revision parameter; if the current document revision differs, the operation fails with a stale-write error. This nextRevision tracking prevents lost updates when multiple runtime-policy instances compete to modify credentials simultaneously.
Size Boundaries and Safety Checks
Before persisting changes, assertDocumentSize verifies that the JSON payload remains under VAULT_DOCUMENT_MAX_BYTES. Individual secrets cannot exceed 64 KB. These checks, enforced by writeJsonDocument, protect against accidental or malicious storage exhaustion. The codec definitions in packages/core/src/runtime-policy/credential-vault-codec.ts handle serialization errors and schema migrations.
Security Guarantees and Filesystem Protections
The Maka credential vault achieves defense in depth through multiple concrete mechanisms:
-
Exclusive Access: Cross-process file locking via
withCredentialFileLockprevents race conditions between CLI invocations and long-running desktop applications. -
Filesystem Isolation: Directory mode 0700 and file mode 0600 ensure only the operating system owner can list or read secret files.
-
Atomic Durability: Temporary file creation followed by atomic rename guarantees crash consistency without leaking partial secrets to disk.
-
Payload Limiting: Hard caps on document size and entry count prevent resource exhaustion attacks.
-
Audit Trail: Revision numbers and timestamps in the runtime vault provide a history of credential modifications for debugging.
Practical Implementation Examples
The following TypeScript snippets demonstrate working with both storage layers:
// Create a credential store for a workspace
import { createFileCredentialStore } from '@maka/storage';
const store = createFileCredentialStore('/home/user/my-maka-workspace');
// Store a secret (e.g. an API key)
await store.setSecret('my-service', 'api_key', 's3cr3t-apikey');
// Retrieve the secret later
const apiKey = await store.getSecret('my-service', 'api_key');
console.log('Loaded key:', apiKey);
// Use the credential vault (runtime-policy side)
import { CredentialVaultDocumentOwner } from '@maka/storage/runtime-policy';
const vault = new CredentialVaultDocumentOwner();
const root = '/home/user/my-maka-workspace';
// Set a new credential for a connection
await vault.set(root, {
locator: { scope: 'connection', connectionId: 'c123', kind: 'api_key' },
secret: 'conn-secret',
expected: null, // create-only
});
// Read the vault snapshot (useful for UI)
const snapshot = await vault.read(root);
console.log('Vault entries:', snapshot.entries.length);
Summary
- The Apache Maka credential vault combines two specialized components: the simple
CredentialStorefor CLI/desktop use and the versionedCredentialVaultDocumentfor runtime policies. - Filesystem permissions (0700 directories, 0600 files) provide the primary security boundary, supplementing the plaintext storage format.
- Atomic write operations via
writeSecretFileAtomicandwriteJsonDocumentensure crash consistency and prevent data corruption during concurrent access. - Revision-based optimistic locking in the runtime vault detects stale writes and prevents race conditions between policy engine instances.
- Size limits (
VAULT_DOCUMENT_MAX_BYTES, 64 KB per secret) protect against denial-of-service attacks and accidental data bloat.
Frequently Asked Questions
Does the Maka credential vault encrypt secrets at rest?
No, according to the source code in packages/storage/src/credential-store.ts, secrets are stored in plaintext JSON. The design intentionally defers encryption to the operating system layer, relying on strict file permissions (mode 0600/0700) and atomic write patterns for protection. Future backend integrations may add encryption, but the current implementation prioritizes simplicity and cross-platform compatibility.
How does Maka prevent concurrent processes from corrupting the credential files?
Maka uses file-based locking through the withCredentialFileLock primitive implemented in packages/storage/src/file-update-lock.js. Both CredentialStore and CredentialVaultDocumentOwner acquire this lock before reading or writing. Combined with atomic rename operations in writeSecretFileAtomic, this ensures that even with multiple CLI invocations or desktop instances, only one process modifies the file at a time, and readers always see complete, valid JSON.
What happens if the credential vault exceeds its maximum size limit?
The CredentialVaultDocumentOwner calls assertDocumentSize before committing any write via writeJsonDocument. If the operation would cause credential-vault.json to exceed VAULT_DOCUMENT_MAX_BYTES (or if an individual secret exceeds 64 KB), the system throws a RuntimePolicyStoreError. This hard boundary prevents malicious or accidental storage exhaustion that could destabilize the runtime-policy engine.
Where are the credential files physically located on disk?
The CredentialStore creates a file named credentials.json directly inside the workspace root specified during createFileCredentialStore(workspaceRoot). The CredentialVaultDocument stores its data as credential-vault.json in the same workspace directory. Both files reside in a parent directory configured with 0700 permissions (owner-only access), ensuring other users on the system cannot list or traverse the secret storage location.
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 →