Apache Maka just hit GitHub Trending #1 for TypeScript with 3,268 stars. It is a rare Apache Incubator project tackling local-first agent infrastructure with event sourcing, a pattern borrowed from distributed systems that most agent frameworks ignore.
Most agent runtimes store execution state in mutable session objects. When the process crashes, you lose context. When you need to audit a permission decision, you grep logs. When you want to fork execution from step seven, you re-run the entire chain and hope for determinism.
Maka records model messages, tool calls, tool results, permission decisions, and termination events as an append-only log. Every execution becomes a recoverable, replayable, auditable sequence of facts. This is event sourcing applied to agent workflows.
Why Event Sourcing for Agent State
Traditional agent frameworks treat state as a live object in memory. You have a session dictionary, a conversation history array, and maybe a tool result cache. When the runtime crashes, you checkpoint to disk or lose everything.
Event sourcing flips this. State is derived from a log of immutable events. You never update a record. You append a new event. The current state is the result of replaying all events from the beginning.
For agents, this solves three problems:
- Crash recovery: Restart the runtime and replay the log. You are back at the exact step where the process died.
- Permission auditing: Every tool call and approval decision is a timestamped event. You can trace why the agent executed
rm -rfwithout scanning unstructured logs. - Execution forking: Replay events up to step N, inject a different user input or tool result, and continue from there. No need to re-run expensive LLM calls.
Maka’s log is append-only. You cannot edit history. You can only add new events. This makes the log a source of truth for debugging, compliance, and replay.
Architecture: Single Runtime Host, Local-First Execution
Maka runs on your machine as a single Runtime Host. It is not a cloud service. It is not a distributed system. It is a local process that executes tools in a sandbox boundary and writes events to disk.
The core components:
- Runtime Host: The main process that orchestrates model calls, tool execution, and permission checks.
- Event Log: An append-only file or database that stores every execution event.
- Sandbox Boundary: A process isolation layer that prevents tools from accessing the host filesystem or network without explicit permission.
- Tool Registry: A catalog of available tools (file read, shell exec, HTTP request) with declared capabilities and permission requirements.
When you start an agent workflow, Maka:
- Loads the event log for the current session (or creates a new one).
- Sends the user prompt to the model.
- Receives a tool call from the model.
- Checks the permission policy (is this tool allowed?).
- Appends a permission decision event to the log.
- Executes the tool in the sandbox.
- Appends the tool result event to the log.
- Sends the result back to the model.
- Repeats until the model emits a termination event.
Every step writes an event. The log grows monotonically. The runtime never mutates an existing event.
Event Schema and Replay Mechanics
Each event in the log has a type, timestamp, and payload. The schema is simple:
| Event Type | Payload | Purpose |
|---|---|---|
ModelMessage | { role, content, model_id } | LLM input or output |
ToolCall | { tool_name, arguments, call_id } | Model requested a tool |
ToolResult | { call_id, output, error } | Tool execution completed |
PermissionDecision | { call_id, allowed, reason } | User or policy approved/denied |
Termination | { reason, final_output } | Workflow ended |
To replay a session, the runtime:
- Reads events from the log in order.
- Rebuilds the conversation history by processing
ModelMessageevents. - Skips tool execution for events already in the log (the result is already recorded).
- Stops at the first missing event or the end of the log.
This means you can restart Maka mid-workflow and it will pick up exactly where it left off. No checkpointing logic. No state serialization. Just replay the log.
To fork execution, you:
- Replay events up to the fork point.
- Append a new event (e.g., a different user input or a modified tool result).
- Continue execution from there.
The original log remains unchanged. The fork creates a new branch in the event stream.
Sandbox Boundary: What It Means in Practice
Maka’s sandbox boundary is a process isolation layer. Tools run in a separate process with restricted capabilities. The runtime host controls what the tool can access.
In practice, this means:
- Filesystem access: Tools declare which directories they need to read or write. The runtime enforces these boundaries. A tool that requests
/tmpcannot access/home/user/secrets. - Network access: Tools declare which domains or IP ranges they need. The runtime blocks undeclared connections.
- Environment variables: Tools run with a minimal environment. No access to
AWS_SECRET_ACCESS_KEYunless explicitly granted.
The sandbox is not a VM or container. It is a process-level boundary enforced by the runtime. On macOS, this uses sandbox-exec. On Windows, it uses job objects. On Linux, it will likely use seccomp or landlock.
The key insight: the sandbox boundary is not about preventing malicious code. It is about preventing accidental damage. An agent that runs git push --force should not be able to do so without explicit permission.
Permission Decisions as First-Class Events
Most agent frameworks treat permissions as a runtime check. You call a function, it checks a policy, and execution continues or fails. The decision is ephemeral.
Maka makes permission decisions a first-class event. When the model requests a tool call, the runtime:
- Appends a
ToolCallevent to the log. - Checks the permission policy.
- Appends a
PermissionDecisionevent withallowed: trueorallowed: false. - If allowed, executes the tool and appends a
ToolResultevent. - If denied, appends a
ToolResultevent with an error.
This creates an audit trail. You can replay the log and see every permission decision. You can answer questions like:
- Did the agent ever try to delete a file?
- How many times did the user approve a shell command?
- What was the reason for denying a network request?
The log is the compliance artifact. You do not need to instrument logging separately.
Code Example: Replaying a Session
Here is a simplified TypeScript snippet showing how Maka replays an event log:
interface Event {
type: 'ModelMessage' | 'ToolCall' | 'ToolResult' | 'PermissionDecision' | 'Termination';
timestamp: number;
payload: unknown;
}
class AgentRuntime {
private events: Event[] = [];
private conversationHistory: Array<{ role: string; content: string }> = [];
async replayLog(logPath: string): Promise<void> {
this.events = await this.loadEvents(logPath);
for (const event of this.events) {
switch (event.type) {
case 'ModelMessage':
const msg = event.payload as { role: string; content: string };
this.conversationHistory.push(msg);
break;
case 'ToolCall':
// Tool call already recorded, skip execution
break;
case 'ToolResult':
// Result already recorded, add to context
const result = event.payload as { call_id: string; output: string };
this.conversationHistory.push({
role: 'tool',
content: result.output,
});
break;
case 'Termination':
console.log('Session ended:', event.payload);
return;
}
}
// Resume execution from the last event
await this.continueExecution();
}
private async loadEvents(logPath: string): Promise<Event[]> {
// Read events from disk or database
return [];
}
private async continueExecution(): Promise<void> {
// Send conversation history to model and continue workflow
}
}
The key: you never re-execute tools during replay. The results are already in the log. You just rebuild the conversation history and resume.
Observability: The Log Is the Dashboard
Because every execution event is in the log, you can build observability tools by querying the log. No need for separate tracing infrastructure.
Example queries:
- Tool usage histogram: Count
ToolCallevents bytool_name. - Permission denial rate: Count
PermissionDecisionevents whereallowed: false. - Execution timeline: Plot events by timestamp to see where the agent spent time.
- Error analysis: Filter
ToolResultevents whereerroris not null.
You can build these dashboards with SQL if you store the log in SQLite. You can build them with log parsers if you store the log as JSONL. The log is structured data.
Failure Modes and Trade-Offs
Event sourcing is not free. Here are the failure modes:
- Log corruption: If the log file is corrupted, you lose the entire session. Mitigation: write to a transactional database (SQLite with WAL mode) or use checksums.
- Replay non-determinism: If a tool produces different output on replay (e.g., reading a file that changed), the replayed state diverges. Mitigation: snapshot external state or mark non-deterministic tools.
- Log size growth: Long-running sessions produce large logs. Mitigation: compact the log by snapshotting state at intervals and discarding old events.
- Expensive replay: Replaying a 10,000-event log takes time. Mitigation: cache the replayed state and only replay new events.
Event sourcing works best when:
- You need auditability (compliance, debugging).
- You need crash recovery without checkpointing.
- You need to fork or rewind execution.
It works poorly when:
- You have high-frequency events (thousands per second).
- You need real-time state queries (event replay is slower than reading a mutable state object).
- You cannot tolerate log storage overhead.
Deployment Shape
Maka is a desktop application built with Electron. It runs on macOS arm64 (stable), Windows (preview, unsigned), and Linux (coming soon). It is not a server. It is not a SaaS product.
The deployment model:
- Single user: Maka runs on your laptop. The event log is stored locally.
- No network dependency: The runtime does not require internet access (unless your tools do).
- Model agnostic: Maka supports any LLM API (OpenAI, Anthropic, local models via Ollama).
This is a local-first architecture. Your data stays on your machine. The event log is a local file. You control the runtime.
Comparison: Event Sourcing vs. Session State
| Aspect | Event Sourcing (Maka) | Session State (Typical Frameworks) |
|---|---|---|
| Crash recovery | Replay log from disk | Checkpoint to disk or lose state |
| Auditability | Every decision is an event | Grep unstructured logs |
| Execution forking | Replay to fork point, inject new event | Re-run entire workflow |
| State queries | Replay log (slow) | Read mutable object (fast) |
| Storage overhead | Log grows monotonically | Fixed size (until checkpoint) |
| Debugging | Time-travel through events | Inspect live state or logs |
Technical Verdict
Use Apache Maka when:
- You need crash recovery for long-running agent workflows without manual checkpointing.
- You need a compliance audit trail for tool calls and permission decisions.
- You want to fork or rewind execution without re-running expensive LLM calls.
- You prefer local-first infrastructure over cloud-hosted agent platforms.
Avoid Maka when:
- You need high-frequency event processing (thousands of events per second).
- You need real-time state queries (event replay is slower than mutable state).
- You cannot tolerate log storage growth for long-running sessions.
- You need a production-ready, battle-tested runtime (Maka is in Apache Incubator, still maturing).
Event sourcing is a powerful pattern for agent workflows. Maka is one of the first runtimes to make it a first-class primitive. If you are building multi-tool agents that need auditability and recovery, the append-only log model is worth exploring.