f2c534f636
- mongo-driver v2 connection with bounded startup retry + /readyz gate - idempotent creation of all §4.9 indexes - UpdateVersioned: version-filtered updates, conflict vs not-found errors - AES-256-GCM Seal/Open for ticketing credentials, base64(nonce|ct) - GridFS file store: MIME sniffing, MAX_UPLOAD_MB cap, sha256 metadata - HMAC-signed short-lived file URL tokens (§5.1, 1h TTL) - integration tests against compose Mongo via docker-compose.test.yml overlay Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.5 KiB
3.5 KiB
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/httpis namedhttpxto avoid clashing with stdlibnet/httpinside its own files. The directory staysinternal/httpper spec §14; importers alias it (httpx "bountyboard/internal/http"). /readyzuses a pluggable checker registry (AddReadinessCheck): required checks (Mongo, Phase 2) gate readiness with 503; optional checks (atomizer / work performer, Phase 11) are reported asdegradedbut stay 200, matching §6's "non-fatal, reported".- Runtime image is
alpine:3.21, not distroless — busyboxwgetenables the compose healthcheck without adding tooling, andca-certificatesis needed for outbound HTTPS to Jira/ADO/YouTrack. Non-root user. - Config validation is strict and fail-fast:
CREDENTIALS_ENC_KEYis mandatory at startup (base64, exactly 32 bytes) andSESSION_SECRETmust 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 viaerrors.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).
.envloader semantics: real environment variables win over.envvalues; unquoted values support trailing# comment; missing.envfile is not an error (containers receive env via composeenv_file).- Makefile
seed/backup/restoreare failing stubs until Phase 12 so the targets exist but cannot be mistaken for working.
Phase 2
- Integration tests reach Mongo via
docker-compose.test.yml, an explicit test-only overlay publishing Mongo on127.0.0.1:27017. The normal compose file still never publishes Mongo (§12); the spec's integration-test requirement (§2.1) needs host access, and a loopback-only opt-in overlay is the smallest hole. Each run uses abountyboard_test_<ulid>database and drops it on cleanup. UpdateVersionedrejects updates that touchversionand mergesupdatedAt/$inc versioninto the caller's operators; it distinguishesErrNotFoundfromErrVersionConflictwith a follow-up existence check.- File MIME type = sniffed (
http.DetectContentType) unless inconclusive (application/octet-stream), in which case the client-declared type is kept. A confident sniff overrides a lying declaration. - sha256 of uploads is recorded post-upload via an update on
fs.filesmetadata (GridFS metadata must be supplied before streaming; the hash is only known after). - Signed file tokens are
base64url(exp || hmac-sha256(fileID|exp))with a key derived assha256("bountyboard/file-url/v1" + SESSION_SECRET)so file tokens can never collide with other uses of the session secret.