OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-room commits / d574b78

0.2.16: record the creator decision — _amCreator is a METHOD, not a flag
Every consumer asking "is this node the creator?" via `!!sb._amCreator` was reading a truthy prototype function and getting `true` unconditionally. hyper-proto-peer's members-diag did exactly that, so its `amCreator` field has always reported `true` for every node, creator or not, and could never express the negative case. That is not cosmetic. The field was cited as evidence that a joining machine had stolen the creator role during a live incident, and it is likely the source of a standing "amCreator:true on BOTH nodes" concern recorded for wj62 — a condition the field would report whether or not it were true. _open() now records the decision it actually makes as `this._isCreator`, alongside the existing log line. Additive: one property, no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d574b7846201a32204bf0a82b8ba6c952dc410f4
by plankatron <fleeky@gmail.com> · 2026-07-27T22:10:23.000Z

Changed files (2) +7 -1

lib/state-base.js +6 -0
……@@ -386,6 +386,12 @@ export class StateBaseV3 {
386386 // EXISTING store to "continue" the genesis in place — that reindexes the view and crashes on write.
387387 // EXACTLY ONE co-owner may hold gsKeyPair at a time — the resynced peer MUST be forceNonCreator.
388388 const amCreator = this._forceCreator ? true : (this._forceNonCreator ? false : ((!this._getMembers || !this._selfKey) ? true : await this._amCreator()))
389 // RECORD the decision. `_amCreator` is a METHOD, so every consumer doing `!!sb._amCreator` to ask
390 // "is this node the creator?" was reading a truthy function and getting `true` unconditionally — which
391 // is exactly what hyper-proto-peer's members-diag did, so that field has always said `true` for every
392 // node, creator or not. A diagnostic that cannot report the negative case is worse than none: it was
393 // cited as evidence of a role flip during a real incident. This is the answer to that question.
394 this._isCreator = amCreator
389395 const roleTag = this._forceCreator ? ' (assume-genesis: forced creator)' : (this._forceNonCreator ? ' (resync: forced non-creator)' : '')
390396 console.log('[state-base-v3] room=%s %s%s base=%s', this._roomKeyHex.slice(0, 12),
391397 amCreator ? 'creator' : 'member', roleTag, this._detBaseKey.toString('hex').slice(0, 12))
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "atproto-room",
3 "version": "0.2.15",
3 "version": "0.2.16",
44 "description": "Attestation-gated, encrypted multi-writer room engine — StateBase (keet-model admission) + Lease + signer + RepoBase + LabelBase/LabelRoom + serve-membrane. Extracted from hyper-proto-peer; consumed by the atproto peer (repo devices) and the co-authored labeler.",
55 "type": "module",
66 "main": "index.js",