Docs Decisions
0003 · Go, shipped as a single static binary
Status: Accepted · 2026-09-22 · supersedes an initial TypeScript decision
Context
The first draft specified a TypeScript monorepo — defensible, since the MCP SDK, Typesense client, and web framework are all first-class there. On review, the deployment story decided it.
CoDNA is self-hosted software. Most installations will be someone in an infrastructure team trying it on a spare VM. What they have to install is a feature.
Decision
Go 1.27+, compiled to one static binary carrying the API, MCP server, outbox worker, scheduler, and
web UI. The Svelte UI is built with Vite and embedded via embed.FS, so Node is a build-time
dependency and never a runtime one.
Supporting choices: sqlc over an ORM, goose for migrations, pgx v5 for the driver.
Consequences
docker run codna/codna serve. No runtime, nonode_modules, no version manager.- sqlc means writing SQL by hand. The load-bearing queries here are recursive CTEs for link traversal
and
FOR UPDATE SKIP LOCKEDfor the scheduler — an ORM would obstruct both. - Goroutines plus
LISTEN/NOTIFYgive the outbox worker and scheduler a natural home in-process. No queue, no sidecar. - Low, predictable memory, which matters on the spare VM.
- The repository is polyglot. The UI is TypeScript and always will be.
- Document extraction is weaker than Python's. PDF especially; stated plainly in ingestion.md with the mitigations.
- More boilerplate around JSON and validation than Zod would need.
Alternatives rejected
TypeScript. One language across the stack, the best MCP and Typesense tooling, and Zod schemas that feed MCP tool definitions directly. Rejected on deployment: a Node runtime plus a dependency tree is exactly the install friction that makes people bounce off self-hosted software.
Rust. Stronger guarantees and the same single-binary benefit, at a development pace this project cannot afford, for a system whose hard parts are SQL and permissions rather than performance.