Skip to content

feat(canvas): gate stream + snapshot + keepalive recheck (#1800 slice 4)#1830

Closed
jaylfc wants to merge 1 commit into
feat/lead-identity-basefrom
feat/lead-identity-stream-gating
Closed

feat(canvas): gate stream + snapshot + keepalive recheck (#1800 slice 4)#1830
jaylfc wants to merge 1 commit into
feat/lead-identity-basefrom
feat/lead-identity-stream-gating

Conversation

@jaylfc

@jaylfc jaylfc commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Slice 4 of the lead-agent identity epic (taOS project board task tsk-lid4).

  • Requires canvas_read (scope + can_read_canvas member flag) for agent
    principals on GET /canvas/stream, /canvas/snapshot.png and
    /canvas/snapshot.tldr. Reuses _authorize_canvas_actor in read mode.
  • Snapshots stay READ scope only: a canvas_write-only token gets 403.
  • On the SSE keepalive tick, re-verify the agent principal's
    can_read_canvas flag and close the stream if it was cleared, so a
    revoked agent cannot hold a long-lived stream open.

DO NOT MERGE

Docs-Reviewed: stream + snapshot gating with keepalive recheck per
lead-agent-identity-and-canvas-access.md

Require canvas_read (scope + can_read_canvas member flag) for agent
principals on GET /canvas/stream, /canvas/snapshot.png and
/canvas/snapshot.tldr, reusing _authorize_canvas_actor in read mode.
Snapshots stay read-scope only: a canvas_write-only token is 403.
On the SSE keepalive tick, re-verify an agent principal's
can_read_canvas flag and close the stream if it was cleared, so a
revoked agent cannot hold a long-lived stream open.

Tests: SSE connect as agent with and without scope+flag, snapshot
fetch as agent (read allowed, write-only 403), stream closes after
the read flag is cleared; session owner/admin behavior unchanged.

Docs-Reviewed: stream + snapshot gating with keepalive recheck per lead-agent-identity-and-canvas-access.md
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • master
  • dev

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6cba896f-eb13-4720-8d4b-d5fb40d156e1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/lead-identity-stream-gating

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gitar-bot

gitar-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

# SSE open (slice 4). Session users are unaffected.
if actor_kind == "agent":
member = await ps.get_member(project_id, actor_id)
if not (member or {}).get("can_read_canvas"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Flag recheck is bypassed while canvas events keep flowing, so a revoked agent can hold a long-lived stream open on a busy canvas

The can_read_canvas recheck lives inside the except asyncio.TimeoutError branch, which only runs when no event arrives for 10s. On an actively-updated canvas, queue.get() keeps returning canvas.* events, this branch never executes, and a revoked/removed agent keeps receiving real event frames indefinitely — which contradicts the stated goal that "a revoked agent cannot hold a long-lived stream open."

Consider re-verifying the flag before yielding event data (or on a time-based cadence that runs every iteration regardless of idle), and/or unsubscribing the queue when the flag is cleared. As written, only an idle revoked stream is actually closed.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

closed = False
try:
while True:
await asyncio.wait_for(gen.__anext__(), timeout=12)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Strengthen revocation test to cover a busy canvas

This test publishes a single canvas.permission_changed event and then asserts the generator closes within 12s, which only exercises the idle keepalive recheck. It does not assert that no further canvas.* event frames are delivered after revocation, and it would still pass on a canvas that emits events continuously (the exact scenario the production WARNING above describes), because the loop simply consumes frames until the timeout-based close.

Consider publishing additional canvas.* events after _grant_canvas(..., read=False) and asserting that no event frames are received post-revocation (only the final StopAsyncIteration), so the test would catch the busy-canvas bypass.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
tinyagentos/routes/project_canvas.py 328 Flag recheck only runs on the 10s idle keepalive; a revoked agent on a busy/continuously-updated canvas keeps receiving event frames because the except asyncio.TimeoutError branch never executes, so the stream is not actually closed.

SUGGESTION

File Line Issue
tests/test_routes_project_canvas.py 879 Revocation test only exercises the idle keepalive path; it should publish events after revocation and assert no further event frames are delivered, to catch the busy-canvas bypass.
Files Reviewed (2 files)
  • tinyagentos/routes/project_canvas.py - 1 issue (WARNING)
  • tests/test_routes_project_canvas.py - 1 issue (SUGGESTION)

Fix these issues in Kilo Cloud


Reviewed by hy3:free · Input: 76K · Output: 9.6K · Cached: 168K

@jaylfc

jaylfc commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #1838, which landed all seven slices as one consistent set on dev (with the CI-caught active-handle migration-order fix folded). Closing.

@jaylfc jaylfc closed this Jul 15, 2026
@jaylfc jaylfc deleted the feat/lead-identity-stream-gating branch July 15, 2026 23:08
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