Task · OPN-0002

main is red: WAN address reintroduced into public fixtures, check-public-ips failing since 2026-08-08

Description

Found during the GitHub → backlog.md tracker migration on 2026-08-14, not migrated from an issue. main is red and has been for a week, and the reason is the gate that exists to keep Rob’s own network addresses out of a public repository.

This task deliberately names no literal. backlog/ is committed and is scanned by make check-public-ips like any other content, so quoting the offending addresses here would add violations rather than describe them. Run the gate, or open the lines below.

State

make check-public-ips fails on clean main with 13 un-allowlisted globally routable literals, all in internal/logship/:

file:line what is there
internal/logship/syslog/dpinger.go:55 a sample dpinger config line carrying both a public resolver address and the box’s own WAN bind address
internal/logship/syslog/dpinger_test.go:138,167,171,172,222 the same pair, in the fixture strings and in the comment that justifies them
internal/logship/unbound_test.go:414 one v6 literal in the same ISP’s space, counted three times because the table row repeats it in three columns

Every CI run on main from 2026-08-08 onward fails on the Tests & Linters job at make check-public-ips, which fails ci-success. (The failures on 2026-08-06 have a different cause — the generated-docs job — so do not assume one fix turns everything green.)

Why this is not just a red build

The v4 literal is the box’s own WAN bind address, and the comment at dpinger_test.go:138 says so in as many words while arguing it is fine to commit: the lines carry no WAN address, it says, and the values are just the gateway monitor’s configured dest_addr/bind_addr, and the identifier is a gateway name rather than a secret.

That reasoning is the exact thing #565 rejected, and it is wrong on its own terms — bind_addr is the WAN address. The concern was never secrecy. It is that a globally routable literal committed to a public repository is durable public metadata about whoever’s box produced it.

This is the third reintroduction, which is the actual finding

4a62a580  2026-07-30  security: replace live firewall addresses with documentation ranges, and gate it
a02e9eec  2026-08-01  feat(logship): parse ppp, firewall aliases, acme and unbound's dnsbl chatter   <- back
a493795f  2026-08-01  test(syslog): use RFC 5737 documentation addresses in the ppp fixtures         <- scrubbed
8cca5da9  2026-08-07  feat(logship): structure dpinger lifecycle...                                  <- back
3726ca97  2026-08-07  fix(logs): stop the unbound lane's client field being a mixed type             <- the v6 one

The gate works — it caught all three. What failed twice is the response: on 2026-08-01 it was fixed the same day; on 2026-08-07 it was left red for a week.

The mechanism is consistent: live-box captures get pasted into fixtures verbatim, because a real capture is the honest source for a parser test. Each time, a comment is written explaining why these particular values are acceptable. Any fix that does not address that habit will see a fourth reintroduction.

Do not just add allowlist entries

The public-resolver address is benign and belongs in scripts/public-ip-allowlist.json with a justification, alongside the resolver entries already there.

The WAN address and the v6 literal do not. Replace them with RFC 5737 / RFC 3849 documentation addresses, the way a493795f already did for the ppp fixtures. An allowlist entry for a real WAN address defeats the gate rather than satisfying it.

Replacing them means updating the fixture strings and the surrounding comments, which currently assert the literals are genuine unsanitised capture output. A fixture carrying a documentation address must say it was sanitised — the standing rule is that a fixture must never encode a shape upstream cannot produce, and a comment claiming provenance it no longer has is the same defect.

Acceptance Criteria

Definition of Done

Implementation Notes

Deviation from the original AC, and why

The task was filed saying the public-resolver address (the dest_addr in the dpinger startup line) should get a justified allowlist entry, and only the WAN bind address and the v6 literal should be replaced. That AC was replaced with the stronger one.

Reason: dpinger_test.go already uses RFC 5737 addresses in every other case in the file (198.51.100.42, 192.0.2.100), and network_diag_test.go already pairs the same AAISP interface name with 203.0.113.187. So the repo’s own convention is documentation addresses everywhere, and the lifecycle fixtures were the outlier rather than a case needing an exemption. Replacing all three means the allowlist does not grow at all — it is still 91 entries — and there is no exemption to audit later.

What the fixture actually pins is the field layout: two-space separators, the quoted identifier with its trailing space inside the quotes, the unit suffixes. None of that depends on which addresses appear, so substitution costs the fixture nothing.

Deliberately not touched

Both are the same defect class as the IP literals and neither is covered by make check-public-ips. Worth a decision, not worth folding into this fix.

Final Summary

Fixed in 5b23445a. All three literals replaced with RFC 5737 / RFC 3849 documentation addresses in internal/logship/syslog/dpinger.go, dpinger_test.go and internal/logship/unbound_test.go; the allowlist was not touched and still holds 91 entries. make check-public-ips: OK (1296 files scanned). ci-success is green on 5b23445a — the first green CI run on main since 2026-08-08. The comments that claimed the fixtures were unsanitised live captures now state the substitution and why bind_addr counts as a WAN address, which was the reasoning error behind all three reintroductions. AAISP as an interface/gateway name and the rob-knight.net hostname fixtures are deliberately untouched and are recorded in the notes as a separate decision.

View the source file on GitHub