From 71e7799bc1d77efb4a7f114b99a3a14a9c1dfd50 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 1 Aug 2026 22:29:44 +0000 Subject: [PATCH] docs: publish private issue operating model Document ACL behavior, audited break-glass, accepted aggregate-count disclosure, the trust-agent residual risk, CI leak-surface inventory, and the reviewed enforce rollout. Co-Authored-By: Paperclip --- doc/DEVELOPING.md | 12 +++++- doc/ISSUE-PRIVACY.md | 92 ++++++++++++++++++++++++++++++++++++++++++++ doc/PRODUCT.md | 2 +- 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 doc/ISSUE-PRIVACY.md diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index 7fd18c27ba9..b0e4b824501 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -334,12 +334,20 @@ No Docker or external database is required for this mode. `PAPERCLIP_ISSUE_PRIVACY_MODE` controls the canonical opt-in issue/project privacy predicate: -- `shadow` (default): log structured would-deny decisions but preserve existing reads -- `enforce`: return private issues only to implicit principals, issue grantees, or private-project access members; hide private projects from non-members +- `enforce` (default): return private issues only to implicit principals, issue grantees, or private-project access members; hide private projects from non-members +- `shadow`: log structured would-deny decisions but preserve existing reads; use only for rollout diagnosis - `off`: skip the predicate and shadow logging Grant lookups use a five-second cache by default; override it with `PAPERCLIP_ISSUE_PRIVACY_CACHE_TTL_MS` when testing revocation timing. +The default changed from `shadow` to `enforce` after the P5 shadow-log review. The +review ran the privacy authorization and route fixtures in shadow mode, classified +every structured `issue privacy would deny read` / `project privacy would deny read` +record against the expected non-member probes, and found zero unexpected denials. +Keep `shadow` as a temporary diagnostic override; it is not a safe production +privacy setting. See [ISSUE-PRIVACY.md](./ISSUE-PRIVACY.md) for the user-facing +model, break-glass behavior, and residual risks. + Issue-bound heartbeat runs inherit the same read predicate through the stored `heartbeat_runs.scope_kind = 'issue'` and `issue_id` binding. Issue deletion nulls the foreign key but preserves the explicit issue scope as a fail-closed diff --git a/doc/ISSUE-PRIVACY.md b/doc/ISSUE-PRIVACY.md new file mode 100644 index 00000000000..b8a06619274 --- /dev/null +++ b/doc/ISSUE-PRIVACY.md @@ -0,0 +1,92 @@ +# Private tasks and projects + +Paperclip work is company-open by default. A task or project can instead be +marked private when its title, discussion, documents, attachments, work +products, or agent run traces should be limited to named participants. + +## Who can read private work + +A private task is readable by its responsible user, creating user, current +user or agent assignee, active task grantees, and members of its private +project. Private children inherit the root task's privacy boundary. Assigning a +private task grants the assignee access to that private subtree; the grant stays +active until it is explicitly revoked. + +Private projects have a separate access-member list. A task-level grant can +expose one task without exposing its containing private project. Direct reads +by non-members return `404`, and list, count, search, attention, status-card, +activity, run-history, and tree-control surfaces apply the same predicate. +Visible blocker and mention edges may show a locked identifier-only stub; they +never include the private task's title or content. + +Audit logs remain company-wide because they are the sanctioned oversight path. +They contain entity identifiers rather than private task content. Dashboard and +sidebar aggregate counts also remain company-wide: they may include private +work in totals, but do not expose titles, descriptions, or identifiers. The +issue list/count APIs themselves are viewer-filtered. + +## Audited break-glass + +Company owners and admins do not silently inherit private-task access. A normal +task detail request still returns `404`. When an owner or admin has a legitimate +emergency need, they must deliberately request the task with +`?breakGlass=true`. + +Every successful break-glass read writes both: + +- an `issue.break_glass_read` audit row containing the actor, task id, and time; +- a warning system notice on the private task so its owners can see that access occurred. + +The flag does not change the canonical read predicate, create a grant, or make +future reads implicit. Non-admin members and agents cannot use it. + +## Agent runs and shared-agent residual risk + +Issue-bound run detail, events, transcripts, logs, and workspace operations use +the task ACL. Company run lists retain only timing/status/token/cost metadata for +non-members so budget oversight continues without exposing task identity or run +content. + +> **Residual risk — trusted agents (`trust-agent`).** Paperclip enforces privacy +> on reads from the control plane, but an agent that legitimately processes a +> private task may write learned content into shared persistent memory, its home +> directory, an external tool, or a later public response. V1 deliberately +> trusts the agent not to exfiltrate that context. Use isolated execution +> workspaces and appropriately trusted agents for sensitive work; task ACLs are +> not a sandbox or data-loss-prevention system. + +## Rollout modes + +`PAPERCLIP_ISSUE_PRIVACY_MODE=enforce` is the default. `shadow` records +structured would-deny decisions without enforcing them and exists only for +rollout diagnosis. `off` disables the task predicate. Operators should not use +`shadow` or `off` when private-task confidentiality is required. + +Before the enforce-default flip, the P5 review ran the privacy authorization +and route fixtures under shadow mode, matched every would-deny record to an +expected non-member probe, and found zero unexpected denials. The regression +suite keeps explicit non-member assertions for task lists/counts, search and +extract, attention, status cards, activity, runs, tree control, blocker and +mention stubs, documents/attachments, and private-project lists. + +## CI leak-test inventory + +The privacy regression gate is part of the normal server Vitest suite. Its +surface coverage is intentionally distributed beside the routes and services +it protects: + +| Surface | Non-member regression coverage | +| --- | --- | +| Task detail, list, count, grants, documents, work products | `issue-access-grants-routes.test.ts`, `company-search-service.test.ts` | +| Search and machine extract | `company-search-service.test.ts`, `company-search-extract-service.test.ts` | +| Attention feed | `attention-service.test.ts` | +| Status-card hydrate and dry-run | `status-cards.test.ts` | +| Activity stream | `activity-service.test.ts`, `activity-routes.test.ts` | +| Run list, live run, detail, transcript, events, logs, operation history | `heartbeat-run-privacy-routes.test.ts` | +| Tree holds and tree control | `issue-tree-control-routes.test.ts` | +| Blocker and mention identifier-only stubs | `issue-access-grants-routes.test.ts` | +| Attachment content | `issue-attachment-routes.test.ts` | +| Private-project list and direct read | `projects-list-archived-routes.test.ts` | + +Adding a new task-derived read surface requires a non-member fixture in this +gate before the surface can ship. diff --git a/doc/PRODUCT.md b/doc/PRODUCT.md index f276d3dfbc7..f8112502039 100644 --- a/doc/PRODUCT.md +++ b/doc/PRODUCT.md @@ -128,7 +128,7 @@ Paperclip’s core identity is a **control plane for autonomous AI companies**, - Do not make the core product a general chat app. The current product definition is explicitly task/comment-centric and “not a chatbot,” and that boundary is valuable. - Do not build a complete Jira/GitHub replacement. The repo/docs already position Paperclip as organization orchestration, not focused on pull-request review. - Do not build enterprise-grade RBAC first. Paperclip now has authenticated mode, company memberships, instance roles, and permission grants, but fine-grained enterprise governance should remain secondary to the core company control plane. -- Do not interpret agent-level privacy flags as a project/issue privacy feature in V1; work visibility stays company-scoped. +- Do not interpret agent-profile privacy flags as project/issue privacy. Work remains company-open by default, while explicitly private issues and projects use their own task ACLs. - Do not lead with raw bash logs and transcripts. Default view should be human-readable intent/progress, with raw detail beneath. - Do not force users to understand provider/API-key plumbing unless absolutely necessary. There are active onboarding/auth issues already; friction here is clearly real.