Run the Server
Basic, local, and unauthenticated. One Go binary serves a web UI, an HTTP API, and an MCP server on one port. It has no authentication, no signatures, and no authorization. It listens on localhost unless you pass another address, so keep it on your machine or a trusted network.
Run it
make seed # load the Spring Spin example story into share/techlog.db
make run # serve the UI, the API and MCP at http://localhost:8080
make skill # optional: install the Claude skill into ~/.claude/skillsData lives in share/techlog.db, a SQLite file the server creates on first start. The share/ directory is git-ignored.
Make targets
| Target | What it does |
|---|---|
make run | Build and serve. Pass flags with ARGS="--addr :9090". |
make seed | Load the Spring Spin example story. Safe to run more than once. |
make flush | Delete the database and every event in it. It asks first; FORCE=1 skips the prompt. It refuses to run while a server answers on the port. |
make reset | Flush, then seed. |
make skill | Install the Claude skill into ~/.claude/skills as a symlink. make skill-remove undoes it. |
make check | Lint with golangci-lint and run the tests. |
The binary takes --addr (default localhost:8080), --db (default share/techlog.db), and --store (only sqlite exists today). A bare :8080 would listen on every interface.
The web UI
The UI is read-only: it never writes an event.
- Events at
/: a table of events, newest first, 50 per page with Previous and Next links. Filter by text, system, type, and date range; every view is a plain URL you can share. - Event at
/events/{id}: all fields, the links that leave the event and the links that point at it, sources, evidence, and the raw JSON. A hypothesis link is drawn dashed. - System at
/systems/{system}: a timeline of the latest events of one system. - Story at
/story/{id}: the events connected to one event through links, oldest first, with each event's relations. It lets you follow a story like the one the home page animation shows.
The HTTP API
| Route | Purpose |
|---|---|
POST /api/v1/events | Record an event. The server assigns the id. |
GET /api/v1/events/{id} | One event with its outgoing and incoming links. |
GET /api/v1/events?q&system&type&since&until&limit | Search, newest first. Default 20, at most 100. |
GET /api/v1/systems | The known systems. |
GET /api/v1/systems/{system}/history | The latest events of one system, oldest first. Default 50, at most 200. |
POST /api/v1/links | Link two existing events. |
Errors share one shape: {"error": {"code", "message", "details"}}. The codes are invalid_event (422), not_found (404), bad_request (400), and internal (500). There is no update and no delete: events are immutable.
MCP and the Claude skill
The same port serves MCP at /mcp. MCP integration lists the five tools and shows how to connect Claude.
Storage
The server talks to storage through a small interface: insert an event, add a link, get an event, search, list links, list systems. The SQLite backend is pure Go, needs no C toolchain, and keeps the full event JSON next to indexed columns and a full-text index. It uses one connection, and write transactions take the lock up front, so a second process on the same file waits instead of failing.
A new backend is a package under internal/store that passes the shared contract test suite, plus one line that selects it with --store. Nothing else in the server changes.