Skip to content

docs(design): lead-agent identities and canvas access for external agents (#98)#1800

Open
jaylfc wants to merge 2 commits into
devfrom
docs/lead-agent-identity-design
Open

docs(design): lead-agent identities and canvas access for external agents (#98)#1800
jaylfc wants to merge 2 commits into
devfrom
docs/lead-agent-identity-design

Conversation

@jaylfc

@jaylfc jaylfc commented Jul 12, 2026

Copy link
Copy Markdown
Owner

What

Founding design document for lead-agent identities and external-agent canvas access (project board task 98): docs/design/lead-agent-identity-and-canvas-access.md (541 lines). Plan only, no implementation.

The problem it encodes

Jay's org model is a three-lead structure (@taOS-dev, @taOSmd-dev, @taOS-website-dev lead the three elements of the taOS project; grok and kilo are tools the leads use). In reality only jay's admin account exists: two leads have been logging in AS JAY with copied credentials (website-dev broke on a stale password), the Members-screen canvas checkbox writes project_members.can_edit_canvas but nothing enforces it over HTTP, and the auth middleware does not accept agent tokens on canvas routes at all. The 2026-07-12 interim (a website-dev non-admin USER account via invite code) is explicitly retired by this design.

What the design covers (all claims verified in code, cited file:line at dev@9d006f47)

  1. Scope vocabulary: canvas_read + canvas_write added to the closed vocab in both agent_auth_requests.py and agent_registry.py, project-bound like project_tasks, narrow-not-widen preserved.
  2. Middleware: anchored-matcher closed allowlist for the canvas element routes, stream, and snapshots, mirroring _is_agent_task_path; the permissions PATCH stays human-only.
  3. Enforcement: agent writes require BOTH a project-bound canvas_write grant AND project_members.can_edit_canvas = 1; the checkbox becomes the live kill switch (default OFF). Reads require canvas_read + membership. User sessions get _get_owned_project semantics, closing the current any-authenticated-user hole.
  4. Honest attribution: agent elements record author_kind="agent" + canonical id; includes the verified _user_id bug (reads request.state.user, which nothing sets, so every element today is authored as "system").
  5. Lead identity minting via the existing consent flow, with the verified wrinkles (seeded internal identities own the handles; consent-minted records have no bus handle so a2a send 403s) and a per-lead migration checklist ending in credential retirement.
  6. Org-model section (leads vs tools) tied to the nested-elements whole-element assignment slice, plus write limits (payload cap, rate window), honest edge cases, and open questions with recommendations.

Slice plan: 5 bounded slices for external CLI agents (scopes, middleware, gating + attribution, stream/snapshots, limits) with exact file paths and verify commands, plus one operational maintainer slice (minting + retirement runbook).

Review focus

  • The D3 authorization matrix (session vs agent, read vs write).
  • The supersede decision for the seeded @-handle identities in D5.
  • Open questions 1 to 5 (tool canvas_read, event actor field, handle-at-approve, scope implication, element claims).

Do not merge without Jay review; this is a founding design doc.

Summary by CodeRabbit

  • Documentation
    • Added a design document outlining secure access for registry-minted lead agents to project canvases.
    • Defined project-scoped consent permissions for canvas read/write and clarified administrator-controlled edit behavior.
    • Documented attribution rules for human vs. agent-authored actions, plus consistent event updates.
    • Included rollout guidance covering migration, revocation/kill-switch behavior, rate limits, and key edge cases.

@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 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a design document for project-scoped lead-agent identities, canvas authorization and attribution, credential migration, write limits, implementation slices, edge cases, decisions, and non-goals.

Changes

Lead Agent Canvas Access

Layer / File(s) Summary
Current canvas and identity surface
docs/design/lead-agent-identity-and-canvas-access.md
Documents the existing lead identity model, agent-token coverage, canvas authorization gaps, attribution issues, capability model, and lead designation semantics.
Canvas authorization and attribution
docs/design/lead-agent-identity-and-canvas-access.md
Defines canvas_read and canvas_write scopes, agent-route middleware pass-through, route-level membership and edit checks, permission matrices, and principal-based attribution.
Lead migration and operational controls
docs/design/lead-agent-identity-and-canvas-access.md
Specifies lead credential migration, write and snapshot limits, exclusive project lead designation, implementation slices, edge-case handling, consolidated decisions, and non-goals.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the new design doc about lead-agent identities and canvas access for external agents.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/lead-agent-identity-design

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 12, 2026

Copy link
Copy Markdown

Gitar is working

Gitar

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/lead-agent-identity-and-canvas-access.md`:
- Around line 255-262: Resolve the v1 human-member authorization policy
consistently across the session-path helper, authorization matrix, and tests:
allow any HUMAN project member to write while preserving owner/admin access and
existence-hiding behavior for unauthorized users. Update the relevant
`_get_owned_project`-based logic and matrix expectations, including the
corresponding section around the forward-rule documentation, so member access no
longer returns 404.
- Around line 346-355: Update the migration design around the supersession flow
to define an atomic transaction that releases the seeded handle and assigns it
to the consent-minted successor without collision. Explicitly rebind live
task/element assignments, project-member rows, and grants to the successor;
revoke old tokens and credentials; and retain historical attribution against the
original canonical ID. Ensure all identity, handle, reference, and credential
changes commit or roll back together.
- Around line 387-403: Clarify the “D6. Limits on agent canvas writes” section
by defining separate rate-limit keys/windows for agent canvas writes and
snapshot.png render requests. Explicitly state whether agent and human
principals are included in each limit, removing the current contradiction that
limits apply to both principals while human sessions are exempt and renders
share the write window.
- Around line 199-203: The canvas_write API contract must not expose full
element bodies to write-only callers. Update the POST/PATCH /canvas/elements
response behavior described in the canvas_write section to return only a minimal
acknowledgment, or gate the full element response on canvas_read, and add
coverage for the write-only path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b25012e8-491d-4ee1-b6d8-cf17b105dc4e

📥 Commits

Reviewing files that changed from the base of the PR and between 9d006f4 and 017bf1d.

📒 Files selected for processing (1)
  • docs/design/lead-agent-identity-and-canvas-access.md

Comment on lines +199 to +203
- `canvas_read`: list elements, receive the SSE stream, fetch snapshots, for
the token's bound project only.
- `canvas_write`: create, update, delete canvas elements on the bound
project, subject to the membership flag in D3. Write does not imply read;
leads request both.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Avoid returning full element bodies from canvas_write routes. POST/PATCH /canvas/elements return {"element": ...}, which makes write-only access read-capable on update. Return only a minimal acknowledgment here, or require canvas_read for the full element response, and add a test for the write-only path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/lead-agent-identity-and-canvas-access.md` around lines 199 - 203,
The canvas_write API contract must not expose full element bodies to write-only
callers. Update the POST/PATCH /canvas/elements response behavior described in
the canvas_write section to return only a minimal acknowledgment, or gate the
full element response on canvas_read, and add coverage for the write-only path.

Comment on lines +255 to +262
- **Session path**: resolve `request.state.user_id`; apply
`_get_owned_project` semantics (owner or admin, existence-hiding 404 for
everyone else). Decision for USER sessions: a human needs no checkbox and
no scope; the gate is project visibility. Today that means owner/admin,
which is unchanged behavior for jay and simultaneously closes the
any-authenticated-user hole documented above. The forward rule, recorded
now for multi-user: any HUMAN project member may write; the
`can_edit_canvas` flag governs agent principals only.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Resolve the human-member authorization contradiction.

Lines [255-262] restrict human access to project owners/admins, while the stated forward rule allows any human project member to write; the matrix still returns 404 for those members. Make the v1 policy explicit and align the helper, matrix, and tests.

Also applies to: 291-296

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/lead-agent-identity-and-canvas-access.md` around lines 255 - 262,
Resolve the v1 human-member authorization policy consistently across the
session-path helper, authorization matrix, and tests: allow any HUMAN project
member to write while preserving owner/admin access and existence-hiding
behavior for unauthorized users. Update the relevant `_get_owned_project`-based
logic and matrix expectations, including the corresponding section around the
forward-rule documentation, so member access no longer returns 404.

Comment on lines +346 to +355
One verified wrinkle the migration must handle: consent-minted records have
`handle=""`, and a2a send 403s an agent with no handle
(`tinyagentos/routes/a2a_bus.py:171-175`). Additionally, the lead handles may
ALREADY exist as seeded internal identities
(`tinyagentos/routes/agent_registry.py:170-175`). The migration therefore
supersedes: suspend the old seeded row (freeing the handle), then set the
handle on the new consent-minted identity (the store supports it,
`tinyagentos/agent_registry_store.py:573-605`; slice 6 adds the small admin
surface). Supersede-not-mutate matches the reattach rule in
`docs/design/external-agent-onboarding.md`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make identity supersession atomic and rebind persisted references.

The successor has a different canonical ID, so the migration must explicitly enumerate what happens to existing task/element assignments, project-member rows, grants, tokens, and historical attribution. Reassign live references while preserving historical authorship, revoke the old credentials, and perform handle release/reassignment atomically; otherwise leads can lose assignments or the new handle can collide during migration.

Also applies to: 365-385

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/lead-agent-identity-and-canvas-access.md` around lines 346 - 355,
Update the migration design around the supersession flow to define an atomic
transaction that releases the seeded handle and assigns it to the consent-minted
successor without collision. Explicitly rebind live task/element assignments,
project-member rows, and grants to the successor; revoke old tokens and
credentials; and retain historical attribution against the original canonical
ID. Ensure all identity, handle, reference, and credential changes commit or
roll back together.

Comment on lines +387 to +403
### D6. Limits on agent canvas writes

Verified: no payload size cap and no write rate limit exist today. Agents
are exactly the callers that can emit a pathological loop, so writes get
bounds (applied to BOTH principals, humans included, generously):

- Serialized `payload` cap of 64 KiB per element, checked in the route before
the store call, 413 on breach. Covers create and patch.
- Per-principal fixed-window rate limit on agent writes: 60 writes per
minute per canonical_id per project, 429 on breach, reusing the fixed
window pattern already in the tree (login limiter in `routes/auth.py`,
pairing claim limiter in `routes/cluster.py`). Human sessions are not rate
limited in v1.
- `snapshot.png` renders and writes a file server-side on every call
(`tinyagentos/routes/project_canvas.py:127-133`); agent calls share the
same 60/min window so a polling agent cannot turn the renderer into a disk
hose.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Define separate, consistent limits for writes and rendering.

The plan says limits apply to both principals, then says human sessions are not rate-limited. It also defines an agent write window but says snapshot.png reads share it. Specify separate keys/windows for agent writes and render requests, and state whether humans are included in each.

🧰 Tools
🪛 LanguageTool

[style] ~390-~390: Consider an alternative for the overused word “exactly”.
Context: ...rite rate limit exist today. Agents are exactly the callers that can emit a pathologica...

(EXACTLY_PRECISELY)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/lead-agent-identity-and-canvas-access.md` around lines 387 - 403,
Clarify the “D6. Limits on agent canvas writes” section by defining separate
rate-limit keys/windows for agent canvas writes and snapshot.png render
requests. Explicitly state whether agent and human principals are included in
each limit, removing the current contradiction that limits apply to both
principals while human sessions are exempt and renders share the write window.


- `projects` gains `lead_member_id TEXT NULL` referencing a member of that
project (validated in the route, not by FK, matching the store's style in
`tinyagentos/projects/project_store.py`).

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: lead_member_id can dangle after member removal — no cleanup path defined

The column is validated only at SET time (the new PATCH 404s on a member id not in the project), but nothing requires lead_member_id to be cleared when that member is later removed from the project or its project_members row is deleted. Consumers reading lead_member_id would then dereference a non-existent member, so a project could retain a "lead" who is no longer a participant, and the a2a quiet-filter / default-assignee pointer could point at a ghost. Specify the cleanup: clear lead_member_id when a member is removed, or have consumers validate the id against live members on read.


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

their UI call site.
- Consumers move: the a2a quiet-filter and channel `settings.leads` sync
currently select members `WHERE is_lead = 1`
(`tinyagentos/projects/a2a.py:108-146`); they read `lead_member_id`

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: Regenerate any denormalized settings.leads copy during the is_leadlead_member_id migration

The design moves consumers to read lead_member_id, but settings.leads (the persisted channel/quiet-filter list) is described only as "read the column" — it does not state who rewrites the stored settings.leads value when lead_member_id changes. If settings.leads is a denormalized snapshot written by a sync job, that job must be updated to source from lead_member_id; otherwise the a2a channel membership and quiet-filter can silently diverge from the new pointer until something re-syncs it. Call out the sync-write side explicitly in slice 6.


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

@kilo-code-bot

kilo-code-bot Bot commented Jul 12, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 New Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
docs/design/lead-agent-identity-and-canvas-access.md 467 lead_member_id can dangle after a lead member is removed from the project; no cleanup path is defined, so consumers may dereference a ghost lead.

SUGGESTION

File Line Issue
docs/design/lead-agent-identity-and-canvas-access.md 477 settings.leads denormalized copy is only described as "read the column"; the migration/slice 6 does not state who rewrites the persisted value when lead_member_id changes.
Files Reviewed (1 file)
  • docs/design/lead-agent-identity-and-canvas-access.md - 2 new issues (2 pre-existing CodeRabbit findings intentionally not duplicated: human-member authorization contradiction @l291, identity supersession atomicity @l355, write-only API response exposure @l203, rate-limit contradiction @L450)

Fix these issues in Kilo Cloud


Reviewed by hy3-20260706:free · Input: 75.1K · Output: 13.7K · Cached: 222.5K

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/lead-agent-identity-and-canvas-access.md`:
- Around line 465-478: Update the lead-member design to require clearing
projects.lead_member_id when its referenced member is removed or suspended,
preventing a dangling lead pointer. Specify that the PATCH
/api/projects/{project_id}/lead route rejects suspended or inactive members,
while still allowing null to unset the lead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 77dd7f62-1baf-43d2-aff7-4af07c8a5411

📥 Commits

Reviewing files that changed from the base of the PR and between 017bf1d and 4bbbc85.

📒 Files selected for processing (1)
  • docs/design/lead-agent-identity-and-canvas-access.md

Comment on lines +465 to +478
- `projects` gains `lead_member_id TEXT NULL` referencing a member of that
project (validated in the route, not by FK, matching the store's style in
`tinyagentos/projects/project_store.py`).
- New endpoint `PATCH /api/projects/{project_id}/lead` with body
`{"member_id": "<id>" | null}`, session-only (owner or admin, same gate as
the members routes), 404 for a member id not in the project. It replaces
the per-member route `PATCH /api/projects/{pid}/members/{mid}/lead`
(`tinyagentos/routes/projects.py:298`) and `set_member_lead`
(`tinyagentos/projects/project_store.py:80-84`), which are removed with
their UI call site.
- Consumers move: the a2a quiet-filter and channel `settings.leads` sync
currently select members `WHERE is_lead = 1`
(`tinyagentos/projects/a2a.py:108-146`); they read `lead_member_id`
instead (a list of at most one).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Specify unsetting lead_member_id on lead-member removal. The pointer is TEXT NULL with no FK and is only route-validated, but membership revocation deletes the project_members row (per D3, "A revoked membership deletes the row," and the mid-session edge case). Nothing here clears lead_member_id when the pointed member is removed, so the column can dangle at a non-member id. The a2a quiet-filter / settings.leads sync then read a stale lead (a suspended/removed agent still designated), or silently drop it depending on the join. Add an explicit rule: removing a member (or suspending the lead agent) must null lead_member_id, and state whether the set-lead route rejects a suspended/inactive member.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/design/lead-agent-identity-and-canvas-access.md` around lines 465 - 478,
Update the lead-member design to require clearing projects.lead_member_id when
its referenced member is removed or suspended, preventing a dangling lead
pointer. Specify that the PATCH /api/projects/{project_id}/lead route rejects
suspended or inactive members, while still allowing null to unset the lead.

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