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>
148 lines
4.8 KiB
Markdown
148 lines
4.8 KiB
Markdown
# 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 newer** — `go 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
|
|
|
|
```bash
|
|
git clone https://github.com/elfoundation/hatch.git
|
|
cd hatch
|
|
go mod download
|
|
```
|
|
|
|
## Run the Server
|
|
|
|
The fastest path is `go run`:
|
|
|
|
```bash
|
|
go run ./cmd/hatch
|
|
# hatch starting on :8080
|
|
```
|
|
|
|
In another terminal:
|
|
|
|
```bash
|
|
curl -fsS http://localhost:8080/healthz
|
|
# ok
|
|
```
|
|
|
|
The server reads its port from the `PORT` environment variable and defaults to `:8080`.
|
|
|
|
```bash
|
|
PORT=9090 go run ./cmd/hatch
|
|
# hatch starting on :9090
|
|
```
|
|
|
|
## Run the Tests
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
go test ./... -v -race
|
|
```
|
|
|
|
## Vet and Format
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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):
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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-alpine` → `scratch` 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.
|
|
|
|
```bash
|
|
# 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.
|