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>
This commit is contained in:
CTO 2026-06-22 15:12:04 +02:00
parent 9a29eec1cb
commit 82ccb1fc1b
10 changed files with 744 additions and 65 deletions

9
.gitignore vendored
View File

@ -8,9 +8,16 @@
*.out
go.work
# Build
# Build artifacts
/bin
/dist
/hatch
/hatch-*
bin/
*.db
*.db-journal
*.db-wal
*.db-shm
# IDE
.idea/

View File

@ -9,8 +9,9 @@ owner-identifier/short-description
```
Examples:
- `cto/ELF-4-engineering-foundation`
- `eng-1/add-auth-middleware`
- `cto/ELF-15-hatch-stack-migration`
- `engineer/hatch-bootstrap`
- `engineer/hatch-storage`
## No Direct Commits to `main`
@ -36,7 +37,7 @@ Good:
Add rotating refresh tokens
Using a rotating refresh token strategy prevents replay attacks
and gives us a clean theft-detection signal. See ADR-003.
and gives us a clean theft-detection signal. See ADR-0003.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
```
@ -48,20 +49,20 @@ Update auth.ts
## Code Style
- TypeScript strict. No `any` unless explicitly approved — use `unknown` + narrowing.
- Keep `lib/` (pure logic) and `services/` (I/O, DB, network) separate.
- Prefer small modules over clever abstractions.
- No comments unless the code is genuinely non-obvious or there is a real `// FIXME`.
- No defensive try/catch around things that should not fail. Let it throw.
- Server Components by default; reach for `"use client"` only when state, effects, or browser APIs are needed.
- **Go, idiomatic.** `gofmt` clean, `go vet ./...` clean. Prefer the standard library over new dependencies. Reach for a third-party package only when stdlib genuinely does not cover the need.
- **Pure logic in `internal/`, I/O in adapters.** Business logic does not call `http.*` or `database/sql` directly. Storage and HTTP are replaced with interfaces in tests.
- **Server-rendered by default.** Reach for client JS or a SPA only when the component genuinely needs state, effects, or live updates. The v0.1 web UI is HTML templates plus a small vanilla-JS SSE client.
- **Keep packages small.** Prefer small, focused packages over clever abstractions. One file per route group in `internal/handler/`.
- **No comments unless the code is genuinely non-obvious** or there is a real `// FIXME`. Let the code explain itself; let the commit message explain the *why*.
- **No defensive error handling around things that should not fail.** Let it panic or return the error. Wrap at the boundary, not at every call site.
## Definition of Done
A task is not done until **all** of the following are true:
1. Code is written and reviewed.
2. Tests pass. CI is green.
3. Documentation is updated.
2. Tests pass. `go test ./...` is green. CI is green.
3. Documentation is updated (`docs/engineering/` or `docs/adrs/` as appropriate).
4. No secrets in plain text.
5. User-facing changes are validated.
6. Rollback path is known.

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of tracking, improving, or otherwise
addressing the Work, but excludes communication that is conspicuously
marked or otherwise designated in writing by the copyright owner as
"Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Support. While redistributing the Work or
Derivative Works thereof, You may accept and charge a fee for,
acceptance of support, warranty, indemnity, or other liability
obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own
behalf and on Your sole responsibility, not on behalf of any other
Contributor, and only if You agree to indemnify, defend, and hold
each Contributor harmless for any liability incurred by, or claims
asserted against, such Contributor by reason of your accepting any
such warranty or support.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file name and extension be included on the same line as the
copyright notice for ease of identification within third-party
archives.
Copyright 2026 El Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions and
limitations under the License.

View File

@ -11,18 +11,23 @@ El Foundation builds institutions that outlast their founders. We create technol
1. Read the [company charter](docs/company/charter.md) to understand why we exist.
2. Read the [operating model](docs/company/operating-model.md) to understand how decisions are made.
3. Read [CONTRIBUTING.md](CONTRIBUTING.md) before making any changes.
4. Read [docs/engineering/local-dev.md](docs/engineering/local-dev.md) for the day-to-day workflow.
5. Read [docs/engineering/hatch-architecture.md](docs/engineering/hatch-architecture.md) for the component map.
## Repository Layout
```
├── .github/workflows/ # CI/CD definitions
├── cmd/hatch/ # Server entrypoint (Go binary)
├── docs/
│ ├── company/ # Founding documents (charter, org, etc.)
│ ├── engineering/ # Engineering standards and decisions
│ ├── engineering/ # Engineering standards, architecture, local dev
│ └── adrs/ # Architecture Decision Records
├── apps/ # Application code (TBD — created when product work begins)
├── packages/ # Shared libraries and packages
└── scripts/ # Automation and utility scripts
├── internal/ # Go packages (handler, store, ...)
├── Dockerfile # Multi-stage static binary build (golang → scratch)
├── docker-compose.yml # Local stack with optional Caddy sidecar
├── Caddyfile # TLS reverse proxy for the demo host
└── go.mod # Go module definition
```
## Hatch — Deploy in one command
@ -34,7 +39,14 @@ Hatch is a self-hostable HTTP request inspector + mocker. Ship it to any VPS wit
```bash
docker compose up --build
# Hatch UI: http://localhost:8080
# Capture endpoint: http://localhost:8080/{endpoint-id}
# Health check: http://localhost:8080/healthz
```
Or run the binary directly:
```bash
go run ./cmd/hatch
# Health check: http://localhost:8080/healthz
```
### Production (with HTTPS via Caddy)
@ -64,7 +76,7 @@ Caddy terminates TLS and reverse-proxies to the Hatch Go binary. The Hatch conta
## Technology Stack
See [docs/engineering/tech-stack.md](docs/engineering/tech-stack.md) for current choices and rationale.
See [docs/engineering/tech-stack.md](docs/engineering/tech-stack.md) for current choices and rationale, and [docs/adrs/](docs/adrs/) for the decision records that produced them.
## Contributing
@ -72,4 +84,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
## License
Proprietary — All rights reserved.
Apache-2.0 — see [LICENSE](LICENSE).

View File

@ -0,0 +1,129 @@
# ADR-0002: Hatch detail stack
- Status: Accepted
- Date: 2026-06-22
- Authors: CTO
- Supersedes: none
- Related: [ADR-0001](https://github.com/elfoundation/hatch/blob/main/docs/adrs/0001-stack.md), [Hatch v0.1 plan (ELF-13)](https://github.com/elfoundation/hatch/issues/13)
## Context
[ADR-0001](https://github.com/elfoundation/hatch/blob/main/docs/adrs/0001-stack.md) fixed the v0.1 stack at the level of "Go + SQLite + stdlib `net/http` + server-rendered HTML + SSE + single Docker image." That ADR deliberately did not pick:
- which HTTP router (stdlib `net/http` with method-based mux vs. a third-party router)
- which SQLite driver
- which templating system (only said "server-rendered HTML")
- which test framework
- which license
This ADR closes those gaps with concrete choices, named alternatives, and the rollback path for each.
## Decision
| Concern | Choice | One-line reason |
|---|---|---|
| HTTP router | `go-chi/chi` (v5) for v0.1 | SSE middleware story is cleaner than hand-rolled on `http.ServeMux`; chi is the lightest router that still gives us real middleware composition. |
| SQLite driver | `modernc.org/sqlite` (pure Go) | Keeps the "no CGO" promise that the static-binary distribution rests on. |
| Templates | stdlib `html/template`, embedded with `//go:embed` | Auto-escaping by default, no template-engine dependency, no runtime parsing of the filesystem. |
| Test framework | stdlib `testing` + `net/http/httptest` | No assertion library unless pain demands one. |
| License | Apache-2.0 | Permissive, explicit patent grant, matches Go ecosystem norms. |
| Config | Environment variables only (`PORT`, `HATCH_DB_PATH`, `HATCH_LISTEN`) | Twelve-factor. No YAML/JSON config files for v0.1. |
| Logging | stdlib `log` to stdout, structured via `slog` (Go 1.21+) | No third-party logging library until we need log shipping. |
| Process model | Single process, one port, in-process SSE hub | v0.1 is a self-hosted single-binary product. No workers, no sidecars. |
### Router: `go-chi/chi`
Chi v5 is the smallest router that still composes well. It sits on top of `http.Handler`, so it does not break the stdlib-first principle — it just gives us `r.Use(middleware.Logger)`, `r.Route("/e/{id}", func(r chi.Router) { ... })`, and a clean SSE middleware (`middleware.Flush`).
**Why not stdlib `net/http` only?** Go 1.22 added method-based routing and path parameters to `http.ServeMux`. For v0.1's surface (~6 routes) it is genuinely enough. We adopt chi for two reasons: (1) the SSE middleware in `chi/middleware` is one line vs. ~20 lines of hand-rolled flush-on-write logic, and (2) the moment we add a second cross-cutting concern (request ID, auth-ready, rate limit) the hand-rolled composition gets ugly fast.
**Reversibility.** Drop-in: chi is an `http.Handler`. Reverting to stdlib `ServeMux` is a one-file change to `cmd/hatch/main.go`. The handler layer is router-agnostic by construction.
### SQLite driver: `modernc.org/sqlite`
Pure Go. Translates SQLite's C source via `ccgo` at build time, so the produced driver is a normal Go package with no `cgo` import.
**Why not `mattn/go-sqlite3`?** It is the most popular SQLite driver, but it requires CGO. CGO forces us to either (a) ship a glibc-linked binary that breaks on musl-based images, or (b) maintain a separate CGO build pipeline. Both are paid complexity for a feature (a C compiler in CI) we do not otherwise need.
**Why not the stdlib `database/sql` only?** The `database/sql` package is the API; it is not a driver. We still need a driver, and we still need migrations, and we still need a `Repository` interface above it. The choice here is the driver.
### Templates: stdlib `html/template`, `//go:embed`-ed
`html/template` auto-escapes context-sensitively (HTML, JS, URL, CSS contexts). It is in the standard library, has no dependencies, and produces no runtime surprises. We embed templates at build time with `//go:embed templates/*.html` so the binary is self-contained and there is no filesystem layout to manage at deploy time.
**Why not `templ` or `jet`?** Both add a build step (`templ generate`) to the dev loop. The v0.1 surface is small enough that a hand-rolled `{{range .Requests}}` is not a liability. We will revisit if template churn becomes a real cost.
### Test framework: stdlib `testing` + `net/http/httptest`
`testing` is in the standard library, plays well with `go test`, and is what every Go engineer already knows. `httptest` gives us `httptest.NewRecorder` for handler unit tests and `httptest.NewServer` for end-to-end smoke tests without a real network socket.
**Why not `testify` or `gocheck`?** No assertion library has yet earned its place. We will adopt one when the boilerplate of `if got != want { t.Errorf(...) }` becomes a real cost, not before.
### License: Apache-2.0
Permissive. Explicit patent grant. Matches what the Go ecosystem uses (Kubernetes, Docker, the Go toolchain itself are permissive-licensed). Compatible with corporate adoption without legal-review friction.
**Why not MIT?** MIT is also acceptable, but the patent grant in Apache-2.0 is a real protection for downstream contributors and is a default in our target ecosystem.
## Consequences
### Positive
- The full v0.1 dependency surface is: `go-chi/chi`, `modernc.org/sqlite`, and `github.com/google/uuid` (if we use it for endpoint IDs). Everything else is stdlib. That is a small, reviewable surface.
- The single-binary distribution story holds: `CGO_ENABLED=0 go build` produces a working static binary, and the Docker build ends in `scratch` with no runtime.
- The license, the test framework, and the template engine are all things an incoming Go engineer already knows. Zero ramp cost on tooling.
- Each choice has a one-day-or-less reversal path. The stack is composable from independent parts; nothing in this ADR forces a bigger rewrite to undo.
### Negative / Risks
- Chi is one more dependency to track. If a future Go stdlib release subsumes the middleware story, we have a small migration.
- `modernc.org/sqlite` is slower than the C version on raw throughput benchmarks (~23× on simple SELECTs). For v0.1's workload (a self-hosted single-user product) this is invisible. We will measure before optimizing.
- Stdlib `html/template` has no inheritance, no partials-with-arguments, and no "components." If template complexity grows, we will feel it. The mitigation is to keep the page count low (Capture page, Inspect page, Mock config page, error page).
- Apache-2.0 is more verbose than MIT. Trivial cost; mentioned for completeness.
## Alternatives Considered
### Option A: `net/http` only (no chi)
- Pros: zero router dependency; one fewer thing to upgrade; Go 1.22's `ServeMux` is genuinely good.
- Cons: SSE flush middleware is hand-rolled; auth/cors/rate-limit middleware are also hand-rolled; the cost of NOT having middleware composition grows with every new cross-cutting concern.
- Why rejected: SSE is in v0.1 (Inspect live). The first place we will want a second middleware is auth, even if auth itself ships later. Paying for chi now is cheaper than paying for hand-rolled composition later.
### Option B: `mattn/go-sqlite3`
- Pros: most popular SQLite driver, fast, well-known.
- Cons: requires CGO. Breaks the static-binary promise or forces a second build pipeline.
- Why rejected: the cost of CGO exceeds the value of `mattn`'s ecosystem familiarity.
### Option C: `templ` for templates
- Pros: type-safe templates, IDE autocompletion, components.
- Cons: requires a code-generation step (`templ generate`) on every save. The dev loop slows down. The runtime feature set is ~equivalent to `html/template` for our surface.
- Why rejected: the v0.1 template surface is small enough that hand-written `html/template` is not yet a liability. We can adopt `templ` later if template churn becomes a real cost.
### Option D: MIT license
- Pros: shorter, more familiar to individual contributors.
- Cons: no explicit patent grant.
- Why rejected: Apache-2.0's patent grant is a real downstream protection and matches our target ecosystem. The longer license header is a trivial cost.
## Rollback Plan
| Choice | Rollback |
|---|---|
| `go-chi/chi` | Replace `chi.Router` with `http.ServeMux` in `cmd/hatch/main.go`. Handlers and store are unchanged. ~half a day. |
| `modernc.org/sqlite` | Swap to `mattn/go-sqlite3`. Add CGO to the Dockerfile. Update CI to install `gcc`. ~half a day, plus the Dockerfile/CI change. |
| `html/template` | Adopt `templ` (or `jet`). Adds a code-generation step. ~1 day plus regen. |
| stdlib `testing` | Adopt `testify`. Mechanical import swap. ~2 hours. |
| Apache-2.0 | Relicense with contributor agreement. Not a code change, but a legal process. Not reversible by us alone. |
| Env-only config | Adopt `viper` or `koanf`. ~1 day including flag plumbing. |
The first five rollbacks are independent and can be done in any order. The license change is not ours to make unilaterally.
## Related
- [ADR-0001](https://github.com/elfoundation/hatch/blob/main/docs/adrs/0001-stack.md) — parent decision
- [Hatch v0.1 plan (ELF-13)](https://github.com/elfoundation/hatch/issues/13) — build plan and ordering
- [`docs/engineering/tech-stack.md`](../engineering/tech-stack.md) — current stack summary
- [`docs/engineering/hatch-architecture.md`](../engineering/hatch-architecture.md) — component map
- [`docs/engineering/local-dev.md`](../engineering/local-dev.md) — day-to-day commands

View File

@ -4,6 +4,8 @@
El Foundation has established its charter, operating model, and engineering baseline. We are pre-product but have a clear technical direction. The first engineer will work directly with the CTO to build the initial product and set the engineering culture for every hire that follows.
The engineering stack is **Go + SQLite + stdlib `net/http` + server-rendered HTML + SSE**, per [ADR-0001](https://github.com/elfoundation/hatch/blob/main/docs/adrs/0001-stack.md) and [ADR-0002](adrs/0002-hatch-detail-stack.md). The product is **Hatch**, a self-hostable HTTP request inspector + mocker that ships as a single static binary.
## What They Will Own
- **Product implementation.** Write the first lines of production code. Own features end-to-end from task assignment to merge.
@ -16,37 +18,47 @@ El Foundation has established its charter, operating model, and engineering base
### Must-Have
- **TypeScript** — strong typed-language fundamentals, comfortable with strict mode
- **React / Next.js** — experience with App Router, Server Components, and modern React patterns
- **Relational databases** — schema design, query optimization, migration discipline (PostgreSQL preferred)
- **Git and GitHub** — branching, rebasing, PR discipline, code review
- **Testing mindset** — writes tests as a default, not an afterthought
- **Go** — comfortable with the language, idiomatic style, the `net/http` package, and the standard library. A `go test ./...` / `go vet ./...` workflow is the daily loop.
- **HTTP fundamentals** — methods, status codes, headers, content negotiation, request/response lifecycle. SSE and chunked transfer are a plus.
- **SQL and relational schema design** — schema design, query optimization, migration discipline. SQLite-specific knowledge is a plus but not required.
- **Git and GitHub** — branching, rebasing, PR discipline, code review.
- **Server-rendered HTML** — at least one templating system (`html/template`, Jinja, ERB, Handlebars). Comfort with progressive enhancement.
- **Testing mindset** — writes tests as a default, not an afterthought. Knows when to reach for `httptest`, table tests, and in-memory fixtures.
### Nice-to-Have
- **Prisma ORM** — or similar type-safe ORM experience
- **Tailwind CSS** — or strong utility-first CSS experience
- **Monorepo tooling** — Turborepo, Nx, or similar
- **Cloud infrastructure** — Vercel, AWS, Fly.io, or similar
- **Authentication / security** — OAuth, JWT, session management
- **Mongolian language or market context** — our early users are likely in Mongolia
- **`net/http` internals** — middleware, `http.Handler` composition, the `ServeMux` method-based routing added in Go 1.22.
- **Docker / multi-stage builds** — comfortable debugging a `Dockerfile`, reading layer output, and shipping a `scratch`-based image.
- **SQLite** — pragmas, indexes, JSON columns, the `modernc.org/sqlite` driver.
- **Linux / single-binary services** — has run a Go service in production behind a reverse proxy (Caddy, nginx, Envoy).
- **Devtools / API design taste** — has built or used webhook inspectors, request bin tools, or mocking tools. Empathy for the user is the job.
- **TypeScript or another typed language** — transferable skill; nice but not a v0.1 requirement.
- **Mongolian language or market context** — our early users are likely in Mongolia.
### Explicitly Deferred (not required for v0.1)
- Frontend frameworks (React, Next.js, Vue) — Hatch v0.1 is server-rendered. We may adopt one later; we are not starting there.
- TypeScript / Node.js — the foundation is Go. TypeScript remains in the toolbox for tooling only.
- Monorepo tooling (Turborepo, Nx) — single Go module, single binary, no need.
- Tailwind / utility CSS — hand-written CSS is enough for v0.1 surfaces.
## Attributes We Value
- **Slope over intercept.** We care more about how fast you learn than what you already know.
- **Slope over intercept.** We care more about how fast you learn than what you already know. Go fluency is learnable; engineering judgment is not.
- **Writes things down.** Async-first communication. Clear documentation. Decision records.
- **Disagrees and commits.** Healthy dissent, then full commitment once a decision is made.
- **Protects focus.** Ruthless prioritization. Says no to multitasking.
- **Pulls for bad news.** Surfaces problems early. Does not hide blockers.
- **Boring by default.** Reaches for the standard library before adding a dependency.
## First 30-Day Priorities
| Week | Focus | Deliverable |
|---|---|---|
| 1 | Onboard and ship a small fix | Merged PR, environment verified |
| 2 | Build first feature slice | Working code in staging |
| 3 | Establish testing pattern | Test coverage for new code, CI green |
| 4 | Document and refine | Updated docs, onboarding feedback, first ADR contribution |
| 1 | Onboard and ship the bootstrap | Merged `engineer/hatch-bootstrap` PR with `cmd/hatch` HTTP server, `Dockerfile`, CI green |
| 2 | Ship Task 2 (SQLite storage layer) | Merged PR with `internal/storage/...`, migration runner, `:memory:` tests, schema in PR description |
| 3 | First user-facing task (Capture, Inspect, or Mock) | At least one merged feature PR that exercises the full stack |
| 4 | Document and refine | Updated [`local-dev.md`](local-dev.md) / [`hatch-architecture.md`](hatch-architecture.md) with real friction, first own-authored ADR |
## Reporting
@ -63,7 +75,9 @@ El Foundation has established its charter, operating model, and engineering base
## Recommendation
**Hire a mid-level full-stack engineer with strong TypeScript and Next.js experience.** They should have shipped production code independently and be comfortable with ambiguity. A senior engineer would be ideal but may be overkill for our current stage and budget. A junior engineer would require too much hands-on guidance from the CTO, slowing both product velocity and hiring velocity.
**Hire a mid-level engineer with Go and HTTP-services experience.** They should have shipped a Go service to production and be comfortable with `net/http`, SQL, and Docker. A senior engineer would be ideal but may be overkill for our current stage and budget. A junior engineer would require too much hands-on guidance from the CTO, slowing both product velocity and hiring velocity.
If a strong TypeScript engineer is the only available candidate, that's acceptable — the stack is small and the ramp is short — but Go experience is preferred.
**Suggested title:** Software Engineer
**Suggested level:** Mid-level (25 years shipping production code)

View File

@ -0,0 +1,144 @@
# Hatch Architecture
Living document. The CTO sketches the component map; the engineer owns the details as the code lands.
## One-sentence summary
Hatch is a single Go binary that serves a server-rendered HTML UI and captures, inspects, and mocks HTTP requests against per-endpoint URLs, persisting everything in a single SQLite file.
## High-level component map
```
┌─────────────────────────────────────┐
│ Browser (Hatch UI) │
│ GET /e/{id} + EventSource /events│
└──────────────┬──────────────────────┘
│ HTTP / SSE
┌──────────────────────────────────────────────────────────────┐
│ hatch (Go binary) │
│ │
│ ┌──────────────┐ ┌────────────────┐ ┌───────────────┐ │
│ │ http.ServeMux│──▶│ handler layer │──▶│ store layer │ │
│ │ (stdlib) │ │ (internal/ │ │ (internal/ │ │
│ │ │ │ handler/) │ │ store/) │ │
│ └──────────────┘ └────────────────┘ └───────┬───────┘ │
│ │ │ │
│ │ ┌────────────────┐ │ │
│ └────────────▶│ SSE hub │◀─────────┘ │
│ │ (broadcast │ │
│ │ new requests)│ │
│ └────────┬───────┘ │
│ │ │
│ ┌────────▼───────┐ │
│ │ html/template │ │
│ │ (server-render)│ │
│ └────────────────┘ │
└──────────────────────────────┬───────────────────────────────┘
│ modernc.org/sqlite (pure Go)
┌──────────────┐
│ hatch.db │
│ (SQLite) │
└──────────────┘
```
## Layer responsibilities
### `cmd/hatch/main.go` — process entrypoint
- Reads configuration (env: `PORT`).
- Wires the `http.ServeMux` to the handler layer.
- Starts `http.ListenAndServe`. Logs to stdout. Crashes on bind failure.
### `internal/handler/` — HTTP layer
- One file per route group: `capture.go`, `inspect.go`, `mock.go`, `sse.go`, `health.go`.
- Handlers take a `store.Repository` (interface) — never a concrete type — so tests can swap in `:memory:` SQLite or a fake.
- Handlers translate HTTP ↔ store calls. They do not contain business logic.
- Server-rendered HTML lives next to the handler that renders it. Templates are `//go:embed`-ed at build time.
### `internal/store/` — persistence layer
- `schema.sql` is the canonical DDL. Applied idempotently on first start by `migrate.go`.
- `models.go` defines the Go structs (`Endpoint`, `Request`).
- `repository.go` defines the `Repository` interface. The HTTP layer depends on this interface, not on the SQLite implementation.
- `sqlite_repo.go` is the concrete implementation. Uses `modernc.org/sqlite` (pure Go, no CGO).
- `db.go` opens the database file (or `:memory:` for tests), configures pragmas, returns a `*sql.DB`.
### SSE hub (in `internal/handler/sse.go`)
- A goroutine per connected browser, holding an `http.Flusher`.
- A `chan store.Request` that capture handlers publish to.
- The hub fans out new requests to all subscribers for the relevant endpoint.
- No external broker. No Redis. No pubsub library. A `chan` and a `map[endpointID][]chan` is the entire implementation for v0.1.
## Request lifecycle
1. **Capture** — a webhook hits `/{endpoint-id}` (any method). The capture handler:
- Reads method, path, query, headers, body.
- Calls `store.AppendRequest(ctx, request)`.
- Publishes the new request to the SSE hub.
- Looks up the endpoint's mock response and returns it.
2. **Inspect** — a browser hits `GET /e/{endpoint-id}`. The inspect handler:
- Calls `store.ListRequests(ctx, endpointID, limit=100)`.
- Renders an HTML page with the request list (newest first).
- The page includes a small vanilla-JS `EventSource` client that subscribes to `/e/{endpoint-id}/events`.
3. **Live update** — the SSE handler holds the connection open, flushes each new request as a `data:` frame, and the browser appends it to the list.
4. **Mock**`PUT /e/{endpoint-id}/mock` accepts `{status, headers, body}` and updates the endpoint. Subsequent captures to that endpoint return the configured response.
## Data model
Two tables. That's it for v0.1.
```
endpoints
id TEXT PRIMARY KEY -- URL-safe random ID, e.g. "h7c2k"
created_at INTEGER NOT NULL -- Unix epoch seconds, UTC
mock_status INTEGER -- nullable; 200/201/204/etc.
mock_headers TEXT -- JSON object, nullable
mock_body BLOB -- nullable
requests
id INTEGER PRIMARY KEY AUTOINCREMENT
endpoint_id TEXT NOT NULL REFERENCES endpoints(id)
received_at INTEGER NOT NULL -- Unix epoch seconds, UTC
method TEXT NOT NULL
path TEXT NOT NULL -- request path within the endpoint
query TEXT NOT NULL -- raw query string
headers TEXT NOT NULL -- JSON object
body BLOB -- nullable
remote_addr TEXT -- for debugging
FOREIGN KEY (endpoint_id) REFERENCES endpoints(id) ON DELETE CASCADE
```
Index on `requests(endpoint_id, received_at DESC)` for the list page query.
## Boundaries
- **No authentication.** v0.1 is single-user, self-hosted. If you can reach the port, you can read and write. See [v0.1-scope.md](../adrs/v0-1-scope.md).
- **No multi-tenancy.** One SQLite file, one process, one operator. Cloud is v0.2+.
- **No external services.** No Redis, no Postgres, no S3. If the binary needs to phone home, the design is wrong.
- **No client-side framework.** The browser gets HTML and a 50-line `events.js`. Anything more is out of scope for v0.1.
## Performance budget
v0.1 is sized for a single developer self-hosting on a $5 VPS:
- **Cold start:** under 100 ms (Go binary + SQLite open).
- **Capture latency:** under 5 ms p99 on the happy path (no auth, no remote calls).
- **SSE fan-out:** one goroutine per connected browser, no message broker.
- **Database size:** comfortable to 100k requests per endpoint. Retention policy is out of scope for v0.1.
If a real workload breaks these, we measure first, then add complexity.
## Future seams (do not build now)
These are deliberate extension points, not planned features. The v0.1 implementation should not block them but also should not build them.
- **Pluggable storage** — the `Repository` interface is the seam. A Postgres or S3-backed implementation would slot in without handler changes.
- **Pluggable auth** — a middleware in front of the mux. The v0.1 mux is exposed without auth, which is the right v0.1 default.
- **Replay / forwarding** — the `Request` struct has the full request. Replay is "read from store, write back out." Forwarding is the same plus a destination.
- **Multi-tenant cloud** — partitioning by `endpoint_id` is already first-class. A `tenant_id` column is a v0.2 change.
When the v0.1 design cannot accommodate one of these without a rewrite, we have under-designed. When the v0.1 design has built one of these speculatively, we have over-designed.

View File

@ -0,0 +1,147 @@
# 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.

View File

@ -14,15 +14,20 @@
- [ ] Read [how we decide](../company/how-we-decide.md)
- [ ] Read [CONTRIBUTING.md](../../CONTRIBUTING.md)
- [ ] Read [tech-stack.md](tech-stack.md)
- [ ] Read [local-dev.md](local-dev.md)
- [ ] Read [hatch-architecture.md](hatch-architecture.md)
- [ ] Read [ADR-0002: Hatch detail stack](adrs/0002-hatch-detail-stack.md)
- [ ] Introduce yourself in the team channel (async written standup format)
## Day 23: Environment
- [ ] Install Go 1.25 (or newer) — see the [Go install instructions](https://go.dev/doc/install) if you do not have it
- [ ] Verify `go version` reports 1.25 or newer
- [ ] Clone the repo
- [ ] Install dependencies (`pnpm install` when package.json exists)
- [ ] Run the dev server locally
- [ ] Verify you can run tests
- [ ] Verify you can run lint
- [ ] Run `go mod download` to fetch dependencies
- [ ] Run `go test ./...` — should pass on a fresh clone
- [ ] Run `go run ./cmd/hatch` and `curl http://localhost:8080/healthz` — should return `ok`
- [ ] Run `docker compose up --build` and visit `http://localhost:8080/healthz` — should return `ok`
- [ ] Open your first PR (a README typo fix or doc improvement counts)
## Week 1: First Task

View File

@ -2,48 +2,66 @@
## Overview
These choices were made by the CTO on 2026-06-22, aligned with the company charter and operating principles. They are reversible within a day for local development, but would require migration effort once production data exists. All choices default to boring, well-supported technology over novelty.
These choices were made by the CTO on 2026-06-22 to align the engineering foundation with [ADR-0001](https://github.com/elfoundation/hatch/blob/main/docs/adrs/0001-stack.md) and the Hatch product thesis. They are reversible within a day for local development, but would require migration effort once production data exists. All choices default to boring, well-supported technology over novelty.
## Frontend
| Layer | Choice | Rationale |
|---|---|---|
| Framework | Next.js (App Router) | Full-stack React with SSR/SSG, API routes, and a large ecosystem. App Router is the stable future path. |
| Language | TypeScript (strict) | Catches entire classes of bugs at build time. Strict mode is non-negotiable. |
| Styling | Tailwind CSS | Utility-first, colocated with components, no separate CSS files to maintain. |
| UI Components | shadcn/ui pattern | Copy-paste components we own and can customize. No opaque UI library dependencies. |
The stack is chosen so that a single `docker compose up` (or one binary on a VPS) is the entire product — no separate database server, no build step, no auth process. This is the v0.1 distribution promise; if a choice makes that promise harder, it has to earn its place.
## Backend & Data
| Layer | Choice | Rationale |
|---|---|---|
| Database | PostgreSQL | Proven, feature-rich, great ORM support. Our relational data model fits it well. |
| ORM | Prisma | Type-safe queries, excellent migration tooling, good DX. |
| Auth | NextAuth.js | Battle-tested, supports many providers, integrates cleanly with Next.js. |
| Storage | Cloudflare R2 | S3-compatible, zero egress fees, good for file uploads and static assets. |
| Payments | TBD | Will evaluate when we have a revenue model. |
| Language | Go 1.25 | Single static binary, fast iteration, strong stdlib for HTTP and SQL. "One command on a VPS" requires a self-contained binary. |
| HTTP server | stdlib `net/http` (Go 1.22+ method-based routing) | No router dependency for a v0.1 surface. `go-chi/chi` is allowed where middleware is genuinely needed (SSE). |
| Database | SQLite via `modernc.org/sqlite` | Pure-Go driver keeps the "no CGO" promise. Survives restarts, zero-ops, single file you can `cp` to back up. |
| Migrations | Hand-rolled runner that applies `internal/storage/schema.sql` on first start, idempotently | Premature tooling is paid complexity. One SQL file, one `CREATE TABLE IF NOT EXISTS`, ship it. |
| Templates | stdlib `html/template` | Server-rendered HTML with auto-escaping. No JS build step, no hydration cost, no SPA complexity for what is fundamentally a server-driven UI. |
| Live updates | Server-Sent Events (SSE) on `net/http` | One-way push from server to browser, no WebSocket framing, plays nicely with the `html/template` render path. |
## Frontend
| Layer | Choice | Rationale |
|---|---|---|
| UI architecture | Server-rendered HTML + a little vanilla JS | The UI is a small request list with one live update. JS is loaded only where it earns its bandwidth (the SSE client). No bundler. |
| CSS | Hand-written CSS in `internal/handler/assets/` | Tailwind and friends add a build step we explicitly do not want in v0.1. A small `style.css` is enough for the surfaces we ship. |
| Client JS | Vanilla JS (no framework, no transpiler) | One `events.js` for the SSE subscription. If we ever need more, we will earn a toolchain. |
## Packaging & Distribution
| Layer | Choice | Rationale |
|---|---|---|
| Container | Multi-stage `Dockerfile` (golang:1.25 → scratch) | Produces an 810 MB static binary image. No runtime, no shell, no package manager on the final image. |
| Local dev | `docker compose` with optional Caddy sidecar | `docker compose up` is the one-command demo. Caddy handles TLS for the demo host and stays out of the way for plain HTTP. |
| Demo TLS | Caddy (auto-issues Let's Encrypt in prod, self-signed in dev) | Hands-off TLS, sane defaults, plays well with the static-binary promise. |
| Config | Env vars (read with `os.Getenv`) | Twelve-factor. No YAML/JSON config files for v0.1. Defaults documented in `.env.example`. |
## Tooling
| Layer | Choice | Rationale |
|---|---|---|
| Package Manager | pnpm | Fast, disk-space efficient, strict `node_modules` layout avoids phantom dependencies. |
| Monorepo | Turborepo (when needed) | Caching and task orchestration. Only adopt when we have >1 app or shared package. |
| CI/CD | GitHub Actions | Native GitHub integration, free for public repos, cheap for private. |
| Lint | ESLint + Prettier | Standard, autofixable, low-friction. |
| Build | `go build` (with `CGO_ENABLED=0` for the final binary) | No Makefile needed for the common path. |
| Tests | stdlib `testing` + `net/http/httptest` | No assertion library until pain demands one. |
| Vet / lint | `go vet ./...` in CI | Stdlib tooling, no `golangci-lint` config to maintain. |
| CI | GitHub Actions (`.github/workflows/ci.yml`) | `go vet``go test``docker build` → smoke-test the running image. |
| Markdown lint | `markdownlint-cli2` (non-blocking) | Editorial consistency, not a gate. |
| Module path | `github.com/elfoundation/hatch` | Matches the GitHub org. Easy to change before any external consumer depends on it. |
## Principles
- **Server Components by default.** Reach for `"use client"` only when the component actually needs state, effects, or browser APIs.
- **Pure logic in `lib/`, I/O in `services/`.** Business logic does not import from `next/server` or call `fetch` directly.
- **Store money as integers.** MNT (Mongolian Tugrik) in smallest unit. Format on display.
These are stack-agnostic and outlive any choice in the tables above. They are the cultural defaults; the tables are the implementation.
- **Server-rendered by default.** Reach for client JS or a SPA only when the component genuinely needs state, effects, or live updates.
- **Pure logic in `internal/`, I/O in adapters.** Business logic does not call `http.*` or `database/sql` directly. Storage and HTTP are replaced with interfaces in tests.
- **Store timestamps as UTC.** Render in local time only at the edge.
- **Single static binary, single file database.** If a dependency breaks that, justify it.
- **Observability before optimization.** Measure before fixing. No tuning without metrics.
- **Idempotency.** Operations should be safe to retry. Infrastructure changes should be reproducible.
- **Idempotency.** Operations should be safe to retry. Migrations, request capture, and SSE reconnect are all idempotent.
- **Boring technology.** A new dependency must earn its place. If stdlib covers it, stdlib wins.
## Open Decisions
| Decision | Status | Owner | Blocker |
|---|---|---|---|
| Hosting provider (Vercel, Fly, AWS?) | Open | CTO | Need product requirements and traffic estimates |
| Monitoring / alerting stack | Open | CTO | Need hosting decision |
| CDN / edge strategy | Open | CTO | Need hosting decision |
| Router (stdlib `net/http` vs. `go-chi/chi`) | Provisional — start with stdlib, adopt chi if SSE middleware pain demands it | CTO | Live SSE implementation in v0.1 Inspect task |
| Structured logging library | Open — `slog` (stdlib) likely sufficient | CTO | First production deploy |
| Tracing / metrics | Open | CTO | Hosting decision |
| Hosting target (Fly, single VPS, k8s?) | Open | CEO | Need product requirements and traffic estimates |