Skip to content

ops: bundled Mosquitto opt-in, normalised DISABLE_MOSQUITTO, pprof loopback default (#441) - #444

Merged
dborup merged 2 commits into
masterfrom
codex/issue-441-broker-optin-pprof-loopback
Oct 14, 2026
Merged

dborup merged 2 commits into
masterfrom
codex/issue-441-broker-optin-pprof-loopback

Conversation

@dborup-agent

Copy link
Copy Markdown
Collaborator

Relates to #441

Implements the owner decisions recorded in #441 on the "Left for the owner" items of #431 (#372).

Plan

  1. Bundled Mosquitto opt-in. Flip every default to DISABLE_MOSQUITTO=true: docker-compose.yml, docker-compose.example.yml, the image entrypoint (so a bare docker run as well), manage.sh (dc_prod/dc_staging, the port preflight and the setup wizard, whose prompt now defaults to "no"), and .env.example. The no-mosquitto and staging files already render true.
  2. One parser, three readers. docker/entrypoint-go.sh, manage.sh (parse_disable_mosquitto) and the ingestor (ResolveBrokerMode) read the value with the same table: trimmed, case-insensitive, true/1/yes/on → broker off, false/0/no/off → broker on, unset/empty → the default (off), anything else → the default plus a warning. The table is a shared fixture (test-fixtures/disable-mosquitto-cases.json) that both the JS and the Go tests load, so the three implementations cannot drift apart silently. The launcher-mismatch diagnostics from ops(runtime): make bundled MQTT and pprof exposure explicit settings (#372) #431 are gone on both sides; broker-check reports the effective mode instead (Broker mode: external (DISABLE_MOSQUITTO=yes, read as true)).
  3. pprof on loopback by default. internal/pprofbind defaults the server (6060) and ingestor (6061) listeners to 127.0.0.1; PPROF_HOST=0.0.0.0 (or *) widens them. The staging compose files publish the ports on ${PPROF_BIND:-127.0.0.1} and pin the in-container listener to PPROF_HOST=0.0.0.0, because docker's port proxy cannot reach a loopback listener inside the container. Without that pin, PPROF_BIND=0.0.0.0 would publish a dead port. Ports stay distinct. The SSH tunnel is documented.
  4. MQTT_SOURCE_STRICT stays off by default. No code change; the existing guard tests plus two mutants pin it.

Docs: a new "Upgrading past #441" section in docs/deployment.md (the release note below, at more length). The #372 migration table, the env-var references in docs/deployment.md and DEPLOY.md, the MQTT setup and troubleshooting sections, and the README docker run example (now -e DISABLE_MOSQUITTO=false) are updated to match.

Design choice to review: PPROF_BIND vs PPROF_HOST

#441 says "the existing PPROF_BIND setting restores remote access". PPROF_BIND is the compose-level publish interface, and #431 deliberately kept it separate from the in-process PPROF_HOST. I kept that separation, so:

  • On the staging compose files, the ones that publish pprof, PPROF_BIND=0.0.0.0 alone restores remote access, as ops: make the bundled Mosquitto opt-in, normalise DISABLE_MOSQUITTO, pprof loopback by default (#431 owner decisions) #441 asks.
  • For a process whose pprof port is reached without one of those files (a bare binary, a docker run -p …:6060:6060, or another container on the network), the restore knob is PPROF_HOST=0.0.0.0. Making pprofbind also read PPROF_BIND would re-conflate the two layers: a PPROF_BIND=127.0.0.1 that reached a container would bind the container's loopback and silently kill the published port.

Release note

Bundled MQTT broker is now opt-in; pprof listens on loopback

  • DISABLE_MOSQUITTO now defaults to true everywhere: the compose files, the image entrypoint (a bare docker run as well), manage.sh and its setup wizard, and .env.example. An install that relies on the bundled Mosquitto and never set the variable will start without a broker and ingest nothing. The ingestor logs a [broker] error: line, and ./manage.sh broker-check fails. To keep the bundled broker, set DISABLE_MOSQUITTO=false (in .env, or -e DISABLE_MOSQUITTO=false). Installs that already set true or false are unaffected, as are .env files copied from an older .env.example, which carried an explicit false.
  • DISABLE_MOSQUITTO is parsed identically by the entrypoint, manage.sh and the ingestor: true/1/yes/on and false/0/no/off, trimmed, in any case. Unset or empty means true. Any other value (including y/n) also means true and is reported. Before, a bare docker compose up honoured only the exact true, so TRUE/yes/1/on left the broker running there; they now turn it off, as they already did under manage.sh.
  • pprof binds 127.0.0.1 when enabled. With ENABLE_PPROF=true, the server (6060) and ingestor (6061) listen on loopback, and docker-compose.staging*.yml publish those ports on 127.0.0.1 only. Use an SSH tunnel (ssh -L 6060:127.0.0.1:6060 -L 6061:127.0.0.1:6061 you@host). To keep remote access, set PPROF_BIND=0.0.0.0 on the staging compose files (IPv4). For a bare binary or a container that maps the pprof ports itself, set PPROF_HOST=0.0.0.0.
  • MQTT_SOURCE_STRICT is unchanged and stays off.
  • A non-container ingestor started with DISABLE_MOSQUITTO unset now assumes an external broker, so a loopback-only mqttSources logs a [broker] error:. It still starts and still connects; set DISABLE_MOSQUITTO=false to silence it.

Deployments with DISABLE_MOSQUITTO=true: identical before and after (local Docker)

Images built from origin/master (bcf263e1) and from this branch. Each compose file was run with DISABLE_MOSQUITTO=true in .env and a stand-in external broker on a local meshcore-net. Compared: rendered docker compose config, docker port, supervisord config and processes, in-container listening sockets, container environment, entrypoint and ingestor log lines (MQTT connect/subscribe), /api/healthz and /api/mqtt/status.

Compose file Result
docker-compose.yml (bare compose) Identical. Only noise differs: the embedded DNS resolver's random port and the status sample age
docker-compose.no-mosquitto.yml (what manage.sh runs for true) Identical, same noise
docker-compose.staging.yml Broker side identical. pprof differs by design (item 3): the host publish goes from 0.0.0.0+[::] to 127.0.0.1 on 6060/6061, and the container env gains PPROF_HOST=0.0.0.0. The in-container listener is unchanged (:::6060/:::6061)

Heads-up for the owner: the staging compose files hard-code ENABLE_PPROF=true. A staging host profiled remotely needs PPROF_BIND=0.0.0.0 in its .env before it pulls this, or it will need an SSH tunnel. Nothing was changed on any deployment, and no staging or production host was accessed.

Tests

Every item has a test that is red on origin/master and green here, plus mutants. The full table is in the report comment. In short:

  • test-issue-441-disable-mosquitto.js (new, in test-all.sh): drives the shipped entrypoint (absolute paths redirected into a temp dir, stub supervisord) and manage.sh (stub docker) over every row of the shared table, via .env and via the process environment. It checks the selected supervisord config, broker-check's mode, the compose file dc_prod runs, the junk warning, the absence of any mismatch warning, the wizard default and .env.example.
  • cmd/ingestor/broker_mode_test.go: the same table for ResolveBrokerMode, plus the junk diagnostic and the absence of mismatch diagnostics.
  • cmd/server/compose_optin_441_test.go (new): the opt-in default in every app compose file, the staging pprof publish on loopback, the in-container wildcard where pprof is published, no widening where it is not, and the docs upgrade note.
  • internal/pprofbind: the loopback default, the wildcard override (shared and per role), distinct ports, and the URL.
  • Updated expectations in the ops(runtime): harden bundled MQTT and opt-in pprof defaults #372 tests (test-issue-372-broker-check.js, compose_bind_372_test.go, pprofbind_test.go) and in the two non-root entrypoint harnesses, which now ask for the bundled broker explicitly where they need it.

Checks: no cmd/server runtime change (only a test file), so it stays read-only. No new map[string]interface{}, no public/** change (so no colours, and check-xss-sinks.sh --diff origin/master has nothing to scan). Fork guards unchanged: 9 in deploy.yml, 1 in release-fast-path.yml.

🤖 Generated with Claude Code

dborup and others added 2 commits October 14, 2026 06:07
… pprof

Red on origin/master:

- test-fixtures/disable-mosquitto-cases.json: the one DISABLE_MOSQUITTO
  table (unset/empty, true/1/yes/on, false/0/no/off in any case and with
  surrounding whitespace, junk) that every reader is held to.
- test-issue-441-disable-mosquitto.js: drives the shipped
  docker/entrypoint-go.sh and manage.sh over that table (selected
  supervisord config, broker-check mode, the compose file dc_prod runs,
  junk warning, no launcher-mismatch warning), plus the wizard default and
  .env.example.
- cmd/ingestor/broker_mode_test.go: ResolveBrokerMode over the same table;
  junk is a warning that names the default; recognised values are quiet.
- cmd/server/compose_optin_441_test.go: every app compose file renders
  DISABLE_MOSQUITTO=true when unset; the staging files publish pprof on
  ${PPROF_BIND:-127.0.0.1} with the in-container listener pinned to
  0.0.0.0; files that publish nothing do not widen it; docs upgrade note.
- internal/pprofbind: loopback default, wildcard override (shared and per
  role), URL names localhost for an unspecified host.
- The #372 tests and the non-root entrypoint harnesses move to the new
  defaults.

Relates to #441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… loopback (#441)

Owner decisions on the #431 "Left for the owner" items:

1. DISABLE_MOSQUITTO defaults to true in docker-compose.yml,
   docker-compose.example.yml, the image entrypoint, manage.sh (and its
   setup wizard, which now defaults to "no") and .env.example.
2. The entrypoint, manage.sh (parse_disable_mosquitto) and the ingestor
   (ResolveBrokerMode) read it the same way: trimmed, case-insensitive,
   true/1/yes/on and false/0/no/off; unset/empty/anything else is the
   default, and junk is reported. The launcher-mismatch diagnostics are
   removed; broker-check prints the effective mode.
3. pprofbind defaults the server and ingestor listeners to 127.0.0.1
   (PPROF_HOST=0.0.0.0 or * widens them). The staging compose files
   publish 6060/6061 on ${PPROF_BIND:-127.0.0.1} and pin the in-container
   listener to 0.0.0.0, since docker's proxy cannot reach a loopback
   listener; PPROF_BIND=0.0.0.0 restores remote access. Ports stay
   distinct.
4. MQTT_SOURCE_STRICT unchanged (off).

docs/deployment.md gains an "Upgrading past #441" note with the release
text and the SSH tunnel; DEPLOY.md, README and .env.example follow.

Relates to #441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Rapport — CS-pve-agent1 PR#444 #441 — head 32e2202

Status: Implemented; all four #441 decisions have red-before/green-after tests, 27/27 mutants killed, DISABLE_MOSQUITTO=true deployments verified identical in local Docker, CI green on every job on the first run; draft, nothing merged.

Evidence tags: [T] = I ran it and saw the result. [A] = analysis of code or docs, not executed. [K] = known from CI or GitHub metadata.

Branch codex/issue-441-broker-optin-pprof-loopback from origin/master bcf263e1. Two commits, a red test commit (5d3cde4f) and then the implementation (32e22025), both authored and committed by dborup <kontakt@meshview.dk> [T]. No rebase, amend or force-push. The PR is a draft.

Requirements → test → mutant

"Red before" means the test fails with this branch's tests and origin/master's runtime files, which I restored into a scratch copy of the tree [T]. Every mutant was applied to the head tree, its test run, and the file restored [T].

# Requirement (#441) Test (red before → green after) Mutants (all red)
1 Bundled Mosquitto opt-in: every compose/script default is DISABLE_MOSQUITTO=true TestComposeFiles_BundledBrokerIsOptIn (all 5 app compose files; red on docker-compose.yml and .example.yml). test-issue-441-disable-mosquitto.js unset/empty rows: entrypoint picks supervisord-no-mosquitto.conf; broker-check says external; dc_prod runs docker-compose.no-mosquitto.yml; "unset with no source fails the check"; wizard defaults to No; .env.example ships true. TestResolveBrokerMode_FollowsTheSharedTable (unset rows). test-nonroot-entrypoint.sh now expects the no-mosquitto config for an unset value in the real Alpine base (CI job) M1a/M1b compose default back to false; M1c entrypoint '' → bundled; M1d manage.sh "" → bundled; M1e ingestor "" → bundled; M1f wizard back to default-yes; M1g .env.example back to false
2 One parser: trimmed, case-insensitive true/1/yes/on vs false/0/no/off, unset/empty and junk tested; no mismatch report; effective mode reported One table, test-fixtures/disable-mosquitto-cases.json (36 rows: unset, empty, whitespace, 12 true spellings, 12 false spellings, 9 junk), driven through the shipped entrypoint, through manage.sh via .env and via the process environment, and through ResolveBrokerMode. JS: 11/11 red before. Go: does not compile against master's broker_mode.go. TestBrokerMode_JunkValueIsReportedAndFallsBackToTheDefault, TestBrokerMode_RecognisedValuesReportNoMismatch; JS "no launcher-mismatch warning is left; junk is reported once", "broker-check names the effective value it acted on" (Broker mode: external (DISABLE_MOSQUITTO=YES, read as true)) M2a/M2b entrypoint without trim / case-folding; M2c manage.sh drops on; M2d manage.sh junk silent; M2e/M2f ingestor case-sensitive / untrimmed; M2g ingestor junk → bundled; M2h ingestor junk not reported; M2i the #431 mismatch warning re-added for yes/1/on
3 pprof on loopback by default when enabled; PPROF_BIND restores remote access; distinct ports; SSH tunnel documented TestDefaultsBindLoopbackOnly, TestEmptyEnvironmentIsInert, TestWildcardHostRestoresEveryInterface (shared and per role, other role stays on loopback), TestURLNamesLocalhostWhenBoundEverywhere, TestStagingComposes_PprofPublishDefaultsToLoopback, TestStagingComposes_InContainerPprofListenerStaysReachable, TestEnvExample_DocumentsEveryNewKnob (PPROF_BIND/PPROF_HOST default 127.0.0.1), TestDeploymentDocs_CarryThe441UpgradeNote (DISABLE_MOSQUITTO=false, PPROF_BIND=0.0.0.0, PPROF_HOST=0.0.0.0, ssh -L). Guards, green on master by design: TestServerAndIngestorDefaultPortsStayDistinct, TestNonPublishingComposes_LeavePprofOnLoopback M3a default host back to every interface; M3b staging publish back to ${PPROF_BIND:+…}; M3c staging drops PPROF_HOST=0.0.0.0 (dead published port); M3d prod compose widens the unpublished listener; M3e ingestor port 6061 → 6060; M3f per-role host ignored; M3g URL prints 0.0.0.0; M3h docs drop ssh -L; M3i .env.example default undocumented
4 MQTT_SOURCE_STRICT stays off; no change Existing TestStrictModeIsOptIn, TestEnvExample_DocumentsEveryNewKnob (Default: false), new TestEmptyBrokerEnvironmentIsTheExternalDefault (empty → no refusal). Green before and after by design, since nothing changes M4a strict on when empty; M4b .env.example ships true
— Release note In the PR description, and at more length in docs/deployment.md, "Upgrading past #441" (M3h, see row 3)

27 of 27 mutants were killed and none survived [T]. #372 tests whose expectations moved with the owner decisions: 5 JS cases and TestStagingComposes_PprofHostPortsBindExplicitlyAndStayDistinct, TestComposeBindDefaultsStayDualStack (exempts only PPROF_BIND=127.0.0.1), TestEnvExample_DocumentsEveryNewKnob, plus the port and default-host assertions in pprofbind_test.go.

DISABLE_MOSQUITTO=true deployments: identical before and after (local Docker) [T]

Images were built from origin/master (bcf263e1) and from head 32e22025. Each compose file ran with DISABLE_MOSQUITTO=true in .env, a config.json pointing at a stand-in external broker (eclipse-mosquitto:2 on a locally created meshcore-net), and the container attached to that network. Compared: rendered docker compose config, docker port, supervisord config and process list, in-container listening sockets, container environment, entrypoint and ingestor log lines (MQTT connect and subscribe), /api/healthz, and /api/mqtt/status.

Compose file Master vs head
docker-compose.yml (bare compose) Identical. Only noise differs: the embedded DNS resolver's random port and the status sample age. Same supervisord-no-mosquitto.conf, same [config] internal MQTT broker disabled (DISABLE_MOSQUITTO=true) line, the ingestor connects and subscribes to the external broker, healthz 200
docker-compose.no-mosquitto.yml (what manage.sh runs for true) Identical, same noise only
docker-compose.staging.yml Broker side identical. pprof differs by design (item 3): host publish 0.0.0.0+[::] → 127.0.0.1 on 6060/6061, and the container env gains PPROF_HOST=0.0.0.0. The in-container listener is unchanged (:::6060, :::6061)

The intended changes, observed in the real images [T]

DISABLE_MOSQUITTO in the busybox entrypoint (DISABLE_CADDY=true, mosquitto process present or not):

Value master head
unset, '' runs off
false, False, ' off ', 0, no runs runs
true off off
TRUE, YES, 1, on runs (the old gap) off
ture, y runs off + "not a recognised value" line

pprof:

Setup Result
Staging compose (head), PPROF_BIND unset docker port 127.0.0.1:6060/6061; 200 on loopback, refused via the host's external address
Staging compose (head), PPROF_BIND=0.0.0.0 0.0.0.0:6060/6061; 200 via the external address
Container with no published port, ENABLE_PPROF=true master :::6060 (reachable from another container); head 127.0.0.1:6060, 200 inside, refused from another container; with PPROF_HOST=0.0.0.0 back to :::6060, reachable
Bare corescope-server, ENABLE_PPROF=true 127.0.0.1:6060; external address refused. With PPROF_HOST=0.0.0.0: *:6060, 200 via the external address

Suites (local, head) [T]

  • cd cmd/server && go vet ./... && go test ./... → ok
  • cd cmd/ingestor && go vet ./... && go test ./... → ok
  • cd internal/pprofbind && go vet ./... && go test -race ./... → ok
  • sh test-all.sh → 246 passed, 0 failed (incl. the new test-issue-441-disable-mosquitto.js, 11/11, and test-issue-372-broker-check.js, 23/23)
  • node test-frontend-helpers.js → 713 passed, 0 failed
  • E2E: a local Go server on e2e-fixture.db prepared as in CI (freshen, inline seed SQL, corescope-migrate, seeds 2073/199/245), started with ENABLE_PPROF=true. test-e2e-playwright.js → 151/154 passed, 3 skipped, 0 failed. /api/healthz, /api/health, /api/stats, /api/mqtt/status, /api/nodes, /api/observers, /api/packets → 200. The server was stopped by the pid holding the port. No frontend file changed, so no other E2E is affected
  • bash scripts/check-xss-sinks.sh --diff origin/master → "no public/**/*.{js,html} changes to scan"
  • shellcheck on both changed non-root harnesses: clean. On docker/entrypoint-go.sh, only the existing SC1091 info. On manage.sh: 17 findings on master and 17 on head, none new
  • scripts/check-dockerfile-internal-pkgs.sh → ok; gofmt -l clean on every touched Go file

Always-checks [T]

  • cmd/server changes only a test file (compose_optin_441_test.go), so it stays read-only, and the invariant test passes in the suite
  • 0 new map[string]interface{} outside tests (git diff origin/master -- '*.go' ':!*_test.go')
  • No public/** change, so no colours
  • Fork guards unchanged: 9 in deploy.yml, 1 in release-fast-path.yml; no .github/ change
  • No closing keywords in title, body or commits; the body starts with Relates to #441

CI per job (run 38520401105, attempt 1) [K]

Job Result
✅ Go Build & Test pass (16m), including bash scripts/test-nonroot-entrypoint.sh in the real Alpine base (ok 260 - off after on, clean volume: shipped config, i.e. an unset value now selects the no-mosquitto config under busybox) and test-issue-441-disable-mosquitto.js 11/11
🎭 Playwright E2E Tests (1/3) pass (9m15s)
🎭 Playwright E2E Tests (2/3) pass (8m49s)
🎭 Playwright E2E Tests (3/3) pass (9m37s)
📊 Verify E2E Shards & Coverage pass
🧱 Validate Docker Image pass
🏗️ Build & Publish Docker Image (gate) pass
📦 Release Artifacts / 🚀 Deploy Staging / 📝 Publish Badges skipped (fork guards, as expected)

The known flake (#271) did not fire, so nothing was rerun.

Remaining / for the owner

@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Review — CS-MacBook PR#444 — head 32e2202

Dom: APPROVE with nits. One #441 item is implemented differently from the issue's wording and needs the owner's explicit sign-off (F1).

Evidence tags: [T] = I ran it and saw the result. [A] = analysis of code or docs, not executed. [K] = known from CI or GitHub metadata.

Independent, read-only review. I checked git ls-remote before and after: head stayed 32e22025 [T]. Base is origin/master bcf263e1, which is also the merge base, so the merged tree (git merge-tree --write-tree) is byte-identical to the head tree [T]. All work ran on git archive copies in scratch.

Findings

# Severity Where Finding
F1 Medium (owner decision) internal/pprofbind, docs/deployment.md "Upgrading past #441", the release note #441 item 3 says "the existing PPROF_BIND setting restores remote access". On this branch that holds only for the staging compose files. Anywhere else, the restore knob is PPROF_HOST=0.0.0.0, and PPROF_BIND=0.0.0.0 alone does nothing. I measured it [T]: head image with ENABLE_PPROF=true and -e PPROF_BIND=0.0.0.0 listens on 127.0.0.1:6060; another container gets 000 and a host port mapping gets 000. With PPROF_HOST=0.0.0.0 both get 200, as they do on master by default. The author discloses this as a design choice and documents both knobs. The reasoning holds up [A]: PPROF_BIND is a compose-level publish variable that never reaches the process, and the staging files pin PPROF_HOST=0.0.0.0. The owner still has to accept the split explicitly. Any pre-release checklist entry that reads "set PPROF_BIND=0.0.0.0" must be checked against the compose file each deployment actually runs
F2 Low docker/entrypoint-go.sh, manage.sh parse_disable_mosquitto vs cmd/ingestor/broker_mode.go The three parsers still differ on a value with a leading newline. sed trims line by line and $(…) strips only trailing newlines, so $'\nfalse' is junk to the shell (broker off). Go's strings.Trim removes the newline and reads false (bundled). The ingestor would then report "bundled" and skip the external-source check while no broker runs. I ran the shell logic under macOS sed/tr and a copy of the Go switch [T]; busybox sed is line-oriented too [A]. A value like this can only arrive through the process environment, so the risk is cosmetic. A row in test-fixtures/disable-mosquitto-cases.json plus tr -d '\n', or a comment, would close it
F3 Nit docs/deployment.md restore table, the release note The restore table lists "a bare binary, or a docker run/compose file that maps the pprof ports itself". It omits the case of another container on the same Docker network with no port mapping, which also changed from 200 to 000 [T]. .env.example covers it ("reached from outside it"), but the release note does not
F4 Nit docker-compose.no-mosquitto.yml:12-13 The header comment is stale. It still says "For a simple single-host self-hosted deploy that keeps the built-in broker, see docker-compose.example.yml", but that file now ships with the broker off [T, grep]

No blocking correctness, security or scope problems found.

#441, item by item

# #441 item Status Evidence
1 Bundled Mosquitto opt-in, every compose/script default true Done All five compose files, the entrypoint, manage.sh (dc_prod/dc_staging, preflight, wizard default now "no") and .env.example [A]. TestComposeFiles_BundledBrokerIsOptIn and the JS unset/empty rows are red on master runtime and green on head [T]. My mutant X1 was killed [T]
2 One parser, true/1/yes/on vs false/0/no/off, trimmed and case-insensitive; no mismatch report; effective mode reported Done (F2 edge) One shared 36-row table drives the shipped entrypoint, manage.sh (via .env and via the process environment) and ResolveBrokerMode. On master runtime: JS 0/11, Go does not compile (Unrecognised missing). On head: green [T]. Mismatch diagnostics are gone on both sides; broker-check prints Broker mode: external (DISABLE_MOSQUITTO=…, read as true) [A plus the tests]. My mutants X4, X5 and X6 were killed [T]
3 pprof on loopback when enabled; PPROF_BIND override; distinct ports; SSH tunnel documented Done, with F1 deviation pprofbind default 127.0.0.1, server 6060 and ingestor 6061 [T, local server log: listening on 127.0.0.1:…]. Staging publish defaults to 127.0.0.1 and PPROF_BIND=0.0.0.0 restores LAN access (table below) [T]. ssh -L is in docs/deployment.md, DEPLOY.md, .env.example and the compose comments [A]. My mutants X2, X3 and X7 were killed [T]
4 MQTT_SOURCE_STRICT stays off Unchanged StrictBrokerSources is byte-identical to master and .env.example still ships false [T, diff]. Only comments around it changed
— Tests per parsing value (incl. unset/empty/junk), compose default, pprof default and override Done See rows 1–3
— Release note: what changes, how to keep old behaviour Done (F1/F3 wording) The PR body and "Upgrading past #441" cover DISABLE_MOSQUITTO=false, PPROF_BIND=0.0.0.0 (staging) / PPROF_HOST=0.0.0.0 (elsewhere), the y/n reinterpretation, the non-container ingestor and that STRICT is unchanged [A]. It is accurate against what I measured [T]
— DISABLE_MOSQUITTO=true install identical before and after (local Docker) Verified See below [T]

Local Docker: DISABLE_MOSQUITTO=true before and after [T]

I built images from the master tree (bcf263e1) and the head tree. Both were built natively for arm64 with the same --build-arg BUILDPLATFORM=linux/arm64, because the Dockerfile's amd64 default crashed go mod download under emulation on this machine. CI's 🧱 Validate Docker Image covers the amd64 build [K].

Each compose file ran from its own tree with DISABLE_MOSQUITTO=true in .env, a config.json whose mqttSources pointed at a stand-in eclipse-mosquitto:2 on a locally created meshcore-net, and host ports moved to 139xx. Host-side pprof ports were remapped by the same sed in both trees. Compared: rendered docker compose config, docker port, the process list, in-container listening sockets, container env, entrypoint and ingestor log lines, /api/healthz and /api/mqtt/status.

Compose file Master vs head
docker-compose.yml Identical apart from noise: the embedded DNS resolver port, MQTT client ID and connect timestamp. Both log [config] internal MQTT broker disabled (DISABLE_MOSQUITTO=true), both connect and subscribe to the external broker, healthz 200
docker-compose.no-mosquitto.yml Identical, same noise only
docker-compose.staging.yml Broker side identical. The intended pprof difference only: PPROF_HOST "" → 0.0.0.0, and the publish gains host_ip: 127.0.0.1. The in-container sockets are the same (Go's 0.0.0.0 is the same dual-stack wildcard as :6060)

pprof reachability [T]. "LAN" means curl against the host's own LAN address:

Setup Published Loopback LAN Another container Host -p mapping
master staging compose 0.0.0.0 + [::] 200 200 — —
head staging compose, PPROF_BIND unset 127.0.0.1 200 refused — —
head staging compose, PPROF_BIND=0.0.0.0 0.0.0.0 200 200 — —
head staging compose, PPROF_BIND=127.0.0.1 127.0.0.1 200 refused — —
master image, ENABLE_PPROF=true — — — 200 200
head image, default — — — refused refused
head image, PPROF_BIND=0.0.0.0 — — — refused (F1) refused (F1)
head image, PPROF_HOST=0.0.0.0 — — — 200 200

Tests and mutants

On the merged tree (= head tree), against origin/master bcf263e1 [T]:

  • cd cmd/server && go test ./... → ok
  • cd cmd/ingestor && go test -race ./... → ok (430 s)
  • cd internal/pprofbind && go test -race -count=1 ./... → ok
  • sh test-all.sh → 246 passed, 0 failed, including test-issue-441-disable-mosquitto.js and test-issue-372-broker-check.js
  • node test-frontend-helpers.js → 713 passed, 0 failed

Red before: I ran head's test files against master runtime files [T]:

  • JS 441: 0/11
  • cmd/server: 6 compose/env/docs tests fail
  • pprofbind: 7 tests fail
  • cmd/ingestor: does not compile

E2E: a local Go server on e2e-fixture.db, prepared as in CI (freshen, the inline seed SQL from deploy.yml, corescope-migrate, seeds 2073, 199 and 245), ENABLE_PPROF=true, port 139xx [T].

  • test-e2e-playwright.js stops on its fail-fast at "Version info lives on Perf dashboard, not in navbar" (#navStats wait timeout). The same test fails identically against a master-built server on the same fixture, so it is local to this machine and not caused by this PR.
  • A scratch copy with fail-fast removed gives 150/154 passed, 3 skipped, 1 failed (the same one) on both master and head, with the identical failure set.
  • No public/** file changed. CI ran all three Playwright shards green on head [K].
  • The servers were stopped by the pid holding the port, not $!.

My own mutants: 7 of 7 killed [T]. Each was applied to a fresh copy of head and run against the relevant suite. All were distinct from the author's 27.

# Mutant Killed by
X1 entrypoint: junk value → MOSQUITTO_DISABLED=false JS 441 entrypoint table row "ture"
X2 pprofbind: PPROF_HOST=0.0.0.0 silently rewritten to loopback TestWildcardHostRestoresEveryInterface, TestPerRoleHostOverridesSharedHost + 2
X3 docker-compose.staging.no-mosquitto.yml drops PPROF_HOST=0.0.0.0 TestStagingComposes_InContainerPprofListenerStaysReachable + 1
X4 manage.sh parser: no trailing-whitespace trim JS 441 " false " / " true " via proc rows
X5 ingestor: off removed from the false set TestResolveBrokerMode_FollowsTheSharedTable + 1
X6 manage.sh mosquitto_disabled: junk counts as "enabled" (= true instead of != false) JS 441 broker-check / dc_prod rows for "ture"
X7 pprofbind: per-role PPROF_HOST_<ROLE> ignored 6 pprofbind tests

Always-checks

Not verified

  • An IPv6 PPROF_BIND value, and dual-stack publishing via PPROF_BIND (the docs say it is IPv4-only).
  • The manage.sh setup wizard interactively. Only the test harness's stubbed run.
  • scripts/test-docker-nonroot.sh (local-only). I did not run it. scripts/test-nonroot-entrypoint.sh in the real Alpine base ran in CI only [K].
  • The full DISABLE_MOSQUITTO value table inside the real busybox image. I covered it through the JS harness on the shipped entrypoint under macOS sh [T]; the author's report has the busybox table.
  • An end-to-end upgrade of an install that relies on the bundled broker with the variable unset. Covered only by the tests and the release-note text [A].
  • amd64 images (CI only) [K].
  • No staging or production host was accessed. I did not read or use the server API key.

@dborup
dborup marked this pull request as ready for review October 14, 2026 07:58
@dborup
dborup merged commit 38776c4 into master Oct 14, 2026
10 checks passed
dborup-agent pushed a commit that referenced this pull request Oct 14, 2026
A new func Test... in cmd/server no longer needs a hand-written manifest
entry. Manifest v2 pins only the slowest entries to shards 1..N-1
(go test -run); shard N is the catch-all and runs everything else with
go test -skip of the pinned names. The five #444 tests, and any later
ones, run there automatically.

The guard now checks that every active Test, Fuzz seed target and
Example is selected by exactly one shard, and the aggregator checks that
the receipts together ran each active entry once. Stale pins select
nothing and are only reported. Patterns over 96 KiB are refused
(Linux MAX_ARG_STRLEN is 128 KiB per argv string); with 2/9 pins they are
under 0.5 KiB.

scripts/rebalance-go-server-shards.js regenerates the manifest from CI
go test -json artifacts. The checked-in weights come from the GitHub
runners of run 38529224242 attempts 1 and 2 (not local arm64): 2 pins on
shard 1, 9 on shard 2, 2,483 entries in the catch-all, projected
319 / 325 / 299 test seconds. AGENTS.md documents when and how to
rebalance, and the go-test summary prints the per-shard test seconds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants