DocsDeploymentArchitecture
Back
Deployment

Architecture

Exponential is an event-sourced issue tracker that stores its data in a git-tracked append-only log. A single Go binary ships the CLI, a web UI, an HTTP API, and an MCP server.

Event sourcing

All mutations are recorded as events. There is no mutable state on disk:

{"type": "CREATE",  "payload": {"title": "...", "status": "BACKLOG", "labels": ["feature"]}}
{"type": "UPDATE",  "payload": {"status": "DOING", "assignee": "Alice"}}
{"type": "COMMENT", "payload": {"id": "cmt-...", "text": "markdown body"}}
{"type": "DELETE",  "payload": {"reason": "duplicate"}}
{"type": "MERGE",   "payload": {"branch": "myapp-a1b2/login", "strategy": "squash"}}

Events are stored as one JSON object per line (JSONL) in .xpo/issues.db, appended with O_APPEND. Concurrent appends are safe on POSIX filesystems; git merge conflicts stay limited to the tail of the file.

Projection

Current state is derived by replaying the event log. Each event type has a deterministic effect — CREATE initializes, UPDATE patches, DELETE soft-deletes, COMMENT appends, MERGE records metadata. Post-processing applies config-driven rules: parent status inference, cycle rollover, estimate aggregation.

Transport abstraction

All operations go through a Transport interface:

  • LocalTransport reads and writes issues.db directly. Resolves IDs flexibly — exact, prefix, or substring match.
  • RemoteTransport proxies to a server via REST with JWT authentication.
  • Client wraps a Transport and adds git-specific operations (branching, merging, review).

This is what makes local and distributed mode use the same code paths.

Storage

PathGit-trackedPurpose
.xpo/issues.dbYesActive event log (JSONL)
.xpo/archive.dbYesArchived events
.xpo/config.yamlYesProject configuration
.xpo/authorized_keysYesSSH public keys for server auth
.xpo/server.keyNoJWT signing key (auto-generated)
.xpo/issues.snapshot.jsonNoProjection cache

The snapshot cache stores projected state up to a known event offset, enabling fast startup without replaying the full log.

Web UI internals

The server maintains a read-through projection cache. The web UI persists mutations automatically. AppendEventCollapsed() merges consecutive edits on the same issue — two UPDATEs become one — and prunes no-ops.

Real-time updates use Server-Sent Events (SSE) with a 30-second keepalive.

Data model versioning

VersionChange
v1Initial schema (typed issues: TASK, BUG, EPIC)
v2Unified Issue type with labels, assignee, estimation
v3Globally consistent sort order keys

xpo migrate appends corrective events — existing events are never modified.