No description
  • TypeScript 78.3%
  • Go 16.8%
  • CSS 2.5%
  • Just 1.7%
  • HTML 0.7%
Find a file
Dušan Simić cb616001f4 config: docker compose down instead of stop
Removes containers, not just stops them—cleaner dev environment resets
2026-07-29 09:33:44 +02:00
proto/terminko/v1 feat(proto): define event and availability contract 2026-07-28 15:00:53 +02:00
server fix(server): retry database startup until postgres is ready 2026-07-28 18:33:27 +02:00
ui feat(ui): pick the theme from an icon dropdown 2026-07-28 21:18:06 +02:00
.editorconfig chore: bootstrap monorepo tooling and dev compose 2026-07-28 14:59:32 +02:00
.gitignore build: add mprocs dev stack 2026-07-28 18:23:57 +02:00
.pre-commit-config.yaml build: enforce formatting and linting with pre-commit 2026-07-28 16:08:07 +02:00
AGENTS.md fix(server): retry database startup until postgres is ready 2026-07-28 18:33:27 +02:00
buf.gen.yaml feat(proto): define event and availability contract 2026-07-28 15:00:53 +02:00
buf.yaml feat(proto): define event and availability contract 2026-07-28 15:00:53 +02:00
CLAUDE.md chore: bootstrap monorepo tooling and dev compose 2026-07-28 14:59:32 +02:00
compose.yaml chore: bootstrap monorepo tooling and dev compose 2026-07-28 14:59:32 +02:00
justfile fix(server): retry database startup until postgres is ready 2026-07-28 18:33:27 +02:00
mprocs.yaml config: docker compose down instead of stop 2026-07-29 09:33:44 +02:00
README.md fix(server): retry database startup until postgres is ready 2026-07-28 18:33:27 +02:00

Terminko

Open meeting time slot selector. No registration: pick a name, optionally set a password to protect your selection, and mark each slot as available, tentative or unavailable.

Layout

Path Contents
proto/ Protobuf specs — the client/server contract (source of truth)
server/ Go backend: ConnectRPC, pgx, goose migrations, sqlc queries
ui/ React SPA: Vite, TypeScript, shadcn/ui, connect-query

Generated code (server/internal/gen, server/internal/database/sqlcgen, ui/src/gen) is committed so the repo builds without buf/sqlc installed.

Quickstart

Tasks are fronted by just. The whole dev stack runs in one mprocs window:

just dev      # Postgres + backend + frontend, one pane each

Then open http://localhost:5173.

The server pane waits for Postgres before starting, so a cold start needs nothing else. On top of that the server retries connection failures for 60s on startup: a container's published port accepts connections while Postgres is still initialising and then resets them, so a first-run initdb no longer takes the backend down with it. In mprocs: j/k switch panes, r restarts the focused one (the server pane rebuilds), x stops it, q stops everything including the container.

The pieces also run on their own:

just up       # start Postgres in Docker
just server   # backend on :8080, migrations run on boot
just ui       # frontend on :5173, proxies /api to the backend

On a podman host, point compose at the rootless socket first:

systemctl --user start podman.socket
export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock

Development

Formatting and linting are enforced on commit by pre-commit: gofmt, go vet, biome check and buf lint. The hooks are local, so they use the tools you already have installed rather than their own environments.

just          # list every recipe
just hooks    # install the pre-commit git hook (once per clone)
just check    # run every pre-commit hook over the whole tree
just gen      # regenerate from protos + SQL after a contract change
just lint     # buf lint, go vet, biome
just fmt      # gofmt, biome format
just test     # go test ./...
just build    # server binary + SPA bundle

API

Three RPCs on terminko.v1.EventService, reachable over the Connect protocol at POST /terminko.v1.EventService/<Method>:

RPC Purpose
CreateEvent Store a title, timezone, slot length and explicit list of slot starts
GetEvent Read an event by slug with its slots and everyone's answers
SaveAvailability Replace one participant's answers, creating them on first save

A name identifies a participant within one event, case insensitively. A password set on the first save is required for later edits (permission_denied otherwise); without one, the name alone is enough. Absent answers mean unavailable, so only available and tentative slots are stored.

Configuration

The server reads config from defaults, an optional server/config.yaml, and TERMINKO_* environment variables (highest precedence).

Key Env Default
http.addr TERMINKO_HTTP_ADDR :8080
http.cors_origins TERMINKO_HTTP_CORS_ORIGINS http://localhost:5173
database.url TERMINKO_DATABASE_URL postgres://terminko:terminko@localhost:5432/terminko?sslmode=disable
log.level TERMINKO_LOG_LEVEL info

Participant passwords are sent in the request body on every save. Terminate TLS in front of the server for any deployment that is not local.