Keep real node identities and receiver positions out of the tree - #351
Conversation
This repository is public, and it carried the surveyed positions of live receiver sites, the ids and hostnames of live nodes, and one site's name. The API displaces every receiver coordinate it publishes; a copy of the true one in the tree hands that displacement straight back for the node it names. The test is the point. Coordinates and site names are held as SHA-256 digests so the file does not itself list what it forbids, and the walk covers every tracked path, failing on one it cannot read rather than passing over it. Submodule contents are out of reach and declared as such, so a sixth submodule fails until someone acknowledges it. Fixture geometry is translated as a piece rather than blanked: the arc and region assertions are worthless against a shape the builder would decline, so the shape has to survive even though the position is invented. Nothing here changes behaviour, and the published API example moves from a real node id to a synthetic one rather than to the ref that will replace it, which is a later change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Claude finished @Babissimo's task in 7m 6s —— View job Claude finished reviewing PR #351
SummaryThe mechanics of this PR are solid: Finding: real node identity still leaks in files this PR didn't touchThe PR's stated goal is to get real ids/hostnames/site names out of the tree, but a few pre-existing spots outside the 30 changed files still name the real node, and the new guard's regex doesn't catch them:
These slip past Secondary observation: unsalted SHA-256 for coordinate/name digests
A salt wouldn't fully fix this since the salt would have to live in this same public repo to let CI compute digests, so it's visible to the same attacker. This isn't a regression this PR introduces — the ban is still much better than plaintext — but it's worth knowing the digests are a speed bump against casual grep, not a cryptographic guarantee, in case that gap matters for the later PRs in this series (e.g. once real ids are being actively renamed away). Everything else checked out
|
Resolves 21 conflicting files against 53 commits on main, including the two pieces already split out of this PR (#351 identity scrub, #355 receiver-geometry withholding), #358 site markers / empirical coverage, #348 location privacy, #350/#353/#354 owner contact and retnode links, and the #343/#346/#347/#357 palette work. Rules applied: main's version is the base for the add/add files (public_geometry.py, test_no_real_identities.py); the PR's ref-keying is layered on top of main's rework everywhere else; substitution still runs last on every publication path and fails closed for unregistered real nodes. Follow-on edits outside the conflicted set, needed to compile or to keep main's tests honest under ref addressing: frontend nodeSites.ts (+test) now reads is_synthetic; backend test_node_ref.py and test_public_geometry.py address routes by ref / synthetic id; analytics.py's owner-private listing merge keys on the owner identity. Verified: backend 3495 passed / 2 skipped, ruff clean; frontend tsc, lint, vite build, vitest 232 passed; dashboard tsc, eslint, vitest 53 passed, build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eploy `deploy-staging` was the only job in the `staging-deploy` concurrency group. `staging-smoke-tests` and `e2e-staging` carried no concurrency at all, so the deploy was serialised while everything that verifies the deploy was not: a following run's deploy was free to land between one run's smoke tests and its E2E, restarting the box underneath a suite that was testing it. Three consecutive merges failed that way on 2026-09-10 (#348, #351, #352), and four runs within three minutes produced the same thing on 2026-08-21 (#223). None of them failed for a fault in the change. The failure does not present as a flake either: a test hitting a half-swapped application gets a specific, plausible assertion failure, which reads as a content regression and costs someone real time before they conclude otherwise. It also blocks releases, as a red staging E2E skips `deploy-production`. Adding the group to the two verification jobs does not close it. Concurrency is acquired and released per job, so a deploy can still slot in between one run's smoke tests and its E2E. The environment has to stay held across the sequence. So the three jobs move to `staging-deploy-verify.yml` and ci.yml calls it from a single `Staging` job that carries the group. A calling job is not complete until every job in the called workflow has finished, so the group is held from the first byte of the deploy to the last E2E assertion. The step bodies move unchanged; only the job headers differ. Two things the move forced: - `env` does not cross a `workflow_call` boundary and `with:` cannot read the `env` context, so APP_DIR is re-exported as a `changes` output rather than restated in the new file, keeping one definition of the deploy directory. - `playwright-image` joins the staging job's prerequisites. An unpublished image now fails the run before the droplet is touched rather than after a deploy and a smoke suite have landed on it. Two comments that named the old job IDs move with them: the DNS-tolerance notes in frontend/e2e/dashboard.spec.ts and deploy/staging-smoke-test.sh both explained themselves by their job being a direct `needs:` of deploy-production, which is now indirect through the calling job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
retina-serveris public, and it carried the surveyed positions of live receiver sites, the ids and hostnames of live nodes, and one site's name.services/public_location.pydisplaces every receiver coordinate the API publishes, so a copy of the true one in the tree hands that displacement back for the node it names. Two files were already enough to do it:pipeline/passive_radar.pypaired a real position with a node id, androutes/output.py's worked example named a production node.First of five, split out of #349. It changes no behaviour, so it can land on its own.
The test is the deliverable
backend/tests/test_no_real_identities.pyfails if any of it returns. It found 111 occurrences across 26 files when first run against main, including two that #350 added last week.Three things about how it is built:
UNSCANNED_SUBMODULES, with an assertion that the set has not changed, so a sixth submodule fails the build until someone acknowledges it.libs/retina-geolocatorandlibs/retina-simulationstill carry the same coordinate and are tracked separately as 123zgec25ag.What moved
Real ids, hostnames and site names out of production code, tests, docs and the dashboard's example copy. Fixture geometry is translated as a piece rather than blanked: the arc and region assertions are worthless against a shape the builder would decline, so the shape survives even though the position is invented.
routes/output.py's example moves to a synthetic node id rather than to the ref that eventually replaces it, since the rename is a later PR in this series.One allow-list addition rather than a change:
ret0a1b2c3dindashboard/src/test/RetnodeLink.test.tsxis a synthetic fixture #350 added, and the guard's list simply did not know it.Verification
Backend 3210 passed, 2 skipped. Frontend 190. Dashboard tests and typecheck clean.
pre-commit run --all-filesgreen.🤖 Generated with Claude Code