Skip to content

Report upstream: subduction subscription push bypasses the pull gate after revocation #17

Description

@lannbot

The finding

Observed in the walking-skeleton spike, phase 3b (spikes/skeleton/, commit 3177cbd; discussion on #8): with subduction_keyhive's SubductionKeyhive policy gating pull against the keyhive auth graph,

  • explicit pulls behave correctlysync_with_peer from a non-member (before add, and again after revocation) is refused with an empty diff (success=false, nothing sent — the no-information-leak posture);
  • but a subscription established while authorized keeps delivering after revocation — the revoked peer continued to receive new ciphertext commits over the live subscription (OBSERVED: v3 ciphertext still pushed to the revoked subscriber in the spike run).

Mechanics: subduction reaches a peer two ways — request/response sync, which consults StoragePolicy::authorize_fetch / filter_authorized_fetch, and subscription push, which walks the subscriber list built at sync time. Revocation updates the auth graph, but nothing removes the peer from the subscriber list and the push path does not re-check the policy.

Impact is bounded by the crypto tier — BeeKEM revocation excluded the peer from new epochs, so everything pushed is undecryptable (KeyNotFound, asserted in the spike) — but what leaks is precisely what pull-access control exists to withhold from non-members: the ciphertext itself plus its traffic metadata (that the doc changes, when, and roughly how much). For the framework this is the relay-tier gate of the pull/read separation, so we want it closed upstream.

Reproduction

spikes/skeleton/, just run (needs a polymorph-iroh checkout; see its README). The scenario prints the observed branch explicitly; the assertion accepts either upstream behavior and reports which occurred, so a fixed upstream flips the output to v3 ciphertext NOT pushed to the revoked subscriber with no spike changes.

Pins: subduction rev 2401102882b97d6b13c559fbbcfc2257e3a46dd3, keyhive rev efe6ccf via [patch.crates-io].

Suggested fix shapes (either suffices)

  1. Re-check authorize_fetch (or run filter_authorized_fetch) per subscriber at notification time; or
  2. tear down a peer's subscriptions for a sedimentree when its authorization is lost (the subduction_keyhive bridge learns this when it ingests the revocation).

Draft upstream issue (for inkandswitch/subduction — do not file without explicit go)

Title: [bug]: subscription push skips StoragePolicy — revoked peers keep receiving new commits

Setup: subduction_core + subduction_keyhive at rev 2401102, two peers, SubductionKeyhive as the connection/storage policy, keyhive docs with SedimentreeId = keyhive DocumentId. Peer B syncs a tree with subscribe: true while holding Access::Read, then is revoked (keyhive revocation ingested on both sides; transitive_members() no longer contains B).

Observed: after revocation, new add_commits on peer A are still pushed to B over the existing subscription — B's sedimentree keeps growing. Explicit sync_with_peer calls from B are refused correctly (empty diff), so the gap is specific to the push path: the subscriber list is built at sync time and notifications do not re-consult StoragePolicy.

Expected: a peer that would fail authorize_fetch no longer receives pushed commits — either by re-checking the policy per subscriber at notification time, or by dropping the peer's subscriptions when authorization is lost.

Impact: bounded when content is E2E-encrypted (the revoked peer cannot decrypt new epochs), but the ciphertext and its traffic metadata keep flowing — the exact thing pull-level access control withholds from non-members, and the behavior a pull-only relay role would rely on.

Reproduction: two-peer scenario in a wasm32-wasip2 embedding, available at https://github.com/polymorph-components/polymorph-apps/tree/main/spikes/skeleton (the host prints which branch occurs; happy to minimize into a native test against ChannelTransport if useful).

Status

  • Upstream issue filed (needs explicit go; the draft above is ready to paste)
  • Upstream response / fix tracked
  • On fix: bump the subduction pin (as a keyhive+subduction pair) and confirm the spike prints the gated branch

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions