Test multi-homing across two relays, not just against itself (relay#36) #107

Merged
paulhitt merged 1 commits from test/multi-homing-across-relays into main 2026-09-04 20:41:32 +00:00
paulhitt commented 2026-09-04 17:06:02 +00:00 (Migrated from gitlab.com)

relay#36 would spread load using the multi-homing machinery, and the issue is
right that it is premature — "only worth it well past the point PgBouncer stops
helping", and nothing measured is within sight of that. What was worth doing
was checking the foundation, because multi-homing is a property BETWEEN relays
and every test of it ran on one.

homesRoundTrip and rollbackRefused prove a relay stores a signed list and
refuses to go backwards against ITSELF. That is worth having and it is not what
multi-homing exists for. It exists so relay1 cannot make itself the only
mailbox you have — and the check that stops it happens on a client comparing
what two different relays say. None of that had ever been executed.

Now it is, with two relays on separate databases and separate audiences:

  • Both serve the same signed record; neither can produce a different one that
    verifies, because the signature covers the list and the serial.
  • The attack itself: a stale relay serving serial 1 with only itself in the
    list, beside a current relay serving serial 4 with both. BOTH signatures
    verify — which is precisely why the serial exists — and the test asserts the
    relay serves the serial, because ranking them is the client's job and the
    material to do it is what the relay owes.
  • A list signed by the wrong key is refused and not stored. Everything else
    rests on that: a relay able to edit this list would simply remove the other
    relays.
  • And a test that the two relays really are separate, since without it the
    whole suite would pass against one shared database while proving nothing —
    the same trap withRelayPair was written to avoid in the other direction.

Learned while writing it: a relay refuses a home list that does not name
itself. That is a good rule — it stops a relay being handed a record it has no
part in and asked to serve it — and it is why the two harnesses need different
audiences. Two relays with one name would not have been two relays.

No relay can detect the stale-relay attack, and that is not a gap to fix
here: the stale relay is behaving correctly by its own lights, holds a record
that verifies, and cannot know a higher serial exists elsewhere.

659 relay tests in 60 suites green, core paths 7/7.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

relay#36 would spread load using the multi-homing machinery, and the issue is right that it is premature — "only worth it well past the point PgBouncer stops helping", and nothing measured is within sight of that. What was worth doing was checking the foundation, because multi-homing is a property BETWEEN relays and every test of it ran on one. `homesRoundTrip` and `rollbackRefused` prove a relay stores a signed list and refuses to go backwards against ITSELF. That is worth having and it is not what multi-homing exists for. It exists so relay1 cannot make itself the only mailbox you have — and the check that stops it happens on a client comparing what two different relays say. None of that had ever been executed. Now it is, with two relays on separate databases and separate audiences: - Both serve the same signed record; neither can produce a different one that verifies, because the signature covers the list and the serial. - The attack itself: a stale relay serving serial 1 with only itself in the list, beside a current relay serving serial 4 with both. BOTH signatures verify — which is precisely why the serial exists — and the test asserts the relay serves the serial, because ranking them is the client's job and the material to do it is what the relay owes. - A list signed by the wrong key is refused and not stored. Everything else rests on that: a relay able to edit this list would simply remove the other relays. - And a test that the two relays really are separate, since without it the whole suite would pass against one shared database while proving nothing — the same trap `withRelayPair` was written to avoid in the other direction. Learned while writing it: a relay refuses a home list that does not name itself. That is a good rule — it stops a relay being handed a record it has no part in and asked to serve it — and it is why the two harnesses need different audiences. Two relays with one name would not have been two relays. No relay can detect the stale-relay attack, and that is not a gap to fix here: the stale relay is behaving correctly by its own lights, holds a record that verifies, and cannot know a higher serial exists elsewhere. 659 relay tests in 60 suites green, core paths 7/7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
paulhitt commented 2026-09-04 17:06:02 +00:00 (Migrated from gitlab.com)

assigned to @paulhitt

assigned to @paulhitt
paulhitt commented 2026-09-04 17:06:33 +00:00 (Migrated from gitlab.com)

mentioned in issue #36

mentioned in issue #36
paulhitt commented 2026-09-04 20:41:12 +00:00 (Migrated from gitlab.com)

deleted the spike/cold-storage-delivered-envelopes branch. This merge request now targets the main branch

deleted the `spike/cold-storage-delivered-envelopes` branch. This merge request now targets the `main` branch
paulhitt commented 2026-09-04 20:41:32 +00:00 (Migrated from gitlab.com)

mentioned in commit 35fbbbe5b6

mentioned in commit 35fbbbe5b6b96adaffd2ba5b45c7faf3546b45c8
paulhitt (Migrated from gitlab.com) merged commit 35fbbbe5b6 into main 2026-09-04 20:41:32 +00:00
paulhitt commented 2026-09-09 18:59:04 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
Sign in to join this conversation.