fix(spaces): pin visibility and ownerDID as server-authoritative
Automerge CRDT sync could overwrite space visibility when a client with a stale cached doc reconnects and merges. Now the server snapshots visibility and ownerDID before processing sync messages and reverts any client-side changes to these fields. These fields can only be changed through the authenticated API (PATCH /api/spaces/:slug), not through CRDT sync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
dce608ae1b
commit
c0b4250e96
|
|
@ -618,6 +618,10 @@ export function receiveSyncMessage(
|
|||
|
||||
const peerState = getPeerSyncState(slug, peerId);
|
||||
|
||||
// Snapshot server-authoritative fields before sync merge
|
||||
const prevVisibility = doc.meta?.visibility;
|
||||
const prevOwnerDID = doc.meta?.ownerDID;
|
||||
|
||||
// Apply incoming sync message
|
||||
const result = Automerge.receiveSyncMessage(
|
||||
doc,
|
||||
|
|
@ -625,9 +629,23 @@ export function receiveSyncMessage(
|
|||
message
|
||||
);
|
||||
|
||||
const newDoc = result[0];
|
||||
let newDoc = result[0];
|
||||
const newSyncState = result[1];
|
||||
|
||||
// Pin server-authoritative fields — clients must not overwrite these via sync.
|
||||
// Visibility and ownership can only be changed through the authenticated API.
|
||||
if (newDoc !== doc) {
|
||||
const visChanged = newDoc.meta?.visibility !== prevVisibility;
|
||||
const ownerChanged = newDoc.meta?.ownerDID !== prevOwnerDID;
|
||||
if (visChanged || ownerChanged) {
|
||||
console.warn(`[Store] Sync tried to change authoritative fields in ${slug} — reverting (vis: ${prevVisibility}→${newDoc.meta?.visibility}, owner: ${prevOwnerDID}→${newDoc.meta?.ownerDID})`);
|
||||
newDoc = Automerge.change(newDoc, 'Pin server-authoritative fields', (d) => {
|
||||
if (visChanged && prevVisibility) d.meta.visibility = prevVisibility;
|
||||
if (ownerChanged && prevOwnerDID) d.meta.ownerDID = prevOwnerDID;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
communities.set(slug, newDoc);
|
||||
peerState.syncState = newSyncState;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue