Document local-trusted vs cloud-hosted deployment scenarios in PRODUCT.md. Spec out assets and issue_attachments tables with storage provider abstraction and attachment API endpoints. Draft comprehensive plan for human users, memberships, invites, permissions, and agent-to-human task delegation across both deployment modes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.4 KiB
7.4 KiB
Humans and Permissions Plan
Status: Draft Date: 2026-02-20 Owner: Server + UI + Shared + DB
Goal
Add first-class human users and permissions while preserving two deployment modes:
- local trusted single-user mode with no login friction
- cloud-hosted multi-user mode with mandatory authentication and authorization
Why this plan
Current V1 assumptions are centered on one board operator. We now need:
- multi-human collaboration with per-user permissions
- safe cloud deployment defaults (no accidental loginless production)
- local mode that still feels instant (
npx paperclip runand go) - agent-to-human task delegation, including a human inbox
Product constraints
- Keep company scoping strict for every new table, endpoint, and permission check.
- Preserve existing control-plane invariants:
- single-assignee task model
- approval gates
- budget hard-stop behavior
- mutation activity logging
- Keep local mode easy and trusted, but prevent unsafe cloud posture.
Deployment modes
Mode A: local_trusted
Behavior:
- no login UI
- browser opens directly into board context
- embedded DB and local storage defaults remain
- a local implicit human actor exists for attribution
Guardrails:
- server binds to loopback by default
- refuse startup if mode is
local_trustedwith non-loopback bind, unless explicit--allow-unsafe-local-network - UI shows a persistent "Local trusted mode" badge
Mode B: cloud_hosted
Behavior:
- login required for all human endpoints
- hosted DB and remote deployment supported
- multi-user sessions and role/permission enforcement
Guardrails:
- fail startup if auth provider/session config is missing
- fail startup if insecure auth bypass flag is set
- health payload includes mode and auth readiness
Auth and actor model
Unify request actors into a single model:
user(authenticated human)agent(API key)local_board_implicit(local trusted mode only)
Rules:
- in
cloud_hosted, onlyuserandagentare valid actors - in
local_trusted, unauthenticated browser/API requests resolve tolocal_board_implicit - all mutating actions continue writing
activity_logwith actor type/id
Data model additions
New tables
users
- identity record for human users (email-based)
company_memberships
company_id,user_id, status, role metadata- stores effective permissions and optional org scope constraints
invites
company_id, invite email, token hash, expires_at, invited_by, revoked_at, accepted_at- optional default permissions payload at invite time
user_permission_grants(or JSON grant blob in membership)
- explicit grants such as
agents:create - includes scope payload for chain-of-command limits
issuesextension
- add
assignee_user_idnullable - preserve single-assignee invariant with XOR check:
- exactly zero or one of
assignee_agent_id/assignee_user_id
- exactly zero or one of
Compatibility
- existing
created_by_user_id/author_user_idfields remain and become fully active - agent API key model remains unchanged, still company-scoped
Permission model (initial set)
Core grants:
agents:createusers:inviteusers:manage_permissionstasks:assigntasks:assign_scope(org-constrained delegation)
Additional behavioral rules:
- board-level users can manage all grants
- non-board users can only act within explicit grants
- assignment checks apply to both agent and human assignees
Chain-of-command scope design
Initial approach:
- represent assignment scope as an allow rule over org hierarchy
- examples:
subtree:<agentId>(can assign into that manager subtree)exclude:<agentId>(cannot assign to protected roles, e.g., CEO)
Enforcement:
- resolve target assignee org position
- evaluate allow/deny scope rules before assignment mutation
- return
403for out-of-scope assignments
Invite and signup flow
- Authorized user creates invite with email + grants + optional expiry.
- System sends invite URL containing one-time token.
- Invitee signs up/logs in.
- Email on authenticated account must match invite email.
- Accepting invite creates active
company_membershipand permission grants. - Inviter/admin can revoke invite before acceptance.
Security rules:
- store invite token hashed at rest
- one-time use token with short expiry
- all invite lifecycle events logged in
activity_log
Human inbox and agent-to-human delegation
Behavior:
- agents can assign tasks to humans when policy permits
- humans see assigned tasks in inbox view (including in local trusted mode)
- comment and status transitions follow same issue lifecycle guards
API additions (proposed):
GET /companies/:companyId/inbox(human actor scoped to self)POST /companies/:companyId/issues/:issueId/assign-userPOST /companies/:companyId/invitesPOST /invites/:token/acceptPOST /invites/:inviteId/revokeGET /companies/:companyId/membersPATCH /companies/:companyId/members/:userId/permissions
Local mode UX policy
- no login prompt or account setup required
- local implicit board user is auto-provisioned for audit attribution
- invite/multi-user screens can be hidden or marked unavailable in local mode
- if operator wants collaboration, they must switch to
cloud_hosted
Cloud agents in this model
- cloud agents continue authenticating through
agent_api_keys - same-company boundary checks remain mandatory
- agent ability to assign human tasks is permission-gated, not implicit
Implementation phases
Phase 1: Mode and guardrails
- add explicit deployment mode config (
local_trusted | cloud_hosted) - enforce startup safety checks and health visibility
- implement actor resolution for local implicit board
Phase 2: Human identity and memberships
- add schema + migrations for users/memberships/invites
- wire auth middleware for cloud mode
- add membership lookup and company access checks
Phase 3: Permissions and assignment scope
- add grant model and enforcement helpers
- add chain-of-command scope checks for assignment APIs
- add tests for forbidden assignment (for example, cannot assign to CEO)
Phase 4: Invite workflow
- invite create/send/accept/revoke endpoints
- email-match enforcement and token security
- UI for invite management and membership permissions
Phase 5: Human inbox + task assignment updates
- extend issue assignee model for human users
- inbox API and UI
- agent-to-human assignment flow with policy checks
Acceptance criteria
local_trustedstarts with no login and shows board UI immediately.cloud_hostedcannot start without auth configured.- No request in
cloud_hostedcan mutate data without authenticated actor. - Humans can be invited by email, accepted with matching email, and revoked.
- Permissions can be granted/revoked per company member.
- Assignment scope prevents out-of-hierarchy or protected-role assignments.
- Agents can assign tasks to humans only when allowed.
- Humans can view assigned tasks in inbox and act on them per permissions.
- All new mutations are company-scoped and logged in
activity_log.
Open decisions
- Auth provider choice for cloud mode (Auth.js vs hosted provider).
- Whether local mode supports optional login in addition to implicit board.
- Exact representation of permission grants (normalized table vs JSON schema).
- Whether a user can belong to multiple companies in initial release.
- Whether invite email delivery is built-in or webhook/provider integration only.