Beta review: does live group resolution hold up for peering allow-lists? #125
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/Cairn#125
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Scheduled revisit of a v0.5 decision, to be judged against real beta usage rather than reasoning. Full argument in #123 (comment of 2026-08-05).
What was decided: peering
sendersandreceiversallow-lists store the group name and resolve membership live per transfer (cached, with a last-known-good fallback on IdP outage). Cairn does not expand a group into a stored user list.The alternative that was declined: materialize group membership at assignment time, tracking whether each user was granted via group or directly, so group removal revokes only what it granted, direct grants survive, and a user in two listed groups keeps access when one is removed. That provenance design is correct and is written out in full on #123 — it was declined because all three of those problems are created by materializing in the first place, it conflicts with "identity is consumed, not implemented", it reopens the revocation window #96 closed, and it makes the stored config record a computation instead of an admin's intent.
What beta should actually test
PrepareTransfer? It sits in the inbound path before any bytes flow.receiverslist matches nobody. Correctly fail-closed, but silently useless — does an admin actually hit this, and is the UI warning enough? (See #123.)Close this with either "live resolution confirmed" or a link to the issue implementing the materialized design.
Triage: tandem (beta judgement). Decide, from real use, whether live group resolution (vs materialising IdP group membership) holds up for peering allow-lists. Feeds the #150/#176 design. No build until the call is made.
after #211 epic, members are encrypted stored in .cairn-state/ at least for the ones who ever were assigned to a Spaces folder.
this can be potentially used to share available users via peering for easiers knowledge what potential "receivers" are at another instance. so this would Improve UI/UX for users using this feature, while blast radius is already kept at a minimum.
need to investigate if the shared keys for instance peering could be used to decrypt.
OR
another member list gets created and stored in the backend an be able to be decrypted with the shared key between the instances. this member list is already created on a per peering instance card as "can receive" so when peering this list could be exchanged, and upon adding or removing members, this can be re-read when someone prepares a file to be transfered, and the "receiver" list gets populated by the information from the to be sent to peer by a call checking their "can receive" member list.
Decision (2026-08-14): live resolution confirmed — and extended to recipient discovery. Greenlit.
The beta-review question is settled in favour of keeping live group resolution; materialization stays rejected. Beyond confirming the authorization model, this issue now tracks the concrete feature the review surfaced: recipient discovery for peering sends (the sender getting a picker of who it can send to at a peer, instead of typing blind).
Chosen design — Option 1: live
ListRecipientsRPC. One method on the existing authenticated control plane (Hello → Authenticate, same sessionPrepareTransferuses). The receiver computes, live, exactly the set it would accept:Receiversallow-list ∩ resident users (group receivers resolved via #96, empty without the admin client). The sender renders it as the #226-style picker.peer-recipient-list).Why not materialize (Options 2/3, the #125 comment's push/decrypt ideas): the offline argument that justifies caching for authorization has no analogue for discovery — you can only discover recipients at a peer you can reach, and you can only send to a peer you can reach. Reusing the app-owned spacestore is also the wrong data set (Space members ≠ eligible receivers), and its "decrypt with the shared peering key" premise breaks minimal-blast-radius (the peering key is an Ed25519 auth credential, not a state-decryption key).
Degraded fallback (Option 4), leak-free: when the peer can't enumerate (group receivers, no admin client) or is unreachable, the field degrades to free-text seeded from a local datalist of the sending user's own previously-used recipients for this peer — that data already lives in
peer-transfers.json(Outbound.Recipient), so it's the sender's own history, never the peer's roster. Offline needs no special path: the send fails with a visiblefailedtransfer + reason, so the user always gets an output.Full analysis + ranking table in the decision memo. Sequenced behind #176 → #150 (the card is where the picker lives).
Shipped in v0.6.22 (PR #233, squash-merged; CI green in 41s; protobuf regenerated and full
go build/vet/testgreen on the runner before merge). This is the concrete build the recipient-discovery decision produced — Option 1 (liveListRecipients) + Option 4 (validated free-text) as the leak-free fallback.What landed:
ListRecipientsRPC on the authenticatedcairn.peer.v1.Peeringcontrol plane. The receiver returns itsReceiversuser allow-list narrowed to residents, reusing the exact delivery gate (Recipients.Exists, #123), as one auditedpeer-recipient-listevent. It moves no data and returns only names an authenticated peer could already confirm one at a time via size-0PrepareTransferprobes (§4.3) — so it formalizes that enumeration rather than widening it. Fail-closed on a disabled/paused peer.GET /api/v1/peering/peers/{peer}/recipients, gated exactly like the peers list (only a permitted sender may enumerate; a peer the caller may not use is 404, indistinguishable from absent).{recipients, live:true}on success.{live:false}) — local send history frompeer-transfers.json, never the peer's roster. An offline peer can't be sent to anyway; the async pipeline still surfaces that as a visible failed transfer, so the user always gets an output.<datalist>picker populated on open and on peer change, with a hint distinguishing live vs. history. Recipient field stays free-text.Tests: endpoint (live / history-fallback / fail-closed-for-non-senders / anonymous),
ListRecipients(resident-allow-listed / disabled-peer),ReceiverUsers.Deployed to both dogfoods, confirmed live on digest
sha256:c720da67…(the published v0.6.22):cairn-enc(IdP-managed, .249) andcairn-openbao(app-owned, .247), both Running, no restarts. Visual click-through on files.c0rdyceps.ch is the remaining human step (redeploy logged sessions out; I can't log in). Closing on shipped + tested + deployed; reopen if the picker misbehaves in the browser.The v0.5 peering trio (#176 backend, #150 card UI, #125 discovery) is complete.
To close the loop on the issue as originally written (the beta-review of live group resolution), separate from the recipient-discovery feature above:
Decision on the fork: live resolution confirmed. The recipient-discovery decision memo (2026-08-14) re-examined live-vs-materialize and reaffirmed live: config stays intent, revocation is instant, and materialization is declined for the same reasons #123 declined it.
ListRecipients(this release) is the live model extended to discovery, not a reason to abandon it — which is corroborating evidence for the live direction rather than against it. The one condition named for reversing (peering must work with the IdP unreachable for long stretches) has no analogue for discovery and did not materialize as a real requirement.The concrete beta checks stay live work, folded into the v0.7 closed beta (this issue's milestone), not dropped:
PrepareTransfer— measure under real transfers.Closing as live resolution confirmed; if real beta usage surfaces a case for materialization, that reopens as a fresh design issue rather than reverting this one. #129 remains open for the group-only-receivers admin warning.