- TypeScript 78.3%
- Go 16.8%
- CSS 2.5%
- Just 1.7%
- HTML 0.7%
|
|
||
|---|---|---|
| proto/terminko/v1 | ||
| server | ||
| ui | ||
| .editorconfig | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| AGENTS.md | ||
| buf.gen.yaml | ||
| buf.yaml | ||
| CLAUDE.md | ||
| compose.yaml | ||
| justfile | ||
| mprocs.yaml | ||
| README.md | ||
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.