Skip to content

feat: config-as-code CLI, SP_NODE_NAME worker identity, docs showcase media, and agent key redaction - #198

Merged
fclairamb merged 61 commits into
mainfrom
batch/2026-08-05-2
Aug 7, 2026
Merged

fclairamb merged 61 commits into
mainfrom
batch/2026-08-05-2

Conversation

@fclairamb

@fclairamb fclairamb commented Aug 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

Six specs implemented over 2026-08-05 → 2026-08-07. Each was implemented, QA'd against its scoped targets, and cleared by an independent completeness audit before being archived to specs/done/2026/08/.

Security

  • Agent private keys are never written to logs or stdout. Key material was previously echoed during agent bootstrap; printing it now requires opting in with SP_AGENT_PRINT_KEYS. A malformed SP_SYSTEM_AGENT_ENROLLMENT_TOKENS entry is no longer echoed either.

Features

  • Config-as-code for checks. sp checks export / import round-trip a check catalog as YAML, sp checks diff shows what an import would change before it runs, and sp checks validate accepts a whole export document offline — no server, no credentials — so a catalog can be linted in CI.
  • SP_NODE_NAME worker-identity override. Identity was hard-wired to os.Hostname(), which broke registration under Kubernetes hostNetwork: true (shared UTS namespace → node name with dots → workers_slug_check violation) and silently collapsed two workers onto one row whenever their hostnames shared 15 characters. Both workers now share one helper, the effective slug is validated at startup against a regex guard-tested to match the DB CHECK constraint, and hostname truncation emits a warning naming the resulting slug.
  • Docs Tour page. A regenerable Playwright pipeline (make showcase) drives the real dashboard to produce screenshots and an AV1 recording of the create-an-HTTP-check flow. Regenerates on demand rather than rotting as the UI changes; no CI wiring, by design.

Fixes

  • E2E specs across both suites read a dead E2E_API_BASE var or hardcoded localhost:4000, so a suite pointed at a test server was quietly validating the dev loop instead. Both now derive their origin from a shared fixtures module, with a lint guard in each config to stop it regressing.
  • Session-sensitive tests get a dedicated login instead of mutating the shared worker session.
  • kubernetes-cluster spec cleans up on assertion failure.
  • ValidateDocument no longer mutates its input.

No breaking changes. One behaviour change worth noting: a worker whose effective slug is illegal now fails fast at startup with an actionable message, where it previously started and then failed to register with an opaque SQLSTATE=23514.

Test plan

  • Backend: make build-backend lint-back test green.
  • dash0: make build-dash0 + bun run lint — no new errors in touched files (the ~44 pre-existing react-hooks errors are unchanged baseline).
  • status0: make build-status0 + bun run lint clean; the new eslint guard verified to actually fire on all three banned patterns, and to exempt the fixtures module.
  • docs: make build-docs green, all four /docs/showcase/* assets resolving in the built page.
  • Showcase pipeline run end to end against an isolated side-car; committed video confirmed AV1 via ffprobe, stills visually verified.
  • SP_NODE_NAME binding proven end-to-end through Load(), and the validator regex guard-tested against the actual migration constraint.

Not covered

One acceptance criterion on the worker-identity spec is deliberately unchecked: running a check worker with hostNetwork: true against an IPv6-only target needs a released build deployed from the k8xp repo. SP_NODE_NAME is what unblocks it.

Four of the five status0 e2e specs still can't run against a test-mode side-car — they navigate to the default org, which only the make dev seed creates. Pre-existing run-mode coupling, not introduced here.

🤖 Generated with Claude Code

fclairamb and others added 30 commits August 5, 2026 23:31
…he shared worker session

sessions.spec.ts and the two mcp-oauth-consent.spec.ts tests that depend on
their refresh-token row surviving (session list isCurrent, /auth/refresh
bounce) no longer ride the shared authWorkerStorageState session, which
enforceSessionCap (server/internal/handlers/auth/service.go, cap=10) evicts
as the least-recently-active row once ~60+ unrelated fresh logins land
elsewhere in the suite. Also makes sessions.spec.ts:80 target the session it
created by uid instead of "the first non-current row".
Two consecutive full E2E runs (--retries=0, single worker, fresh SP_DB_RESET
side-car server) both pass sessions.spec.ts and mcp-oauth-consent.spec.ts on
the first attempt. Only remaining failure is the pre-existing, unrelated
kubernetes-clusters.spec.ts:36 (hardcoded localhost:4000, out of scope).
bun run lint: no new errors in touched files (fixtures.ts, sessions.spec.ts,
mcp-oauth-consent.spec.ts) against the pre-existing dash0 eslint debt.
Adds the missing ### Verification (Proposal 4) section: per-run test counts
for the baseline and two post-fix full-suite runs, explicit sessions.spec.ts:4
and mcp-oauth-consent.spec.ts:156 status per run, and honest reconciliation
of the baseline's other two failures (kubernetes-clusters.spec.ts:36 is a
pre-existing wrong-base-URL bug, independently verified in all three runs;
discovery.spec.ts:357 did not recur post-fix and is flagged as an unproven
one-off). Also records that mcp-oauth-consent.spec.ts:156 was never
reproduced locally (it passed pre-fix too), so that half of Proposal 4's
acceptance criterion is CI-confirmable only, with the login-count reasoning
for why the theory still plausibly holds. Corrects the plan's endpoint
reference (GET /api/v1/orgs/test/tokens?type=refresh, not the nonexistent
GET /api/v1/auth/sessions) to match the shipped code.
Exports ParseManifest (was parseManifest) and adds ValidateDocument, a
pure/offline validator for the generic export-document format rules
(version, slug uniqueness/format, per-type config via each checker's
own Validate, duration/label/region formats, no inlined credentials,
expectedStatusCodes vs expectedStatus exclusivity, dependency graph
soundness). Org-specific convention rules are intentionally out of
scope. Groundwork for sp checks validate <file> (offline document
validation).
Auto-detects a top-level  array and routes to the new
ValidateDocument path (no token, no network) instead of the existing
single-check online validate. Satisfies Proposal 4's sp checks validate
<file> without renaming or shadowing the pre-existing single-check
command.
- sp checks export gains --format yaml|json, defaulting from the -o
  extension. JSON->YAML transcoding decodes via json.Decoder.Token()
  straight into a yaml.Node tree (never through map[string]interface{})
  so exports stay key-order stable.
- sp checks import now sets Content-Type from the file extension
  (reusing detectContentType) and prints a created/updated/skipped
  summary with per-item [index] slug: error lines, exiting non-zero on
  any failure. client.SolidPingClient.ImportChecks gains a contentType
  parameter.
- New sp checks diff <file>: strips exportedAt from both sides,
  normalizes to sorted-key JSON, and prints a go-difflib unified diff.
  Exit 0/1/>=2 (no drift / drift / errors).

Covered by checks_yaml_test.go (order preservation, determinism,
round-trip stability, format inference), checks_diff_test.go
(exportedAt stripping, drift detection, JSON/YAML equivalence), and
apply_import_test.go (summary/exit-code contract).
Adds a Config as Code section to web/docs/docs/cli.md: the export ->
edit -> validate -> import --dry-run -> import -> re-export loop, the
import-is-an-upsert-never-deletes caveat (with sp apply --prune as the
delete-by-absence alternative), and the export --format behavior.
make build-backend lint-back test and make build-docs all green.
Renamed a test-fixture label key (env -> environment) to clear a
goconst threshold that the new test data pushed a pre-existing
occurrence count over.
Gap 1 (real bug): checker.Validate mutates spec.Config in place for
heartbeat/email checks (auto-generates a "token" when absent) - correct
for the live create/update path, but ValidateDocument was passing the
check's own Config map by reference into that call, so its own
no-inlined-credentials heuristic then flagged the token it had just
caused to be generated. The reference workflow's real config.yaml
failed validation as a result. Fix: deep-copy each check's Config
(JSON round trip) before handing it to checker.Validate; credential/
status-field checks still run on the original, untouched map. Added
TestValidateDocumentDoesNotMutateInput (heartbeat + email) as a
regression guard, and confirmed `sp checks validate
~/code/stonal/exp-devops/solidping/config.yaml` now exits 0.

Gap 2: factored checksDiffAction's drift/exit-code decision into pure
computeDiffOutcome + diffExitCode (mirroring formatImportSummary for
import), and added table tests for no-drift (0), drift (1), and error
(>=2) - the spec's "diff exit codes with and without drift" test area
was previously only exercised indirectly via normalizeForDiff.

Gap 3: TestJSONToYAMLRoundTripStable was calling jsonToYAML twice on
the same JSON string (a duplicate of the determinism test, not a round
trip). Replaced with a real round trip: parse the produced YAML back
with yaml.Unmarshal into a yaml.Node and re-render with the same
encoder settings, asserting byte-identical output. Key-order-matches-
wire-order is now its own small test.

Gap 4: added TestIsExportDocumentShape, table-testing the predicate
that routes `sp checks validate` between the offline whole-document
path and the existing online single-check path - including the
adversarial case of a single check whose own config has a nested
"checks" key.
make build-backend lint-back test green after closing the four audit
gaps (ValidateDocument purity bug, diff exit-code test coverage,
genuine YAML round-trip test, isExportDocumentShape routing test).
sp checks validate against the real reference config.yaml now exits 0.
… hardcode localhost:4000) silently talk to the wrong server
kubernetes-clusters.spec.ts and discovery-scan-method.spec.ts derived
their API base from E2E_API_BASE, an env var nothing sets, so they
always hit localhost:4000 regardless of E2E_BASE_URL - silently
targeting the wrong server/database when a side-car test server runs
on another port. Delete the dead E2E_API_BASE fallback entirely and
import the correct API_BASE from fixtures.ts, like the rest of the
suite.

Also consolidate every other file that re-derived the same origin
(webpush, discovery, global-setup, and the "page-URL variant" files
which turn out to compute the same API origin, not a distinct page
URL) onto the single fixtures.ts export, plus two files found by a
broader grep sweep (login.spec.ts, account-notifications-whatsapp)
that had the same literal localhost:4000 fallback pattern.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fclairamb and others added 22 commits August 7, 2026 20:32
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Worker identity was derived entirely from os.Hostname(), lowercased and cut
to 15 characters. On Kubernetes with hostNetwork: true the pod shares the
host UTS namespace, so os.Hostname() returns the (dotted) node name and the
workers.slug CHECK constraint rejects it — the worker never registers.
Truncation also silently collapses two workers onto one upsert-by-slug row.

SP_NODE_NAME now sets slug and name verbatim, skipping the hostname read
entirely. The effective slug is validated at startup against the same
^[a-z][a-z0-9-]{2,20}$ pattern the database enforces, and a truncated
hostname logs a WARN naming the resulting slug. Both the check worker and
the job worker share config.Config.WorkerIdentity().
…os.Hostname(), which blocks IPv6 egress on Kubernetes
Covers the end of the chain the config tests reach from the other side: the
check worker's workers row (slug + name) and the job worker's self-stats slug
both come from the shared config.Config.WorkerIdentity resolution.
…os.Hostname(), which blocks IPv6 egress on Kubernetes
…s.Hostname(), which blocks IPv6 egress on Kubernetes
…DE_NAME, showcase media and agent key redaction
@fclairamb fclairamb changed the title Batch 2026-08-05: agent key redaction, config-as-code CLI, SP_NODE_NAME, showcase media, E2E base-URL fixes feat: config-as-code CLI, SP_NODE_NAME worker identity, docs showcase media, and agent key redaction Aug 7, 2026
@fclairamb
fclairamb enabled auto-merge (squash) August 7, 2026 21:00
@fclairamb
fclairamb merged commit ec971d6 into main Aug 7, 2026
11 checks passed
@fclairamb
fclairamb deleted the batch/2026-08-05-2 branch August 7, 2026 21:21
@fclairamb fclairamb mentioned this pull request Aug 7, 2026
fclairamb added a commit that referenced this pull request Aug 7, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.9.0](v0.8.0...v0.9.0)
(2026-08-07)


### Features

* config-as-code CLI, SP_NODE_NAME worker identity, docs showcase media,
and agent key redaction
([#198](#198))
([ec971d6](ec971d6))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
This was referenced Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant