Skip to content

0.5.218: evicting a device is one act that reaches every device; quorum leave/dissolve replicate - #700

Merged
emooreatx merged 4 commits into
chore/adopt-edge-v33from
fix/evict-device-0.5.218
Sep 30, 2026
Merged

emooreatx merged 4 commits into
chore/adopt-edge-v33from
fix/evict-device-0.5.218

Conversation

@emooreatx

Copy link
Copy Markdown
Contributor

Into chore/adopt-edge-v33 (#697; edge v34.3.0 / persist v51.1.0 / verify v18.0.0).

1. Evicting a device is ONE signed act (CSD-037, the stolen-device path)

  • POST /v1/self/nodes/{node}/release and POST /v1/self/occurrence/revoke now share one function, self_devices::evict_device. It withdraws the owner-binding(s) with owner-signed withdraws. It revokes the occurrence(s) through persist's signed put_identity_occurrence_revocation, signed by the owner's pen, so the IdentityOccurrenceRevocation plane carries it. It kicks replication. Then it reads both halves back from persist (nodes_owned_by, list_identity_occurrences_active).
  • A release now also revokes the node's content-KEM occurrence. Revoking an occurrence that is one of the owner's nodes also withdraws that node's binding. Either act on the node you are talking to needs force_self.
  • The revoke moved from auth::occurrence to the self-device router. It is now authorised like release: the owner's session, with the owner's pen signing on the server. The app's bearer is enough; before this change every call answered 401. Delegates and non-owners are still refused.
  • The response names each part that completed and each part that failed, with part and target. There are no silent Ok(_) arms: an incomplete part answers self.evict_incomplete, 500.
  • Honesty (CC 3.3.6.1): every answer carries history. It says that history already shared stays readable by the evicted device. There is no DEK rotation and no re-encryption; only new content and new room epochs are withheld. A test pins this: the grant on a file written before eviction stays, and a file written after eviction carries no key_grant to the evicted occurrence.
  • A source gate checks that put_identity_occurrence_revocation_local has no caller in src/. Its allow-list is empty.

2. Nobody joins without their own consent (maintainer's ruling 2026-09-30; CIRISConstitution#133, CIRISPersist#955)

These doors now answer 409 membership.consent_required, a literal id shared by families and communities:

  • the direct POST …/members for families and communities;
  • a quorum envelope, cosign or assemble whose proposed roster grows;
  • a create whose founding roster names anyone other than the founder.

Unaffected: remove, role changes, leave, dissolve, re-adding someone who is already active (still *.already_member), and pair rooms. The refusal sits at the HTTP doors only.

The tests now assert each refusal. Where a test is about what happens after a join, it builds the roster through test_only_* fixtures under tests/. There is no production bypass. In the devices ladder, the family rung now asserts the refusal, and family_on_b / family_file_* are marked red-expected on #955.

3. Quorum leave and dissolve as replicating amendments: stopped, not faked

  • Dissolve. I tried supersede_family_with_quorum with an empty proposed roster. persist refuses it: verify_membership_quorum: membership change not authorized: malformed accord family envelope: group has no members. The ignored red test a_quorum_dissolve_replicates_as_an_amendment pins this.
  • Leave. The quorum door evaluates quorum:M/N, so the leaver's single signature cannot meet it, and rule 5 says leaving is never put to a quorum.

Both sites document what persist would need.

4. Stale #907/#908/#910 markers

  • The #907 test runs un-ignored and passes.
  • Corrections were made additively in communities.rs, FSD §1.1 / §2.2 / §4 / §6, and the devices ladder. The Cargo.toml 0.5.218 headline was also appended to.

Localization

Two new ids, both added to KNOWN_UNLOCALIZED: self.evict_incomplete and membership.consent_required. The ratchet goes from 139 to 141. With ciris-client 0.5.225, --strict passes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QdM21U8xUmHk2TwrMTKJPf

…dy joins without consent

EVICT (CSD-037, the stolen-device path). `POST /v1/self/nodes/{node}/release`
and `POST /v1/self/occurrence/revoke` converge on `self_devices::evict_device`:
withdraw the owner-binding(s) (owner-signed `withdraws`), revoke the device's
occurrence(s) through persist's SIGNED `put_identity_occurrence_revocation`
(signed by the owner's pen, so the IdentityOccurrenceRevocation plane carries
it), kick replication, and read both halves back from persist. Release now
revokes the node's content-KEM occurrence; revoking an occurrence that IS an
owned node withdraws its binding. The revoke moved to the self-device router
and authorises the owner's session (the app's bearer; it answered 401 before),
never a delegate. Every answer names each part done and each part that failed
(`self.evict_incomplete`), and states that already-shared history stays
readable by the evicted device (no DEK rotation, no re-encryption). A source
gate pins that `put_identity_occurrence_revocation_local` has no caller in src/.

CONSENT TO JOIN (maintainer's ruling 2026-09-30; CIRISConstitution#133,
CIRISPersist#955). Every roster-growing HTTP door answers 409
`membership.consent_required`: family and community direct adds, a quorum
envelope/cosign/assemble whose proposed roster grows, and a create whose
founding roster names anyone but the founder. Remove, role, leave, dissolve,
re-adding an active member and pair rooms are unaffected. Tests build
multi-member rosters through clearly named test-only fixtures; the devices
ladder's `family` rung asserts the refusal and the downstream family rungs are
red-expected on #955.

QUORUM LEAVE/DISSOLVE AMENDMENTS: stopped, not faked. persist v51.1.0's
`supersede_family_with_quorum` refuses an empty proposed roster ("malformed
accord family envelope: group has no members"), and a self-leave cannot meet a
quorum:M/N on the leaver's signature; documented at both sites, pinned by an
ignored red test.

STALE MARKERS: persist v49 closed #907/#908/#910 — the #907 widened-member
read runs un-ignored; communities.rs, the FSD and the devices ladder corrected
additively.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QdM21U8xUmHk2TwrMTKJPf
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@emooreatx
emooreatx merged commit 5d5db07 into chore/adopt-edge-v33 Sep 30, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant