diff --git a/apps/docs/docs/concepts/stories-and-workspaces.md b/apps/docs/docs/concepts/stories-and-workspaces.md index 779aefff..2794d230 100644 --- a/apps/docs/docs/concepts/stories-and-workspaces.md +++ b/apps/docs/docs/concepts/stories-and-workspaces.md @@ -14,8 +14,8 @@ scheduled activation to the same story. An idempotency key protects individual s requests from network retries. These are different concerns: the external id identifies the body of work, while the key identifies one requested operation. -A project member can continue a story created by another member. The conversation is shared -project state rather than a private chat transcript. +Any organization member whose role grants it can continue a story created by another member. The +conversation is shared project state rather than a private chat transcript. ## Durable and replaceable state diff --git a/apps/docs/docs/contributors/architecture.md b/apps/docs/docs/contributors/architecture.md index a1a6c43e..65065aee 100644 --- a/apps/docs/docs/contributors/architecture.md +++ b/apps/docs/docs/contributors/architecture.md @@ -47,7 +47,8 @@ similar handwritten versions. ## Main request path -A UI or MCP request enters the API, resolves a principal, checks project scope and permissions, and +A UI or MCP request enters the API, resolves a principal, checks its organization-wide role +permissions and — for a project-scoped API key — that the key's project matches the request, and calls a domain service. Starting or continuing a story persists its message and turn in PostgreSQL. The worker claims the turn, resolves an exact agent manifest, wakes the workspace provider, prepares `.facility.yml`, issues repository credentials, and starts or resumes Claude Code or diff --git a/apps/docs/docs/guides/operate-story.md b/apps/docs/docs/guides/operate-story.md index 2486cc9c..02e2beb2 100644 --- a/apps/docs/docs/guides/operate-story.md +++ b/apps/docs/docs/guides/operate-story.md @@ -64,7 +64,7 @@ Use `facility_get_story` to inspect status and `next_operations`. Use `facility_get_conversation` with its cursor for durable message history. Each agent message is the run's final response; progress messages and logs live in the run's activity (`/turns/:turnId/activity`). The UI renders the same conversation as request-and-response -exchanges and can continue it under the current user's project membership. +exchanges and can continue it under the current user's organization role. The story timeline is the review path across the whole delivery. It shows which agent, model, session, workspace, branch, and initial SHA started each turn; the final SHA, commits, files, and diff --git a/apps/docs/docs/reference/security.md b/apps/docs/docs/reference/security.md index 72b7dff5..2b59db3a 100644 --- a/apps/docs/docs/reference/security.md +++ b/apps/docs/docs/reference/security.md @@ -37,10 +37,12 @@ Facility still enforces: - secret redaction from persisted command events and API responses; and - explicit confirmation and idempotency for durable workspace deletion. -Authorization uses organization membership, roles, and route permissions. Project-scoped API keys -are pinned to one project and cannot enumerate others. Organization administration rejects scoped -keys. Audit events record successful privileged mutations with actor, target, project, and request -id. +Authorization uses organization membership, roles, and route permissions. A human role is +organization-wide: its permissions apply to every project in the organization, and a project +boundary does not contain a person. Project-scoped API keys are pinned to one project and cannot +enumerate others, so a scoped key is the only mechanism that contains a principal to one project. +Organization administration rejects scoped keys. Audit events record successful privileged +mutations with actor, target, project, and request id. GitHub webhook signatures are verified over the raw body before JSON parsing. The installation id must map to one active organization, and delivery ids are deduplicated within that installation. diff --git a/apps/docs/docs/self-host/authentication.md b/apps/docs/docs/self-host/authentication.md index bb07ae71..614e252b 100644 --- a/apps/docs/docs/self-host/authentication.md +++ b/apps/docs/docs/self-host/authentication.md @@ -125,9 +125,17 @@ the API from the deployment secret store. ## Authorization behavior -Any active project maintainer can read and continue the same story. Every project operation checks -organization and project scope. Cross-project lookups return 404 so scoped credentials cannot use -the API as an enumeration oracle. +Human authorization is organization-wide. A role grants its permissions across every project in the +organization, and reading and acting are separate grants: `projects:read` is what reads a story and +its conversation, while `workspaces:execute` is what continues one, opens its preview, or suspends +it. `workspaces:execute` does not imply read access, so a role needs both to follow a story and act +on it. There is no project membership and no project dimension on either: "maintainer on one +repository" cannot be expressed. + +The project dimension exists for API keys. A project-scoped key is pinned to one project, and a +cross-project lookup returns 404 rather than 403 so a key cannot use the API as an enumeration +oracle. Reach for a scoped key when automation should be contained to one project, and note the +trade-off: audit events then record the key rather than a person. Roles and route permissions govern humans and API keys. They do not change an agent manifest into a restricted workspace profile: once a principal is allowed to execute an agent in a project, the @@ -139,13 +147,13 @@ requests remain available in structured service logs and can be correlated with ## Preview authentication Preview cookies are separate from control-plane sessions. A one-time handoff issues an expiring -preview cookie; each proxied request revalidates the user, project membership, workspace, service, -expiry, and revocation status. +preview cookie; each proxied request revalidates the user, organization membership, workspace, +service, expiry, and revocation status. The preview origin must be on a different registered site from Facility control origins. Opening a service creates a one-time handoff; consuming it sets a host-only preview cookie. The preview proxy -rechecks membership on each HTTP request and WebSocket upgrade, so revoking membership, the -session, or the workspace stops continued access. +rechecks organization membership on each HTTP request and WebSocket upgrade, so removing the member, +revoking the session, or deleting the workspace stops continued access. Never send a Facility API key, OAuth token, or control-plane session cookie to the application running inside a preview.