feat: Add organization and workspace API key management#2347
feat: Add organization and workspace API key management#2347daryllimyt merged 76 commits intomainfrom
Conversation
There was a problem hiding this comment.
7 issues found across 41 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/auth/types.py">
<violation number="1" location="tracecat/auth/types.py:45">
P2: `api_key_kind` has exactly two valid values (`"organization"`, `"workspace"`) but is typed as bare `str | None`. Use `Literal["organization", "workspace"] | None` to catch invalid values at the type level.</violation>
</file>
<file name="frontend/src/app/organization/settings/workspaces/[workspaceId]/page.tsx">
<violation number="1" location="frontend/src/app/organization/settings/workspaces/[workspaceId]/page.tsx:55">
P2: Including `workspace:api_key:read` in this page-level allow condition grants access to full workspace settings UI, not just API key read functionality. Restrict this route to workspace/org update scopes (or split API key views to a separate route/component).</violation>
</file>
<file name="tracecat/api_keys/router.py">
<violation number="1" location="tracecat/api_keys/router.py:49">
P1: The fallback in `_translate_error` returns `str(exc)` for all unexpected exceptions, which can leak internal details (e.g., database constraint names, internal paths) to API clients. Return a generic message for the catch-all case and log the actual error server-side.
(Based on your team's feedback about error handling posture.) [FEEDBACK_USED]</violation>
</file>
<file name="tracecat/auth/credentials.py">
<violation number="1" location="tracecat/auth/credentials.py:133">
P2: Use `is not None` instead of truthiness when checking `role.scopes`. An empty `frozenset()` (resolved zero scopes) is falsy and would be discarded by `or`, collapsing it with `None` (unresolved). This matters for distinguishing "no scopes resolved" from "scopes not yet computed."
(Based on your team's feedback about checking `role.scopes is not None` instead of using truthiness.) [FEEDBACK_USED]</violation>
</file>
<file name="tracecat/api_keys/service.py">
<violation number="1" location="tracecat/api_keys/service.py:47">
P2: `TracecatAuthorizationError` is semantically wrong for an invalid cursor value. This is a validation error (bad input), not an authorization failure. Use `ValueError` so the router maps it to HTTP 400 instead of a 403.</violation>
<violation number="2" location="tracecat/api_keys/service.py:74">
P1: Reverse pagination returns wrong results because `ORDER BY` is always DESC. The existing workflow pagination pattern flips to ASC when `params.reverse` is True so that `LIMIT` picks the items closest to the cursor. Without this, the previous-page query returns items from the wrong end of the filtered set.</violation>
</file>
<file name="frontend/src/components/organization/api-keys-manager.tsx">
<violation number="1" location="frontend/src/components/organization/api-keys-manager.tsx:574">
P2: `AlertDialogAction` auto-closes the dialog on click, so the `disabled={revokePending}` guard and "Revoking..." loading text are never visible. Call `e.preventDefault()` to keep the dialog open while the async revoke runs; it will close when `handleRevoke` sets `revokeTarget` to `null`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d60370e7d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
1 issue found across 10 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/api_keys/router.py">
<violation number="1" location="tracecat/api_keys/router.py:58">
P2: Use sanitized error logging instead of `logger.exception` on this public API error path to avoid recording full traceback details in production logs.
(Based on your team's feedback about sanitized logging on public API exception paths.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d501c3c88
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 918c8a9383
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
9ead194 to
0141368
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
10 issues found across 61 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/unit/test_role_headers.py">
<violation number="1" location="tests/unit/test_role_headers.py:221">
P2: This unit test now triggers a real DB lookup because the mocked headers include a workspace ID but omit `x-tracecat-role-organization-id`. Add the org header (or monkeypatch `_get_workspace_org_id`) to keep the test isolated.</violation>
</file>
<file name="tracecat/audit/types.py">
<violation number="1" location="tracecat/audit/types.py:35">
P2: `service_account_api_key` audit events use the parent service-account ID as `resource_id`, so key rotation/revocation events cannot identify the actual API key record.</violation>
</file>
<file name="tracecat/workspaces/router.py">
<violation number="1" location="tracecat/workspaces/router.py:58">
P1: This scope decorator blocks regular workspace members from listing their own workspaces. `/workspaces` is an org-scoped request, so workspace-scoped `workspace:read` grants are not present during dependency resolution and the membership-based fallback never runs.</violation>
</file>
<file name="tracecat/workflow/store/router.py">
<violation number="1" location="tracecat/workflow/store/router.py:90">
P2: Allowing `WorkspaceActorRole` here makes workspace API keys hit a downstream `org:settings:read` check they can never satisfy, so these sync endpoints will still 403 for API-key callers.</violation>
</file>
<file name="frontend/src/components/organization/service-accounts-manager.tsx">
<violation number="1" location="frontend/src/components/organization/service-accounts-manager.tsx:200">
P2: `openCreateDialog` is missing from the `useEffect` dependency array. Because the function is recreated every render and isn't wrapped in `useCallback`, simply adding it to deps would re-fire the effect each render. Inline the calls or memoize the function to satisfy exhaustive-deps correctly.
(Based on your team's feedback about avoiding non-stable object references in useEffect deps.) [FEEDBACK_USED]</violation>
</file>
<file name="frontend/src/components/organization/org-settings-service-accounts.tsx">
<violation number="1" location="frontend/src/components/organization/org-settings-service-accounts.tsx:45">
P2: Gate organization service-account actions by org write scopes; the manager currently enables all mutating controls by default for read-only users.</violation>
</file>
<file name="tracecat/db/models.py">
<violation number="1" location="tracecat/db/models.py:973">
P3: Tautological check constraint: `organization_id` is `NOT NULL`, so `organization_id IS NOT NULL` is always true, making the entire constraint `workspace_id IS NULL OR TRUE` — always satisfied. If the intent is to enforce "workspace requires org", that's already guaranteed by the NOT NULL column. Consider removing the no-op constraint to avoid misleading future readers, or if the real intent was something else (e.g., workspace must belong to the same org), express that instead.</violation>
</file>
<file name="frontend/src/app/workspaces/[workspaceId]/service-accounts/page.tsx">
<violation number="1" location="frontend/src/app/workspaces/[workspaceId]/service-accounts/page.tsx:14">
P2: Wait for `useScopeCheck` to resolve before rendering this page. While it is still `undefined`, the child component mounts and issues workspace service-account queries before unauthorized users are sent to the 404.</violation>
</file>
<file name="tracecat/service_accounts/router.py">
<violation number="1" location="tracecat/service_accounts/router.py:213">
P1: All remaining mutation endpoints (disable, enable, regenerate-key, revoke-key) for both org and workspace routers are also missing `@require_scope` decorators. These should be gated behind appropriate scopes (e.g., `org:service_account:update` or a dedicated `org:service_account:manage` scope) consistent with the read-endpoint pattern.</violation>
</file>
<file name="tracecat/service_accounts/service.py">
<violation number="1" location="tracecat/service_accounts/service.py:61">
P2: `has_more` and `has_previous` must be swapped along with the cursors when `reverse=True`. Other pagination implementations in the codebase (`tables/service.py`, `cases/rows/service.py`) correctly swap both pairs. Without this, backward pagination returns incorrect `has_more`/`has_previous` flags to the client.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a980fdf5a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
10 issues found across 28 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/cases/dropdowns/router.py">
<violation number="1" location="tracecat/cases/dropdowns/router.py:37">
P1: Switching these endpoints to `WorkspaceActorRole` opens part of the case API to API keys while the rest of case management is still user-only. That lets service accounts mutate/read case dropdown data through a narrower path that bypasses the existing actor restriction.</violation>
</file>
<file name="tracecat/cases/tag_definitions/router.py">
<violation number="1" location="tracecat/cases/tag_definitions/router.py:49">
P1: This delete route is authorized with `case:update`, which bypasses the existing `case:delete` privilege boundary for case resources.</violation>
</file>
<file name="tracecat/workflow/management/router.py">
<violation number="1" location="tracecat/workflow/management/router.py:214">
P2: Allowing `WorkspaceActorRole` here drops revocation attribution for service-account callers, because they always have `user_id=None` and this handler records `revoked_by` from `role.user_id`.</violation>
</file>
<file name="tracecat/workspaces/router.py">
<violation number="1" location="tracecat/workspaces/router.py:61">
P1: Keep `create_workspace` on a user-only org dependency. `OrgActorRole` admits API keys, so service-account credentials with `workspace:create` can now create new workspaces through this org-level route.</violation>
</file>
<file name="tracecat/settings/router.py">
<violation number="1" location="tracecat/settings/router.py:6">
P1: Switching these settings routes to `OrgActorRole` makes organization API keys eligible to read and mutate organization-wide settings, including SAML and audit configuration. Keep these endpoints user-only unless you explicitly want machine credentials to control auth and audit state.</violation>
</file>
<file name="tracecat/cases/durations/router.py">
<violation number="1" location="tracecat/cases/durations/router.py:38">
P1: This endpoint now grants write-capable sync behavior to callers with only `case:read`. Because `list_case_durations` commits `sync_case_durations(...)`, a read-scoped actor can still mutate case duration state.</violation>
</file>
<file name="tracecat/organization/router.py">
<violation number="1" location="tracecat/organization/router.py:72">
P0: Keep organization deletion user-only. Changing this dependency to `OrgActorRole` lets org API keys with `org:delete` remove the entire organization.</violation>
</file>
<file name="tracecat/workspaces/service.py">
<violation number="1" location="tracecat/workspaces/service.py:244">
P2: This authorization failure is unhandled on `/workspaces/search`, so the new `TracecatAuthorizationError` path becomes a 500 response instead of a forbidden result.
(Based on your team's feedback about centralizing translation of known authorization/domain errors into sanitized client responses.) [FEEDBACK_USED]</violation>
</file>
<file name="tracecat/workflow/executions/router.py">
<violation number="1" location="tracecat/workflow/executions/router.py:367">
P1: Keep the draft-execution route user-only; switching it to `WorkspaceActorRole` lets API keys run unpublished workflow graphs under the normal `workflow:execute` scope.</violation>
</file>
<file name="tracecat/vcs/router.py">
<violation number="1" location="tracecat/vcs/router.py:31">
P2: `/github/manifest` now allows API-key actors, but the follow-up `/github/install` callback is still user-only, so service accounts can start a manifest flow they cannot complete.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
https://github.com/TracecatHQ/tracecat/blob/29e58442ca5ecc108b275ad1910942b55fef412a/tracecat/workspaces/router.py#L126
Handle authorization errors in workspace search route
Catch and translate TracecatAuthorizationError here the same way list_workspaces does. WorkspaceService.search_workspaces() raises this error for non-privileged service-account actors without a bound workspace (tracecat/workspaces/service.py), which is reachable when an organization-scoped API key has workspace:read (allowed by the route’s require_scope(..., require_all=False)) but not org:workspace:read; without a handler, that valid request path returns a 500 instead of a 403.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
10 issues found across 132 files (changes from recent commits).
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="frontend/src/components/nav/builder-nav.tsx">
<violation number="1" location="frontend/src/components/nav/builder-nav.tsx:502">
P2: `Run` can submit the default `{}` payload before the persisted trigger payload has been restored, so the first execution after opening a workflow may run with the wrong inputs.</violation>
</file>
<file name="frontend/src/app/organization/page.tsx">
<violation number="1" location="frontend/src/app/organization/page.tsx:4">
P2: This redirect makes `/organization` land on an entitlement-locked page for orgs without `custom_registry`. Use an always-available organization settings route as the default landing page.</violation>
</file>
<file name="frontend/src/components/dashboard/workflows-dashboard.tsx">
<violation number="1" location="frontend/src/components/dashboard/workflows-dashboard.tsx:1020">
P2: A failure after `createWorkflow` leaves a partial duplicate behind while showing "Duplicate failed". Users can retry and accidentally create orphaned or half-tagged copies.</violation>
</file>
<file name="frontend/src/components/settings/profile-settings.tsx">
<violation number="1" location="frontend/src/components/settings/profile-settings.tsx:46">
P2: Wait for the profile update to succeed before clearing the local draft; otherwise failed saves silently discard the edited name.</violation>
<violation number="2" location="frontend/src/components/settings/profile-settings.tsx:55">
P2: Skip the Enter-to-blur behavior while IME composition is active, or name entry can be interrupted for composed languages.</violation>
</file>
<file name="frontend/src/providers/builder.tsx">
<violation number="1" location="frontend/src/providers/builder.tsx:309">
P2: Clearing the debounce on cleanup drops the latest trigger-payload edits if the user leaves the workflow within 250ms.</violation>
</file>
<file name="frontend/src/components/settings/workspace-general-settings.tsx">
<violation number="1" location="frontend/src/components/settings/workspace-general-settings.tsx:124">
P2: The delete confirmation text persists across close/reopen because the dialog stays mounted, which weakens the re-type safeguard for this destructive action.</violation>
</file>
<file name="frontend/src/components/agents/agents-table.tsx">
<violation number="1" location="frontend/src/components/agents/agents-table.tsx:409">
P2: Block duplicate requests globally; the current per-row guard allows concurrent duplications and races navigation.</violation>
</file>
<file name="frontend/src/components/settings/workspace-runtime-settings.tsx">
<violation number="1" location="frontend/src/components/settings/workspace-runtime-settings.tsx:68">
P2: Use nullish coalescing here; `|| undefined` drops a saved `0`, so the form cannot display or round-trip a valid persisted timeout value.</violation>
<violation number="2" location="frontend/src/components/settings/workspace-runtime-settings.tsx:109">
P2: This field defines a validation error message but never renders `FormMessage`, so invalid timeout values fail without any visible explanation.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f2dd75753
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
6f2dd75 to
fa9a497
Compare
There was a problem hiding this comment.
3 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/workspaces/router.py">
<violation number="1" location="tracecat/workspaces/router.py:58">
P1: Requiring only `org:read` here locks workspace-scoped API keys out of the workspace list/search endpoints; they only carry `workspace:read`.</violation>
</file>
<file name="tracecat/workflow/graph/router.py">
<violation number="1" location="tracecat/workflow/graph/router.py:27">
P1: Removing `@require_scope("workflow:read")` bypasses RBAC scope enforcement on the graph read endpoint. Every other workflow router endpoint retains its scope check. Any authenticated workspace actor—including service accounts that were intentionally granted only narrow scopes—can now read workflow graphs without `workflow:read` permission.</violation>
<violation number="2" location="tracecat/workflow/graph/router.py:49">
P0: Removing `@require_scope("workflow:update")` bypasses RBAC scope enforcement on the graph mutation endpoint. This allows any authenticated workspace actor—including service accounts without `workflow:update` permission—to modify workflow graphs via PATCH.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25a2348c93
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
1 issue found across 21 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/workflow/management/router.py">
<violation number="1">
P1: Restricting only the revoke route leaves webhook API-key deletion callable by workspace API keys.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 74bbaa3f8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17e684507f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8c29dd889a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58efb9c4b2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Checklist
uv run pytest tests)?pre-commit run --all-files)?Description
This PR adds organization-level and workspace-level API key management.
Backend changes add managed API key models, migration, auth credential handling, routes, scopes, and audit metadata so API keys can be issued, updated, revoked, and authenticated with the correct org/workspace context.
Frontend changes add API key settings pages and management flows for organization and workspace contexts. The workspace API key page was aligned to the same collection-page structure used by workflows and case management, and the edit dialog now uses the RBAC scope-selection UI pattern.
Validation run for this branch:
uv run pytest tests/unit/api/test_api_api_keys.py tests/unit/test_auth_api_keys.py tests/unit/test_auth_credentials_api_keys.py tests/unit/test_role_headers.pyuv run ruff check .uv run basedpyright tracecat/api_keys tracecat/auth/api_keys.py tracecat/auth/credentials.py tracecat/auth/dependencies.py tracecat/auth/types.py tracecat/authz/scopes.py tracecat/authz/seeding.py tracecat/db/models.py tracecat/db/tenant_rls.py tracecat/api/app.py tracecat/workspaces/router.py tracecat/agent/router.py tracecat/audit/enums.py tracecat/audit/logger.py tracecat/audit/service.py tracecat/audit/types.py alembic/versions/548aa7691799_add_api_key_tables.py tests/unit/api/conftest.py tests/unit/api/test_api_api_keys.py tests/unit/test_auth_api_keys.py tests/unit/test_auth_credentials_api_keys.py tests/unit/test_role_headers.pypnpm -C frontend run typecheckRelated Issues
None.
Screenshots / Recordings
Not included. The workspace API keys page and edit dialog intentionally match the existing workflows, case management, and RBAC permission-selection UI patterns.
Steps to QA
API keys.API keys.Summary by cubic
Adds organization- and workspace‑scoped service accounts with managed API keys so machines can call actor‑safe routes under RBAC. Includes pages to issue, rotate, and revoke keys, plus tighter auth via
x-tracecat-api-key.New Features
tc_org_sk_…/tc_ws_sk_…) with CRUD, issue/rotate/revoke, cursor pagination, and audit on service account and API key resources; org/workspace routers added; organization domains gain verification fields.WorkspaceActorRole/OrgActorRolefor API‑key auth on actor‑safe org/workspace routes; managed key parsing/verification and headerx-tracecat-api-key; workspace listing useslist_accessible_workspaces.Bug Fixes and Refactors
workflow:readfor graph reads andworkflow:updatefor graph mutations; extend guards across cases, integrations, editor, workflows, executions, schedules, and tags; tighten case tag routes tocase:read/case:update; guard org entitlement reads by scope; keep org deletion/settings/agent, GitHub manifest, draft execution, webhook API‑key revocation, webhook secret reads, and workflow detail user‑only; allow org members to list workspaces.Written for commit f681f0b. Summary will update on new commits.