Merge33 Shared Notes viewer WebSocket rejects current Share identity with 403 #1386

Closed
opened 2026-10-07 17:28:53 +00:00 by kayg · 7 comments
Owner

The merge33b workspace cargo test gate failed on owner_editor_viewer_and_live_revoke in calternal-collab/tests/shared_notes.rs. The viewer WebSocket returns 403 instead of 101. The server says Files item identity is not current, although the logged Share and Files Index item IDs match. This is a non-timeout failure. Keep the existing expectation. No Rust source changed in merge33b. Branch job/merge33-1122; gate source a9e331a3c; base origin/dev 0419170fe. Investigate the filesystem identity check and fixture save/refresh ordering before merge.

Verbatim gate evidence:

running 1 test
test owner_editor_viewer_and_live_revoke ... FAILED

failures:

---- owner_editor_viewer_and_live_revoke stdout ----
shared Note save identity before Files refresh: owner=27b870bd-86ec-47b4-ae93-a019668170fd path=Notes/live.md share_item_index=Some((Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68"), Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")))
shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=211 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")
shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=208 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")
shared Note access denied: recipient=78b09190-883a-4873-b10e-b4a897d5ef1e owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=206 reason=Files item identity is not current share=editor-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")
shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=215 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")
shared Note access denied: recipient=78b09190-883a-4873-b10e-b4a897d5ef1e owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=228 reason=Files item identity is not current share=editor-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")
shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=222 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")
shared Note WebSocket upgrade refused: user=viewer status=403 Forbidden reason=Origin, principal, Home, or shared Share check rejected the request

thread 'owner_editor_viewer_and_live_revoke' (2995542) panicked at crates/calternal-collab/tests/shared_notes.rs:289:5:
17 |       ws.send(encoding.toUint8Array(encoder));
18 |       resolve();
19 |     };
20 |     ws.onerror = (event) => {
21 |       const reason = event?.message ?? event?.error?.message ?? event?.type ?? String(event);
22 |       reject(new Error(`WebSocket upgrade failed: user=${user} url=${url} reason=${reason}`));
                      ^
error: WebSocket upgrade failed: user=viewer url=ws://127.0.0.1:2843/collab/shared/27b870bd-86ec-47b4-ae93-a019668170fd/notes/558eb4a8-4b10-493b-ae43-741c0c31e4a0 reason=WebSocket connection to 'ws://127.0.0.1:2843/collab/shared/27b870bd-86ec-47b4-ae93-a019668170fd/notes/558eb4a8-4b10-493b-ae43-741c0c31e4a0' failed: Expected 101 status code
      at <anonymous> (/home/kayg/Developer/calternal-wt/merge33-1122/target/tmp/.tmpnoYUFv/client/shared_notes.mjs:22:18)

Bun v1.4.2 (Linux x64)

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
shared Note access denied: recipient=78b09190-883a-4873-b10e-b4a897d5ef1e owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=183 reason=Files item identity is not current share=editor-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68")


failures:
    owner_editor_viewer_and_live_revoke

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.89s

error: test failed, to rerun pass `-p calternal-collab --test shared_notes`

The merge33b workspace cargo test gate failed on owner_editor_viewer_and_live_revoke in calternal-collab/tests/shared_notes.rs. The viewer WebSocket returns 403 instead of 101. The server says Files item identity is not current, although the logged Share and Files Index item IDs match. This is a non-timeout failure. Keep the existing expectation. No Rust source changed in merge33b. Branch job/merge33-1122; gate source a9e331a3c; base origin/dev 0419170fe. Investigate the filesystem identity check and fixture save/refresh ordering before merge. Verbatim gate evidence: ``` running 1 test test owner_editor_viewer_and_live_revoke ... FAILED failures: ---- owner_editor_viewer_and_live_revoke stdout ---- shared Note save identity before Files refresh: owner=27b870bd-86ec-47b4-ae93-a019668170fd path=Notes/live.md share_item_index=Some((Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68"), Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68"))) shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=211 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=208 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") shared Note access denied: recipient=78b09190-883a-4873-b10e-b4a897d5ef1e owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=206 reason=Files item identity is not current share=editor-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=215 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") shared Note access denied: recipient=78b09190-883a-4873-b10e-b4a897d5ef1e owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=228 reason=Files item identity is not current share=editor-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") shared Note access denied: recipient=f78fe7e3-e4bd-4e5d-bd10-f52e0ed125d8 owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=222 reason=Files item identity is not current share=viewer-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") shared Note WebSocket upgrade refused: user=viewer status=403 Forbidden reason=Origin, principal, Home, or shared Share check rejected the request thread 'owner_editor_viewer_and_live_revoke' (2995542) panicked at crates/calternal-collab/tests/shared_notes.rs:289:5: 17 | ws.send(encoding.toUint8Array(encoder)); 18 | resolve(); 19 | }; 20 | ws.onerror = (event) => { 21 | const reason = event?.message ?? event?.error?.message ?? event?.type ?? String(event); 22 | reject(new Error(`WebSocket upgrade failed: user=${user} url=${url} reason=${reason}`)); ^ error: WebSocket upgrade failed: user=viewer url=ws://127.0.0.1:2843/collab/shared/27b870bd-86ec-47b4-ae93-a019668170fd/notes/558eb4a8-4b10-493b-ae43-741c0c31e4a0 reason=WebSocket connection to 'ws://127.0.0.1:2843/collab/shared/27b870bd-86ec-47b4-ae93-a019668170fd/notes/558eb4a8-4b10-493b-ae43-741c0c31e4a0' failed: Expected 101 status code at <anonymous> (/home/kayg/Developer/calternal-wt/merge33-1122/target/tmp/.tmpnoYUFv/client/shared_notes.mjs:22:18) Bun v1.4.2 (Linux x64) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace shared Note access denied: recipient=78b09190-883a-4873-b10e-b4a897d5ef1e owner=27b870bd-86ec-47b4-ae93-a019668170fd note=558eb4a8-4b10-493b-ae43-741c0c31e4a0 path=Notes/live.md elapsed_ms=183 reason=Files item identity is not current share=editor-share share_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") index_item=Some("a616d51c-aeeb-4f15-9cba-6d11d5fc4a68") failures: owner_editor_viewer_and_live_revoke test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.89s error: test failed, to rerun pass `-p calternal-collab --test shared_notes` ```
Author
Owner

Additional source evidence for #1386: the test deliberately waits 100 ms in Access::note_saved before calling record_note_write. Files note_access retries a stale fingerprint at most eight times with a 20 ms delay. In this gate, each denial was logged after about 200 ms while the same Share and Files Index IDs were present; no Files refresh-complete message preceded the failed upgrade. The result is a real 403, not a client timeout. This suggests the save/reindex window, but it does not prove the cause. Keep the 101 assertion and the identity checks. I did not extend production retry budgets or weaken authorization for this verification failure.

Additional source evidence for #1386: the test deliberately waits 100 ms in Access::note_saved before calling record_note_write. Files note_access retries a stale fingerprint at most eight times with a 20 ms delay. In this gate, each denial was logged after about 200 ms while the same Share and Files Index IDs were present; no Files refresh-complete message preceded the failed upgrade. The result is a real 403, not a client timeout. This suggests the save/reindex window, but it does not prove the cause. Keep the 101 assertion and the identity checks. I did not extend production retry budgets or weaken authorization for this verification failure.
Author
Owner

Production comparison at detached revision 0419170fe4 completed. Same unmodified owner_editor_viewer_and_live_revoke test passes:

Finished `test` profile [unoptimized + debuginfo] target(s) in 8m 42s

running 1 test
test owner_editor_viewer_and_live_revoke ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 12.13s

Files shares.rs is byte-for-byte identical to the merge branch. Next run the branch test with callback diagnostics visible, then compare changed dependencies if the failure reproduces. No authorization assertion changed.

Production comparison at detached revision 0419170fe456b58614befcb12d9c9d255118f1c0 completed. Same unmodified owner_editor_viewer_and_live_revoke test passes: Finished `test` profile [unoptimized + debuginfo] target(s) in 8m 42s running 1 test test owner_editor_viewer_and_live_revoke ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 12.13s Files shares.rs is byte-for-byte identical to the merge branch. Next run the branch test with callback diagnostics visible, then compare changed dependencies if the failure reproduces. No authorization assertion changed.
Author
Owner

The unchanged merge-branch Shared Notes test also passes: 1 passed; 0 failed; finished in 11.83s. The Files completion callback returns Ok(()) and access=Editor. After revoke, the same resolver reports Share row absent and the existing client asserts socket closure within 1100 ms.

Diff inspection: Share resolver, Note writer, collaboration session, calternal-fs and calternal-db are unchanged from 0419170fe. No merged branch changed the Share identity check. No bisect can identify an identity change absent from the diff. The prior failing run showed matching Share/Index IDs but no Files-refresh completion before the handshake exhausted retries. The fixture artificially holds that stale interval with a 100-ms timer. Production and branch passes do not prove that the earlier failure is load-only. Keep #1386 open while replacing that timer with an explicit viewer-arrival barrier and retaining all handshake, viewer-write denial and live-revoke assertions. No product authorization relaxation.

The unchanged merge-branch Shared Notes test also passes: 1 passed; 0 failed; finished in 11.83s. The Files completion callback returns Ok(()) and access=Editor. After revoke, the same resolver reports Share row absent and the existing client asserts socket closure within 1100 ms. Diff inspection: Share resolver, Note writer, collaboration session, calternal-fs and calternal-db are unchanged from 0419170fe. No merged branch changed the Share identity check. No bisect can identify an identity change absent from the diff. The prior failing run showed matching Share/Index IDs but no Files-refresh completion before the handshake exhausted retries. The fixture artificially holds that stale interval with a 100-ms timer. Production and branch passes do not prove that the earlier failure is load-only. Keep #1386 open while replacing that timer with an explicit viewer-arrival barrier and retaining all handshake, viewer-write denial and live-revoke assertions. No product authorization relaxation.
Author
Owner

Committed cec7ddf72c: test(collab): order the shared Note save window by viewer arrival (#1386).

The failing run never logged Files-refresh completion before viewer upgrade refusal. Production and the unchanged branch both pass independently, and the authorization implementation is unchanged. The suspected scheduling race is in the test's artificial 100-ms stale interval; this is an inference, not a demonstrated product authorization regression. Replace the sleep with a first-save viewer-arrival notification, and assert the actual file fingerprint changed while the Share/Index item IDs still match. No existing expectation, client deadline, retry policy or production access code changed. Viewer 101, forbidden viewer-update rejection, saved bytes, and live revoke remain required.

Verification:

  • cargo fmt --check: exit 0, no output.
  • cargo clippy -p calternal-collab --all-targets -- -D warnings: exit 0.
    Finished dev profile [unoptimized + debuginfo] target(s) in 6m 09s
  • cargo test -p calternal-collab -- --test-threads=4: exit 0. All unit/integration/doc-test binaries passed. Shared Notes result:
    test owner_editor_viewer_and_live_revoke ... ok
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.68s
  • Focused barrier test: exit 0.
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.10s

The required fetch/merge origin/dev completed once: Already up to date. origin/dev is still 0419170fe4. Server tests and final workspace gates remain pending. Keep #1386 open; do not treat passing focused tests as a full gate pass.

Committed cec7ddf72ca134af0e8019429b9f423a169b654a: test(collab): order the shared Note save window by viewer arrival (#1386). The failing run never logged Files-refresh completion before viewer upgrade refusal. Production and the unchanged branch both pass independently, and the authorization implementation is unchanged. The suspected scheduling race is in the test's artificial 100-ms stale interval; this is an inference, not a demonstrated product authorization regression. Replace the sleep with a first-save viewer-arrival notification, and assert the actual file fingerprint changed while the Share/Index item IDs still match. No existing expectation, client deadline, retry policy or production access code changed. Viewer 101, forbidden viewer-update rejection, saved bytes, and live revoke remain required. Verification: - cargo fmt --check: exit 0, no output. - cargo clippy -p calternal-collab --all-targets -- -D warnings: exit 0. Finished `dev` profile [unoptimized + debuginfo] target(s) in 6m 09s - cargo test -p calternal-collab -- --test-threads=4: exit 0. All unit/integration/doc-test binaries passed. Shared Notes result: test owner_editor_viewer_and_live_revoke ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.68s - Focused barrier test: exit 0. test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.10s The required fetch/merge origin/dev completed once: Already up to date. origin/dev is still 0419170fe456b58614befcb12d9c9d255118f1c0. Server tests and final workspace gates remain pending. Keep #1386 open; do not treat passing focused tests as a full gate pass.
Author
Owner

Strengthened regression committed as ef0aa12b12. It now requires the first viewer lookup to return Denied from the real Files resolver while the save callback withholds refresh. Only that lookup releases the callback. The next Hub recheck must allow the viewer. This tests the stale inode explicitly rather than allowing refresh to race ahead of the first Files query. Existing viewer 101, refused viewer writes, saved bytes and live revoke assertions are unchanged.

Observed sequence in the focused run: viewer denied after 204 ms with matching Share and Index item IDs; Files refresh completes with result=Ok(()) and access=Editor; viewer opens and syncs; revoked editor loses its Share and closes. No production retry budget or identity check changed.

Verbatim focused gate output:
Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/merge33-1122/crates/calternal-collab)
Finished dev profile [unoptimized + debuginfo] target(s) in 5m 59s
Finished test profile [unoptimized + debuginfo] target(s) in 13.64s
test owner_editor_viewer_and_live_revoke ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.04s

Fmt, focused clippy and focused test each exited 0. Full workspace clippy already passed; full workspace tests remain running. Keep the original failed gate as evidence. The artificial timer window is the supported fixture explanation; the original host scheduling delay was not directly captured, so do not claim a proven production authorization fix.

Strengthened regression committed as ef0aa12b12e0047cf9a779827cc0ed32261b934e. It now requires the first viewer lookup to return Denied from the real Files resolver while the save callback withholds refresh. Only that lookup releases the callback. The next Hub recheck must allow the viewer. This tests the stale inode explicitly rather than allowing refresh to race ahead of the first Files query. Existing viewer 101, refused viewer writes, saved bytes and live revoke assertions are unchanged. Observed sequence in the focused run: viewer denied after 204 ms with matching Share and Index item IDs; Files refresh completes with result=Ok(()) and access=Editor; viewer opens and syncs; revoked editor loses its Share and closes. No production retry budget or identity check changed. Verbatim focused gate output: Checking calternal-collab v0.0.1 (/home/kayg/Developer/calternal-wt/merge33-1122/crates/calternal-collab) Finished `dev` profile [unoptimized + debuginfo] target(s) in 5m 59s Finished `test` profile [unoptimized + debuginfo] target(s) in 13.64s test owner_editor_viewer_and_live_revoke ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.04s Fmt, focused clippy and focused test each exited 0. Full workspace clippy already passed; full workspace tests remain running. Keep the original failed gate as evidence. The artificial timer window is the supported fixture explanation; the original host scheduling delay was not directly captured, so do not claim a proven production authorization fix.
Author
Owner

Merge33c verification completed at ef0aa12b12. Production and the unchanged branch passed independently; no merged Share identity implementation change was found. The fixture now requires a real stale-inode denial before it releases the Files refresh, then requires viewer access and live revoke through the unchanged Hub/Files checks. Existing assertions and production retry limits remain intact.

Full workspace result: exit 0. Shared Notes in that run:

test owner_editor_viewer_and_live_revoke ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.41s

Final strengthened focused regression: exit 0, 11.04s. Full workspace fmt/clippy/test and web check/test passed; the final fixture also passed focused fmt/clippy. The original scheduler delay remains an inference from the stalled fixture callback, not a captured production defect. Keep the earlier failing output. The final report is on #1122; this issue remains open for the orchestrator.

Merge33c verification completed at ef0aa12b12e0047cf9a779827cc0ed32261b934e. Production and the unchanged branch passed independently; no merged Share identity implementation change was found. The fixture now requires a real stale-inode denial before it releases the Files refresh, then requires viewer access and live revoke through the unchanged Hub/Files checks. Existing assertions and production retry limits remain intact. Full workspace result: exit 0. Shared Notes in that run: ``` test owner_editor_viewer_and_live_revoke ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 11.41s ``` Final strengthened focused regression: exit 0, 11.04s. Full workspace fmt/clippy/test and web check/test passed; the final fixture also passed focused fmt/clippy. The original scheduler delay remains an inference from the stalled fixture callback, not a captured production defect. Keep the earlier failing output. The final report is on #1122; this issue remains open for the orchestrator.
Author
Owner

Deployed to production as ef0aa12b1 at 2026-10-08 02:22 IST (backup /srv/calternal/backup-pre-m33-20261007-2236). Root cause: test fixture race (stale-identity window released by a timer); production and the unchanged branch both passed; the fixture now waits for a real stale denial before releasing the save.

Deployed to production as ef0aa12b1 at 2026-10-08 02:22 IST (backup /srv/calternal/backup-pre-m33-20261007-2236). Root cause: test fixture race (stale-identity window released by a timer); production and the unchanged branch both passed; the fixture now waits for a real stale denial before releasing the save.
kayg closed this issue 2026-10-07 20:52:39 +00:00
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal-v0#1386
No description provided.