Menu 0.6.0 preview

Integrate

Membership

This page states membership in the Developer Preview as trueseal-sync ADR-0027 decides it, with the API names of ADR-0028. Destroy Group is on Destroy Group.

Authority and trust boundary

  • Any current member may admit, remove or leave. There are no admins and no creator role.
  • Every current member is fully trusted for membership.
  • A hostile current member can remove every other member.
  • A removed device that does not cooperate can still rewrite membership on devices that have not yet seen its removal.
  • TrueSeal defends against neither. The response to a hostile or compromised device is Destroy Group.
  • Apps that need roles enforce them above the library, before calling it.

API

Manifest validity and ordering

  • Each Group Manifest carries parent, the hash of the manifest it was built on. The genesis manifest has none. parent is covered by the signature.
  • An issuer sets version = base.version + 1 with checked arithmetic.
  • A receiver accepts an incoming manifest only if all of these hold:
    • the signature verifies;
    • the group ID matches;
    • the issuer is a member of the receiver’s current manifest;
    • (version, hash) is strictly greater than the current manifest’s, where on an equal version the lower hash wins;
    • the version jump is within a sanity bound.
  • A manifest that fails is acked and dropped with a delivery issue (ADR-0026).

Convergence promise

Once every member has received the same set of Group Manifests, all members hold an identical manifest. A membership change from an honest member is never lost, unless that member was itself removed concurrently.

Pending Membership Changes

Admitting, removing or leaving writes the new manifest, its outbox entries and a Pending Membership Change in one Session State transaction. Group Manifest outbox entries never expire. The change records its intent (“D is a member”, “X is not a member”) and the hash of the manifest that carried it.

Each time the issuer accepts a newer manifest W, it checks every pending change:

CaseConditionAction
SatisfiedW already reflects the intentCleared
Deliberately supersededW descends from a manifest the issuer knows carried the changeCleared
Concurrently overwrittenW neither descends from the change nor satisfies itRe-applied on top of W at W.version + 1; the new manifest becomes the carrier
Issuer removedW excludes the issuerDropped

When a concurrently overwritten admission would push the group past maxGroupSize, it is dropped instead, with admissionDropped(name, reason), and the joiner stays pendingJoin.

Pending changes survive restarts. An offline issuer re-applies after it reconnects. The app never redoes a membership change.

Event flicker

membersChanged follows the device’s current view. While a concurrent change is being re-applied, a device can see a member leave and rejoin. Apps render the current members list and never treat one event as a permanent fact.

Maximum Group Size

  • The group holds at most maxGroupSize members, 32 members.
  • accept(request) past the limit fails locally with groupFull{max}.
  • A manifest at the limit fits in one frame (ADR-0025).

Removal

  • A device counts itself removed only when the excluding manifest’s parent is a manifest it holds or has held that includes it.
  • On a deliberate removal the library wipes the namespace’s group state, outbox and dedup table, generates a fresh identity, sets status to notJoined, and raises statusChanged(notJoined, removed). Pending sends are dropped without a delivery issue.
  • False removal: a manifest that excludes the device but was built concurrently, not on a manifest that included it, is stored as the current view. The device does not wipe, and waits for its admitter to re-apply the admission. The usual case is a new joiner whose admission lost a tie.
  • The same TrueSeal object stays usable and can pair again (ADR-0028).

Leave

  • leave() issues a manifest excluding the local device and persists the leaving state.
  • In leaving the device sends no Sync and runs no message handler. It stays connected until the relay has accepted every one of its manifest pushes.
  • It then wipes and rotates as on removal, and raises statusChanged(notJoined, left).
  • A crash or going offline in leaving resumes leaving on the next start.
  • destroyGroup() in leaving abandons the leave (ADR-0029).

State machine

notJoined → pendingJoin → member → leaving → notJoined, and member → notJoined on a deliberate removal. Destroy Group adds destroying.

FromTriggerTo
member leave() leaving. A manifest without this device is issued.
leaving The relay has accepted every manifest push notJoined. The device wipes, makes a fresh identity and raises statusChanged(notJoined, left).
member A manifest that excludes this device, built on one that included it notJoined. The device wipes, makes a fresh identity and raises statusChanged(notJoined, removed).
member A manifest that excludes this device, built concurrently Unchanged. The device stores it as its current view, does not wipe, and waits for its admitter to re-apply the admission.

Crash points

  • Before the issuing transaction commits. No change. The call fails or never returns.
  • After it commits. The manifest is redelivered from the outbox. The pending change is re-checked on the next accepted manifest.
  • Receiving. Accepting a manifest, updating pending changes and queuing any re-application commit together. A crash before the commit means the relay redelivers the manifest.
  • During leaving. leaving resumes. The wipe happens only after the relay has persisted the last manifest push.
  • During the wipe. The wipe restarts.

What stays the app’s job

  • Roles and permissions.
  • Rendering the current member list.
  • Deleting the app’s own data after statusChanged(notJoined, removed | left), if it should. The wipe covers TrueSeal’s Session State only.
  • Calling destroyGroup() instead of remove(memberId) for a lost, stolen or hostile device.