mech.app

The mech.app newsletter

Agentic AI, minus the noise.

Get practical field notes on AI agents, automation, developer tools and security delivered to your inbox.

No spam. Unsubscribe anytime.

Dev Tools

Apache Maka's Event-Sourced Agent Runtime: Why Append-Only Logs Beat Stateful Sessions for Multi-Tool Workflows

How Apache Maka uses event sourcing to solve agent state recovery, permission auditing, and multi-tool coordination without traditional session management.

Source: github.com
Apache Maka's Event-Sourced Agent Runtime: Why Append-Only Logs Beat Stateful Sessions for Multi-Tool Workflows

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 -rf without 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:

  1. Loads the event log for the current session (or creates a new one).
  2. Sends the user prompt to the model.
  3. Receives a tool call from the model.
  4. Checks the permission policy (is this tool allowed?).
  5. Appends a permission decision event to the log.
  6. Executes the tool in the sandbox.
  7. Appends the tool result event to the log.
  8. Sends the result back to the model.
  9. 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 TypePayloadPurpose
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:

  1. Reads events from the log in order.
  2. Rebuilds the conversation history by processing ModelMessage events.
  3. Skips tool execution for events already in the log (the result is already recorded).
  4. 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:

  1. Replay events up to the fork point.
  2. Append a new event (e.g., a different user input or a modified tool result).
  3. 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 /tmp cannot 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_KEY unless 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:

  1. Appends a ToolCall event to the log.
  2. Checks the permission policy.
  3. Appends a PermissionDecision event with allowed: true or allowed: false.
  4. If allowed, executes the tool and appends a ToolResult event.
  5. If denied, appends a ToolResult event 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 ToolCall events by tool_name.
  • Permission denial rate: Count PermissionDecision events where allowed: false.
  • Execution timeline: Plot events by timestamp to see where the agent spent time.
  • Error analysis: Filter ToolResult events where error is 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

AspectEvent Sourcing (Maka)Session State (Typical Frameworks)
Crash recoveryReplay log from diskCheckpoint to disk or lose state
AuditabilityEvery decision is an eventGrep unstructured logs
Execution forkingReplay to fork point, inject new eventRe-run entire workflow
State queriesReplay log (slow)Read mutable object (fast)
Storage overheadLog grows monotonicallyFixed size (until checkpoint)
DebuggingTime-travel through eventsInspect 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.