Skip to content

fix(id): show world chat only to participants who may chat or read history - #256

Merged
dezren39 merged 1 commit into
mainfrom
fix-chat-visibility
Oct 18, 2026
Merged

dezren39 merged 1 commit into
mainfrom
fix-chat-visibility

Conversation

@dezren39

@dezren39 dezren39 commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Summary

Implements the owner's decision: join-only participants do not see chat, and an explicit new history scope opts in to reading it. The install seed stays secret (#250), now through the same single projection. Based on main.

  • WorldScopes::HISTORY (wire name history): read chat lines without being able to send. chat implies it. GUEST is unchanged.
  • project_event_for replaces redact_install_seed. It is the one place that decides what a viewer sees: it hides chat (including host chat) from viewers with neither chat nor history, and zeroes the install seed for everyone. It is used by events_after, the snapshot, and the live stream that sessions forward.
  • Because chat implies history, the scope intersections (attenuation, effective scopes, account and directory ceilings, friend delegation) use implied() so a history child of a chat parent keeps its bit. A guest can hand out a read-only link.

Findings

  • Chat leaked to join-only viewers on main: a join-only snapshot contained chat (reproduced against e1f99237).
  • No live seed leak. The session loop never forwarded the raw install event. The raw WorldHandle::subscribe() broadcast does carry the real seed and chat, and a test now pins that so nobody forwards it unprojected.
  • Gaps are safe. The Rust session and the web client (web/src/world.ts) both advance their cursor with max(cursor, event.sequence); neither assumes contiguous sequences. needs_snapshot and oldest_retained_sequence come from the retained buffer before projection, so they do not depend on the viewer.

Behaviour change

Existing join-only capabilities (for example from attenuate) stop seeing chat. The journal format does not change. Downgrading is not safe for chat privacy: an older binary ignores the unknown scope bit and treats a history-only capability as join-only, and it answers the wire name history with "unknown scope". The record covers this.

Not done

Validation

  • cargo test --lib: 1275 passed, 4 ignored (the subagent's run). I re-ran the 12 chat_visibility tests and the 31 world_session tests: all pass.
  • cargo fmt --check: clean. Clippy via the rustup 1.97.0 cargo has no errors; plain cargo clippy fails with E0514 as before.
  • Not run: the tests/ integration targets and the web build.

Design record: doc/2026-10-18T04-00-00Z_fix_world_chat_visibility/.

Summary by CodeRabbit

  • New Features

    • Added read-only world-chat access for participants with the new history permission. Participants without chat-reading access no longer receive chat in snapshots, catch-up history, or live updates.
    • Install updates now hide their seed while retaining the module hash.
    • Permission checks recognize that chat access includes history access.
  • Documentation

    • Added guidance on chat visibility, permissions, and event delivery behavior.

…story

Add the history scope and route the snapshot, catch-up pages and the live stream through one projection that withholds chat from join-only viewers and zeroes the install seed.
@dezren39
dezren39 requested a review from a team as a code owner October 18, 2026 04:14
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b828895c-6270-44df-a6e9-f210f163cf45
📥 Commits

Reviewing files that changed from the base of the PR and between e1f9923 and 575126f.

📒 Files selected for processing: 4
  • doc/2026-10-18T04-00-00Z_fix_world_chat_visibility/2026-10-18T04-00-00Z_fix_world_chat_visibility.md
  • pkgs/id/src/directory_view.rs
  • pkgs/id/src/world.rs
  • pkgs/id/src/world_session.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📝 Walkthrough

The change adds a history scope for read-only chat access. Snapshots, catch-up pages, and live sessions now project events according to viewer scopes. Install seeds are redacted from projected events.

Changes: World Chat Visibility

Layer / File(s) Summary
History scope and delegation
pkgs/id/src/world.rs, pkgs/id/src/directory_view.rs
Adds the history scope. CHAT implies HISTORY, and either scope permits reading chat. Account ceilings and delegation checks use implied scopes.
Snapshot and catch-up projection
pkgs/id/src/world.rs, doc/2026-10-18T04-00-00Z_fix_world_chat_visibility/2026-10-18T04-00-00Z_fix_world_chat_visibility.md
Snapshots and catch-up pages project events using viewer scopes. Filtered events do not consume snapshot byte budget; event sequence gaps remain possible. The document describes the policy and related behavior.
Live event projection
pkgs/id/src/world.rs, pkgs/id/src/world_session.rs
Adds per-viewer projection for live events. Sessions send visible projected events, skip withheld events while advancing the cursor, and end when projection fails.

Priority: ⬆️ High

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant WorldSession
  participant WorldHandle
  participant WorldActor
  participant WorldCore
  participant Viewer
  WorldSession->>WorldHandle: project_event(token, event)
  WorldHandle->>WorldActor: send projection command
  WorldActor->>WorldCore: project_for(token, event)
  WorldCore-->>WorldActor: projected event or hidden result
  WorldActor-->>WorldHandle: return projection result
  WorldHandle-->>WorldSession: return projected event or hidden result
  WorldSession->>Viewer: send visible projected event
Loading
Merge Risk: ⚪ Minimal · up to 57512

Chat visibility and install-seed handling have no identified merge-blocking issue. The change is ready for normal checks.

Security architecture risk: 🔵 Low · up to 57512

The inspected delivery paths enforce the new read permission without granting chat-sending authority. No introduced authorization bypass was established. Rolling back to an older binary would remove the new chat-privacy protection, and cross-version deployment behavior remains incompletely verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected exposure is bounded to a world's participant-facing retained and live events. A valid JOIN-only capability previously sufficed to obtain chat through these paths; head adds read authorization without granting additional chat-send authority. Raw host access remains privileged. External consumers of the public subscription API were not established by this review.

Security Findings and Attack Paths

  • observed — The suspected raw-subscription disclosure is not supported by the inspected participant transport. Ordinary live events pass through authenticated projection. Program installation instead sends an authorized projected snapshot and a ProgramChanged frame containing the module hash and sequence, not the raw install event or seed.

Trust Boundaries and Controls

  • observed — Viewer identity comes from the capability token and its stored digest, not caller-supplied scope claims. Authorization checks revocation, expiry, and current effective scopes. Effective scopes intersect each capability and account ceiling after implication and invalidate friend delegation when the required friendship ends. HISTORY does not imply CHAT, INPUT, or DELEGATE.

Resilience and Maintainability Implications

  • observed — Live projection recalculates authorization rather than caching viewer scopes for the session. Authorization failure stops delivery; explicit revocation notifications terminate matching sessions. Membership-only changes may leave an idle connection open, but subsequent inspected projection and snapshot operations recheck current authority. Session termination uses the existing presence guard for cleanup.

Hardening Proposals

  • proposed — Define and validate a rollback policy that preserves the new privacy contract, such as blocking older binaries from opening upgraded worlds or requiring an explicit compatibility procedure. This is a deployment safeguard proposal, not an observed authorization bypass introduced by this PR.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 74.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 50 functions across 3 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: restricting world chat visibility to participants with chat or history permission.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 74.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 50 functions across 3 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.98.1)

Clippy execution timed out

Comment @coderabbitai help to get the list of available commands.

@dezren39
dezren39 merged commit d7ccd44 into main Oct 18, 2026
4 of 5 checks passed
@dezren39
dezren39 deleted the fix-chat-visibility branch October 18, 2026 12:15
dezren39 added a commit that referenced this pull request Oct 18, 2026
Resolutions:
- web/mod.rs: take main's unwrap_or(Duration::ZERO) in the save limiter.
- world.rs: keep ModuleProvenance and SourceBuildProvenance; drop the local
  Isolation, which main moved to groups::worlds and re-exports.
- world_session.rs: keep main's per-viewer event projection and pass the
  viewport through to send_view.
- world.rs project_event_for: zero only the install seed, keeping provenance
  and any later event field; main's tests gain provenance: None.

Integration with the chat-visibility change (#256):
- chat.history now needs the right to read chat (CHAT or HISTORY), the same
  rule the event stream applies.
- new observer role: join + history.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant