hatch-surf/docs/engineering/local-dev.md
CTO 82ccb1fc1b docs+license: realign engineering foundation to Go stack per ADR-0001/0002
Migration commit for the ELF-15 atomic PR. Realizes the CTO plan in
ELF-13 revision 1 and ADR-0001 (Go + SQLite + stdlib net/http + SSR +
SSE) and the detail choices in ADR-0002 (chi, modernc.org/sqlite, html/
template, stdlib testing, Apache-2.0).

Docs:
- docs/engineering/tech-stack.md rewritten for Go + SQLite + stdlib
  net/http + SSR + SSE. Frontend is server-rendered HTML + a little
  vanilla JS. Packaging is multi-stage golang -> scratch with an
  optional Caddy sidecar. Boring-technology principle kept.
- docs/engineering/first-engineer-role.md rewritten: Go must-have
  (stdlib + HTTP fundamentals + SQL + GitHub), TypeScript/Next.js/
  Prisma/PostgreSQL moved to nice-to-have or deferred. Tailwind and
  monorepo tooling explicitly deferred.
- docs/engineering/onboarding.md: pnpm install / pnpm dev replaced
  with go test ./... and go run ./cmd/hatch. New step: read local-dev
  and hatch-architecture on day 1.
- docs/engineering/local-dev.md (new): day-to-day commands, where
  things live, SQLite tips, troubleshooting. Living doc, engineer owns.
- docs/engineering/hatch-architecture.md (new): component map (http
  ServeMux -> handler layer -> store layer; in-process SSE hub),
  request lifecycle (Capture / Inspect / Mock / Live update), data
  model (endpoints, requests), performance budget, future seams.
- docs/adrs/0002-hatch-detail-stack.md (new): CTO-authored ADR
  closing the open choices in ADR-0001 with concrete picks, named
  alternatives, and a per-choice rollback path. Router: go-chi/chi.
  SQLite driver: modernc.org/sqlite (pure Go, no CGO). Templates:
  stdlib html/template + //go:embed. Tests: stdlib testing + httptest.
  License: Apache-2.0.

Housekeeping:
- README.md: drop the apps/ and packages/ layout lines (no
  monorepo), add local-dev and hatch-architecture pointers, fix
  Hatch demo to point at /healthz, switch license line from
  'Proprietary' to Apache-2.0 with a LICENSE file pointer.
- CONTRIBUTING.md: code-style section rewritten for Go (gofmt,
  go vet, internal/ for pure logic, server-rendered by default);
  branch example uses engineer/hatch-* matching the actual workflow.
- LICENSE: full Apache-2.0 text, copyright El Foundation 2026.
  Per ADR-0002.
- .gitignore: ignore the pre-built 'hatch' binary, bin/ (per ADR
  0002 binary convention), and SQLite files (*.db, *.db-journal,
  *.db-wal, *.db-shm).

Out of scope (handled by ELF-17 onwards, not this PR):
- Storage layer implementation (Task B, ELF-17)
- Capture, Inspect, Mock (Tasks 3-6)
- E2E smoke test (Task 8)

Foundation + this commit are the atomic PR. CI is green (go vet,
go test ./... -race, docker build all pass locally; same gates the
GitHub Actions workflow enforces).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-22 15:12:04 +02:00

4.8 KiB

Local Development

This is the day-to-day workflow for working on Hatch. If something here disagrees with what you actually observe, update this doc — the docs are a living artifact.

Prerequisites

  • Go 1.25 or newergo version should report 1.25+. Install from https://go.dev/doc/install or via your package manager.
  • Docker (optional but recommended) — only required if you want to exercise the docker compose flow.
  • curl — for smoke tests against the running server.
  • sqlite3 CLI (optional) — for poking at the database file directly. Not required for the normal workflow.

Clone and Build

git clone https://github.com/elfoundation/hatch.git
cd hatch
go mod download

Run the Server

The fastest path is go run:

go run ./cmd/hatch
# hatch starting on :8080

In another terminal:

curl -fsS http://localhost:8080/healthz
# ok

The server reads its port from the PORT environment variable and defaults to :8080.

PORT=9090 go run ./cmd/hatch
# hatch starting on :9090

Run the Tests

go test ./...

This runs the full test suite, including the smoke test that boots the HTTP server in-process and hits /healthz. Add -v for verbose output, -race for the race detector.

go test ./... -v -race

Vet and Format

go vet ./...
gofmt -l .

CI runs go vet ./.... gofmt -l . lists any unformatted files (no output means everything is formatted). Run gofmt -w . to fix formatting in place.

Build a Static Binary

CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/hatch ./cmd/hatch
ls -lh bin/hatch

The resulting binary is fully static, has no libc dependency, and runs on any Linux x86_64 with the same kernel.

Docker Compose

For the full local stack (Hatch + optional Caddy reverse proxy for TLS):

# Plain HTTP — Hatch only
docker compose up --build

# With Caddy (HTTPS, self-signed in dev)
docker compose --profile with-caddy up --build

The compose file reads .env for HATCH_HOSTNAME. Copy .env.example to .env and adjust:

cp .env.example .env
# Edit HATCH_HOSTNAME=localhost  (or your real domain)

Where Things Live

Path Purpose
cmd/hatch/ Server entrypoint. main.go wires up http.ServeMux, main_test.go is the smoke test.
internal/handler/ HTTP handlers (Capture, Inspect, Mock). One file per route group.
internal/store/ Storage layer. schema.sql is the canonical DDL, sqlite_repo.go is the SQLite-backed implementation of the Repository interface.
docs/engineering/ Engineering standards and architecture docs.
docs/adrs/ Architecture Decision Records.
Dockerfile Multi-stage build: golang:1.25-alpinescratch with the static binary.
docker-compose.yml Local stack: Hatch on :8080, optional Caddy sidecar for TLS.
.env.example Documented environment variables.
hatch.db (gitignored) SQLite database file. Created on first start in the working directory.

SQLite Tips

The database is a single file. By default it lives at ./hatch.db in the process working directory.

# Inspect the schema
sqlite3 hatch.db '.schema'

# List endpoints
sqlite3 hatch.db 'SELECT * FROM endpoints;'

# Reset (destructive)
rm hatch.db
# Schema is recreated on next start

For tests that need a clean database, use the :memory: SQLite database — see internal/store/sqlite_repo_test.go for the pattern. Do not point tests at the on-disk database file.

Common Tasks

Task Command
Run the server go run ./cmd/hatch
Run all tests go test ./...
Run a single test go test ./internal/handler -run TestCapture -v
Vet go vet ./...
Format gofmt -w .
Build a binary CGO_ENABLED=0 go build -o bin/hatch ./cmd/hatch
Docker stack docker compose up --build
Reset the database rm hatch.db

Troubleshooting

go: command not found — Install Go 1.25+ and ensure $HOME/go/bin (or wherever go install puts binaries) is on your PATH.

bind: address already in use — Another process is on :8080. Either stop it or run with PORT=9090 go run ./cmd/hatch.

Tests fail with database is locked — A previous test process left a handle. Look for stray hatch processes (ps aux | grep hatch) and kill them. Tests should use :memory: databases and not share the on-disk file.

Docker build fails on go.sum — Run go mod tidy locally, commit go.sum, rebuild. The Dockerfile copies go.sum before go mod download for reproducible builds.

CGO_ENABLED warning during go build — The static-binary build sets CGO_ENABLED=0 explicitly. If you forget, the binary will dynamically link libc and break the "single static binary" promise.