HarperFast / HarperFast/harper-pro
remove_node is not honored by the inbound replication auth gate: a removed node re-authorizes on reconnect and keeps receiving data
- Dominant language
- JavaScript
- Stars
- 3
- Forks
- 0
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 80
Description
`remove_node` revokes a peer locally, but the removed node re-authorizes itself on its own
reconnect and resumes receiving replicated data. The inbound connection auth gate cannot tell a
genuine `hdb_nodes` tombstone from a transient decode miss, so it falls back to trusting the
certificate-validated name.
## Mechanism (harper-pro, `origin/main` as of 2026-09-01)
`resolveNodeForAuth` (`replication/knownNodes.ts:301-330`) — reached from `readNodeForAuth` at
`replication/replicator.ts:215` and `:267`, the cert/hostname gate for an inbound replication
connection — does a point lookup for the node row, and when that lookup is invalid it falls back to
`storeRecordRangeVisible(store, name)` and returns `{ name }`, i.e. it trusts the cert-validated
name. There is no distinction between "row genuinely deleted by `remove_node`" and "row present but
transiently undecodable".
The codebase already has the primitive that makes that distinction: `probeNodeRow`
(`replication/knownNodes.ts:504`, added in `decbca38`, 2026-07-21) uses `isGenuineNodeDeletion` /
a clean-null point read to separate a real tombstone from a decode race. It is wired only into the
**ongoing send-authorization** watch (`resolveNodeForSendAuth`, and `replicationConnection.ts:584`),
not into the **initial-connection** auth path that gates a removed peer's reconnect. So this bug
class was closed for one path and left open for its sibling.
This is not an unauthenticated bypass — the peer holds a valid certificate from when it was enrolled.
It is an operator revocation the data plane does not honor: after `remove_node`, replicated data
keeps flowing to a node that was decommissioned.
## Why it isn't already covered
The `'remove_node_back;'` operation-name typo (fixed in `1169e8848`, shipped via #615) masked this
repro: with the reciprocal removal working, the removed node's own `hdb_nodes` record is deleted, so
the common repro stops reproducing. The auth-gate hole is untouched. A fixer who lands only the
membership fix will believe they are done.
#615's own body says as much: "[the self-initiated resubscribe] still succeeds today… That is a
separate, currently-live bug." PR #714 attempted a related `probeNodeRow`/posture-cache rework but
was **closed unmerged** (2026-08-17) and targeted the subscription/outbound path, not
`resolveNodeForAuth`.
## Repro
`qa764-resubscribe-auth.test.mjs` — 3/3 on 4 runs (harper-pro `86f2955e`). The
`removeNodeBlastRadius.test.mjs` suite added by #615 pins local revocation durability but does not
cover the removed peer's self-initiated re-auth.
## Fix shape
Route `resolveNodeForAuth`'s invalid-point-lookup fallback through `probeNodeRow` /
`isGenuineNodeDeletion` the way `resolveNodeForSendAuth` already does: a genuine deletion must fail
the auth, a decode race may still fall back to the cert-validated name.
Contributor guide
Research direction
Start with resolveNodeForAuth in replication/knownNodes.ts and its callers readNodeForAuth in replication/replicator.ts. Compare its invalid-lookup handling with probeNodeRow, isGenuineNodeDeletion, and resolveNodeForSendAuth, then run qa764-resubscribe-auth.test.mjs. Done means a genuine hdb_nodes deletion rejects a reconnect while a transient decode race still permits the certificate-validated name.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs
- Domain
- authentication, backend, distributed-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100