fix(auth): authorize structural writes from stable ancestry#182
Closed
marcus-pousette wants to merge 1 commit into
Closed
fix(auth): authorize structural writes from stable ancestry#182marcus-pousette wants to merge 1 commit into
marcus-pousette wants to merge 1 commit into
Conversation
This was referenced Jul 11, 2026
Collaborator
Author
|
Superseded by #188. The pre-write snapshot here stabilizes only the local authorization call; it is unsigned and unavailable to remote verifiers, so out-of-order structural delivery can still make honest replicas accept different scoped operation sets. #188 replaces this 497-line path with a smaller deterministic fail-closed rule and removes the obsolete machinery instead of stacking another patch on it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack
Stacked on #157. Merge #157 first, then retarget this PR to
main.In one sentence
Authorize structural writes against the node's stable source ancestry as well as its destination, so a scoped writer cannot pull an external node into its subtree.
What was wrong?
TreeCRDT
insertis also an upsert: if the node already exists, it detaches the node from its current parent and attaches it to the requested parent.Auth checked only the insert destination. A writer scoped to subtree A could therefore submit
insert(existingExternalNode, parent=A)and steal a node from outside its grant.SQLite local writes had a second version of the bypass. The extension prepared/applied the structural change before JavaScript awaited authorization inside the outer savepoint. A normal scope evaluator then saw the node at its new in-scope location instead of its old external location.
What changes?
Why the extra
absentresult?Before this PR, a missing target and an incomplete ancestor chain both returned
unknown. Insert needs to tell those apart: a missing target has no source grant to check, while incomplete ancestry must never be treated as authorization.Size and debloat
The PR is +497/-32 across 14 files:
Pre-write state is captured from the already-open native CRDT session and returned with the existing local-op result; it does not add a separate preflight query/round trip.
Validation
Regressions cover remote insert stealing, local insert/move cross-scope rejection, valid new inserts, valid in-scope moves, rollback, and native pre-state serialization.
Important remaining transaction issue
This fixes which tree state authorization evaluates. It does not remove the pre-existing arbitrary async authorization wait inside the outer SQLite savepoint. Concurrent use of the same runner can still interleave unrelated writes into that savepoint and an auth rejection can roll them back. That transaction-isolation redesign remains a separate critical PR rather than being hidden inside this scope patch.