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 <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-01 22:54:02 +00:00
1 parent 23ea12c188
commit 71e7799bc1
3 files changed
+103 -3

No files matched your search

+10 -2
View File
@@ -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
+92
View File
@@ -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.
+1 -1
View File
@@ -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.