15 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | plan_head_before | plan_head_after | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14-domain-jobs-and-external-integrations | 01 | infra |
|
|
|
|
|
06d6fdc758 |
7700c6da28 |
|
|
|
|
|
25min | 2026-10-03 | complete |
Phase 14 Plan 01: Framework helpers for vendor calls Summary
fetchguard grows a guarded vendor client with a code-only replay seam, tide records and replays upstream vendor exchanges through a loopback MITM proxy, sunscreen redacts credentials from every application log, and beachcomber can tell a dropped index from an absent one.
Performance
- Duration: 25 min
- Started: 2026-10-03T17:37:53Z
- Completed: 2026-10-03T18:02:30Z
- Tasks: 5
- Files modified: 41 in summercms.go, 1 in fonoteka.go
Accomplishments
fetchguard.NewClientsends any method with JSON or multipart bodies and bearer headers, caps the response, returns status and headers unjudged and never follows a redirect.TrustedMode(declared afterPublicOnlyMode) lifts the scheme, host and dial checks for operator endpoints, and only Go code can choose it.WithTransportis the test seam and lives only in a context value.- tide sidecars (
<fixture>.upstream.yaml) hold the vendor exchanges of a fixture.UpstreamFakereplays them without dialing and asserts method, scheme, host, path, query, compared headers, and the body (JSON semantically, multipart part by part, base64 photos by sha256).Verifyreports mismatches, extra requests and unconsumed exchanges, and never prints credential values. summer parity:upstreamruns a loopback CONNECT proxy that terminates TLS with a local ECDSA parity CA (key mode 0600, outside the fixtures tree). It answers from a script or forwards once through a PublicOnlyMode client, then writes the masked sidecar. An unmasked Authorization or X-Api-Key refuses the write.- New
sunscreenmodule:Wrapredacts the RedactCredentialsTap keys and scrubs Bearer,sk-andx-api-key:shapes. Every generated main now callssunscreen.InstallDefault(os.Stderr)first; the example and application mains were regenerated with it. - beachcomber
DropIndexandEnsureIndex(optionalIndexDropperandIndexEnsurer), implemented by Typesense. - REQUIREMENTS, ROADMAP and PROJECT now describe the Phase 14 scope as decided.
Task Commits
- Task 1: guarded JSON POST answered by a tide upstream fake (tracer):
93b7142(feat) - Task 2: PUT, multipart, bearer and trusted mode, plus docs:
e6a6713(feat) - Task 3: summer parity:upstream recording proxy and offline replay:
ee0004f(feat) - Task 4: sunscreen handler, generated mains, surf redaction test:
7241704(feat); beachcomber DropIndex/EnsureIndex:58e6324(feat); fonoteka.go regenerated main:5738e82(chore, fonoteka.go repo) - Task 5: planning docs reworded:
7700c6d(docs)
The tracer feedback gate after Task 1 re-ran <verify> end to end. It passed, so the expansion tasks went ahead.
Files Created/Modified
modules/fetchguard/client.go: Client, the helpers, WithTransport, and the capped body readermodules/fetchguard/fetch.go,policy.go: shared transport builder,Result.Header,TrustedModemodules/tide/upstream.go: sidecar types, LoadUpstream/WriteUpstream, the asserting UpstreamFakemodules/tide/upstream_proxy.go: the recording proxy and EnsureParityCAmodules/sunscreen/*: the new module and its READMEmodules/beachcomber/searchable.go,typesense/engine.go: index lifecycle helpersinternal/build/build.go: the generated main installs sunscreencmd/summer/parity.go,main.go: theparity:upstreamcommand- Docs:
docs/services/logging.md(new), plus updates to outbound-http, parity-testing, search, console utilities, coming-from-wintercms and the architecture introduction
Decisions Made
See key-decisions in the frontmatter. The main ones:
Fetchstays guarded even if given TrustedMode, so the one-shot user-URL fetcher can never be unguarded.- Multipart sidecars keep plain field values (masked) and hash only file parts.
- Flush writes nothing when any request failed.
- Requirements are not marked complete, because this plan only lays the foundation for them.
Deviations from Plan
Auto-fixed Issues
1. [Rule 2 - Security] sunscreen also redacts dashed sk- keys
- Found during: Task 4
- Issue: PHP's pattern
sk-[A-Za-z0-9]{20,}misses Anthropic keys (sk-ant-api03-...), and Phase 14 sends exactly those keys out. - Fix: The pattern is now
sk-[A-Za-z0-9_\-]{20,}. This redacts more than PHP and is invisible to parity. - Files modified: modules/sunscreen/sunscreen.go
- Commit:
7241704
2. [Rule 2 - Security] Upstream mismatch messages never print credential values
- Found during: Task 1
- Issue: An Authorization or X-Api-Key mismatch error would echo the live token into CI logs.
- Fix: These two headers report "value differs". Request URLs in error messages drop the query, and long body diffs are clipped.
- Commit:
93b7142
3. [Rule 2 - Missing functionality] UpstreamPart type and MaxUpstreamBody (32 MiB)
- Found during: Task 3
- Issue: Recording multipart bodies as ordered parts needs an exported part type that the plan's artifact list did not name. Vision requests also carry base64 photos of up to about 14 MB, which exceeds tide's 8 MiB
DefaultMaxBody. - Fix: Added
tide.UpstreamPartandtide.MaxUpstreamBody, both documented in the README. - Commit:
ee0004f
4. [Rule 3 - Blocking] Client tests live in an external test package plus one internal file
- Issue: tide imports fetchguard for the proxy's forward mode, so an internal fetchguard test that imports tide would be an import cycle.
- Fix:
client_test.goispackage fetchguard_test. The redirect test, which needs the unexported loopback hooks, is inclient_internal_test.go. - Commit:
93b7142,e6a6713
5. [CLAUDE.md - one logical change per commit] Task 4 split into two summercms.go commits
- Fix: sunscreen (
7241704) and beachcomber (58e6324) are committed separately. The fonoteka.go main is committed alone in its own repo (5738e82).
Extra tests beyond the plan: TestLoadUpstreamAndPath, TestClientPutJSONHeaderOrder, TestNewClientNegativeLimitsUseDefaults, TestUpstreamProxyUnscriptedRequestFailsFlush, TestUpstreamProxyMultipartAndForwardGuard, TestInstallDefault.
Not changed: docs/architecture/introduction.md has no fetchguard wording in its HTTP row, only a link, so the only edit there is adding sunscreen to the Services row.
Total deviations: 5 (4 Rule 2/3 auto-fixes, 1 CLAUDE.md commit split). Impact: security hardening and test layout only, no scope creep.
Issues Encountered
- The local environment sets
FORCE_COLOR=3, which fails two existing bonfire non-TTY colour tests. The /tmp tmpfs also hit its disk quota while linking test binaries. Both are environmental and unrelated to this plan. The full suite is green withFORCE_COLOR=andTMPDIR/GOTMPDIRpointed at~/.cache/gotest-tmp.
Verification
- summercms.go:
go vet ./...andgo test ./... -count=1are green.go test ./cmd/summer -run '^(TestDocsTree|TestDocsCommandsMirrorGeneratedMain)$'andsummer docs:build --checkpass ("no problems found"). - fonoteka.go:
go vet ./...,go build ./...andgo test ./... -count=1 -shortare green with the regenerated main.go. - Every per-task
<verify>ran with-racewhere the plan asked. All named tests show--- PASS.
Known Stubs
None.
User Setup Required
None. No external service configuration required.
Next Phase Readiness
- 14-02 (Discogs), 14-04 (golem adapters) and 14-05 (G15Office job) can build one
fetchguard.Clientper vendor and test it throughtide.NewUpstreamFakeplusfetchguard.WithTransport. - Recording real PHP exchanges needs
php_parity.sh serveextended withHTTPS_PROXY=http://127.0.0.1:8425and the parity CA ascurl.cainfo/openssl.cafile(plan 14-02 scope). Assumption A1 (Guzzle/libcurl honour the env proxy) is still unverified against a live PHP run. - 14-03 reindex can use
beachcomber.EnsureIndexandbeachcomber.DropIndex.
Phase: 14-domain-jobs-and-external-integrations Completed: 2026-10-03
Self-Check: PASSED
All created files exist; commits 93b7142, e6a6713, ee0004f, 7241704, 58e6324, 7700c6d (summercms.go) and 5738e82 (fonoteka.go) are present.