feat: scaffold app skeleton with config, ULID, health endpoints, compose stack (phase 1)
- internal .env loader (real env wins) + strictly validated config struct - ULID package: 48-bit ms timestamp + 80-bit crypto randomness, sortable - HTTP server with recovery/request-id/trusted-proxy/access-log middleware - /healthz, /readyz (pluggable checkers), /metricsz (counter registry) - multi-stage Dockerfile (alpine, non-root) + compose with healthchecks, app bound to 127.0.0.1:8787, mongo unpublished, graceful 15s shutdown Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# Decisions
|
||||
|
||||
Spec-silent choices, recorded as required by the build instructions.
|
||||
|
||||
## Phase 1
|
||||
|
||||
- **Go toolchain 1.26.4** (latest stable; spec requires ≥ 1.22). Installed at
|
||||
`/usr/local/go`.
|
||||
- **Module name `bountyboard`** — no remote repository exists; a single-word
|
||||
module keeps import paths short (`bountyboard/internal/...`).
|
||||
- **Package in `internal/http` is named `httpx`** to avoid clashing with
|
||||
stdlib `net/http` inside its own files. The directory stays `internal/http`
|
||||
per spec §14; importers alias it (`httpx "bountyboard/internal/http"`).
|
||||
- **`/readyz` uses a pluggable checker registry** (`AddReadinessCheck`):
|
||||
required checks (Mongo, Phase 2) gate readiness with 503; optional checks
|
||||
(atomizer / work performer, Phase 11) are reported as `degraded` but stay
|
||||
200, matching §6's "non-fatal, reported".
|
||||
- **Runtime image is `alpine:3.21`, not distroless** — busybox `wget` enables
|
||||
the compose healthcheck without adding tooling, and `ca-certificates` is
|
||||
needed for outbound HTTPS to Jira/ADO/YouTrack. Non-root user.
|
||||
- **Config validation is strict and fail-fast**: `CREDENTIALS_ENC_KEY` is
|
||||
mandatory at startup (base64, exactly 32 bytes) and `SESSION_SECRET` must be
|
||||
≥ 16 chars, even before the features using them land — a misconfigured
|
||||
deployment should die at boot, not at first use. All validation errors are
|
||||
reported together via `errors.Join`.
|
||||
- **Trusted-proxy resolution walks X-Forwarded-For right-to-left**, skipping
|
||||
addresses inside `TRUSTED_PROXY_CIDRS`; the first untrusted address is the
|
||||
client. A malformed entry stops the walk (everything left of it is
|
||||
attacker-controllable). If the whole chain is trusted, the leftmost entry is
|
||||
used. Headers are ignored entirely when the direct peer is untrusted.
|
||||
- **ULID**: canonical 48-bit ms timestamp + 80-bit crypto randomness, no
|
||||
intra-millisecond monotonicity (spec only needs sortability at ms
|
||||
resolution; randomness makes collisions negligible).
|
||||
- **`.env` loader semantics**: real environment variables win over `.env`
|
||||
values; unquoted values support trailing ` # comment`; missing `.env` file
|
||||
is not an error (containers receive env via compose `env_file`).
|
||||
- **Makefile `seed`/`backup`/`restore` are failing stubs until Phase 12** so
|
||||
the targets exist but cannot be mistaken for working.
|
||||
Reference in New Issue
Block a user