Audit metadata
- Priority: P1
- Estimated effort: L
- Implementation risk: HIGH
- Category: security
- Evidence baseline:
07be5be7ce69bea0c3118744ab90d148b010fce0 (origin/main on 2026-07-17)
Dependencies
None.
Description and impact
Adding a catalog connector enables its entire MCP tool surface for autonomous agent use. That can include issue/PR mutations, social posting, remote commands, and filesystem writes. Neither installation nor execution currently creates an enforceable least-privilege grant or a user confirmation boundary.
Current state
frontend/src/features/settings/connectors-section.tsx:27-59 catalogs GitHub, X posting, and remote shell/filesystem connectors.
frontend/src/features/settings/connectors-section.tsx:175-207 installs and enables connectors without allowTools.
services/agent-runtime/src/connector-pool.ts:40-49 interprets absent allowTools as “allow everything.”
frontend/src/app/api/agent/connectors/call/route.ts:44-64 invokes any allowed tool immediately.
frontend/desktop/resources/pi-extensions/connectors.ts:93-126 registers every inventory tool with a direct executor and no approval exchange.
services/agent-runtime/src/connector-contract.ts:18-57 has an optional allowlist but no risk class, approval mode, or deny-by-default representation.
- MCP annotations originate with the connector process and cannot be treated as trusted policy.
Issue specification
Description
New connectors expose every advertised tool to the agent by default. Mutating and high-impact actions execute without a connector-specific permission grant or per-action user approval.
Steps to reproduce
- Install any catalog connector and leave its generated configuration unchanged.
- Inspect the connector inventory; every advertised tool is registered.
- Invoke a mutating tool, such as a repository mutation, social post, remote command, or file write.
- Observe execution without a scoped connector approval.
Solution design
Make absence of a permission grant deny all tools. Before enablement, show the discovered tool inventory and require an explicit allowlist. Maintain a first-party risk policy for catalog tools; unknown tools default high risk, regardless of MCP annotations. Read-only grants may run automatically. Mutating/high-impact calls require a one-use, short-lived approval scoped to session, connector, tool, and a keyed digest over the full canonical Effect Schema-decoded argument object. Generate a random in-memory HMAC key at broker startup and compute a domain-separated HMAC-SHA-256 over version, session, connector, tool, and deterministic canonical JSON arguments. Redaction/summarization happens only after digesting and only for UI/audit presentation; it must never alter the authorization input. Record bounded metadata for approved/denied actions without secrets or raw arguments.
Acceptance criteria
- A connector with no explicit tool grant exposes and executes no tools.
- New catalog connectors default to a reviewed read-only/minimal grant.
- Users see exact tool permissions and risk tiers before enabling.
- Mutating tools cannot execute without a current, scoped, one-use approval.
- Changing any argument, including a credential or field hidden/redacted in the UI, invalidates the approval.
- Approval digests are keyed, cover the complete canonical decoded argument tree, and are never derived from a redacted/sanitized copy.
- Remote command/filesystem tools receive the highest confirmation tier.
- Unknown/newly advertised tools remain denied until reviewed.
- Approval records never persist credentials or raw sensitive arguments.
Related issues
#200 covers Windows MCP process launch/cleanup. audit proposal 038 covers the deprecated GitHub server artifact. audit proposal 039 covers plugin executable trust. None duplicates this authorization boundary.
Verification commands
| Purpose |
Command |
Expected on success |
| Service tests |
bun test tests/frontend/agent-runtime/connectors-service.test.ts tests/frontend/regression/connector-approval.test.ts |
all permission and approval cases pass |
| Frontend test |
npm --prefix frontend run test |
all script tests pass |
| Frontend quality |
npm --prefix frontend run check:quality |
exit 0 |
| Full repository |
npm run check |
exit 0 |
| Integration |
npm run test:integration |
all tests pass |
Scope
In scope:
services/agent-runtime/src/connector-contract.ts
services/agent-runtime/src/connectors-service.ts
services/agent-runtime/src/connector-pool.ts
services/agent-runtime/src/connector-approval.ts (create)
services/agent-runtime/src/pi-runtime-helpers.ts
frontend/src/app/api/agent/connectors/**
frontend/src/features/settings/connectors-section.tsx
- Connector approval UI/store files under
frontend/src/features/agent/
frontend/desktop/resources/pi-extensions/connectors.ts
tests/frontend/agent-runtime/connectors-service.test.ts
tests/frontend/regression/connector-approval.test.ts (create)
Out of scope:
- Sandboxing arbitrary connector executables.
- Redesigning connector credential storage; audit proposal 050 owns explicit secret fields.
- Migrating the GitHub package; audit proposal 038 owns that artifact change.
- Treating MCP
readOnlyHint as authoritative.
Implementation plan
Step 1: Make permission grants explicit and deny by default
Replace optional allow-all semantics with an explicit permission model decoded by Effect Schema. Migrate existing connectors into a disabled, review-required state unless they already have an explicit allowlist. Inventory may show denied tools for review, but connector-pool must reject their execution server-side.
Verify: service tests → omitted grant, empty grant, unknown tool, changed inventory, and explicit read grant all behave deterministically.
Step 2: Add first-party risk classification and grant UI
Define risk tiers for first-party catalog entries and default all unknown tools to high risk. In Settings, inventory tools before enablement and require a user-selected grant. Use existing UI-kit controls and Schema-decoded API responses; add no useEffect.
Verify: frontend tests → install cannot enable until a grant is saved; newly discovered tools stay denied.
Step 3: Add a scoped approval broker
Implement an in-memory, bounded approval broker with pending, approved, denied, expired, consumed, and cancelled states. Decode the complete call argument tree through the Effect Schema-owned call boundary, rejecting non-JSON/unsupported values before approval. Canonicalize the decoded value deterministically by recursively sorting object keys while preserving array order, JSON scalar types, null, and every field; do not omit or replace secret-looking keys. Bind each request with a domain-separated HMAC-SHA-256 over the canonical arguments plus session, connector, and tool using a random per-process key held only by the broker. On execution, recompute and timing-safely compare that digest before consuming the approval. Only after this authorization value is created may a separate redactor produce a UI preview or audit metadata. The call route must independently enforce approval before invoking mutating/high-risk tools. Use Effect for timeouts and cancellation.
Verify: regression tests → missing, wrong-session, wrong-args, expired, reused, and denied approvals all fail; arguments differing only in a redacted credential, nested field, array order, null/omission, or object insertion order behave correctly; one semantically identical canonical call succeeds once.
Step 4: Connect the Pi extension and UI
Pass an explicit agent session ID into the connector extension. On a protected tool call, create a pending request from the full decoded arguments, then display a separately redacted blocking approval in the agent UI and continue only after approval. Ensure abort/session close cancels pending requests. Log only connector/tool/risk/outcome/timestamps; never reuse the UI/audit projection for digest comparison.
Verify: integration fixture → read tool runs under grant; mutating tool waits; denial stops it; approval executes once; disconnect cancels.
Step 5: Run canonical gates and desktop verification
Run all listed gates. Rebuild/reinstall per AGENTS.md and smoke-test a read-only and a denied mutating connector action; confirm desktop health is 200.
Verify: all commands exit 0 and the packaged approval flow behaves as tested.
Test plan
- Extend connector service tests for migration, allowlists, inventory drift, and deny-by-default behavior.
- Add pure approval-state tests using virtual time.
- Add route tests proving server-side enforcement cannot be bypassed by calling the endpoint directly.
- Add canonicalization/HMAC tests for nested objects, insertion-order equivalence, array-order sensitivity, null versus omission, type differences, and fields normally removed or masked by UI redaction.
- Prove two calls differing only in a synthetic credential value produce different authorization digests while neither value appears in UI/audit output.
- Add an end-to-end fake MCP server with one read and one mutation tool.
- Assert audit output contains no supplied argument values or credentials.
Acceptance criteria
Risks and stop conditions
- The Pi SDK provides a standard approval primitive that can meet all server-side scoping requirements; stop and redesign around it instead of creating a parallel UX.
- Tool risk cannot be classified without a maintainer-approved policy for the first-party catalog.
- Existing connectors cannot be migrated without silently retaining broad write access.
- Approval enforcement would live only in the renderer or extension and could be bypassed through the HTTP route.
- A supported argument value cannot be represented losslessly by the canonical Schema-decoded JSON contract; stop and define its wire representation before approving it.
- The HMAC key would need persistence or exposure to the renderer; keep approvals process-local and redesign the lifecycle instead.
Maintenance considerations
Approval is defense in depth, not a substitute for narrowly scoped provider credentials. Reviewers should verify deny-by-default behavior at the server boundary, full-argument keyed digest coverage before redaction, timing-safe comparison, and that connector-controlled descriptions/annotations never decide authorization.
Generated from Local Studio 2.0 main-branch audit proposal 021.
Audit metadata
07be5be7ce69bea0c3118744ab90d148b010fce0(origin/mainon 2026-07-17)Dependencies
None.
Description and impact
Adding a catalog connector enables its entire MCP tool surface for autonomous agent use. That can include issue/PR mutations, social posting, remote commands, and filesystem writes. Neither installation nor execution currently creates an enforceable least-privilege grant or a user confirmation boundary.
Current state
frontend/src/features/settings/connectors-section.tsx:27-59catalogs GitHub, X posting, and remote shell/filesystem connectors.frontend/src/features/settings/connectors-section.tsx:175-207installs and enables connectors withoutallowTools.services/agent-runtime/src/connector-pool.ts:40-49interprets absentallowToolsas “allow everything.”frontend/src/app/api/agent/connectors/call/route.ts:44-64invokes any allowed tool immediately.frontend/desktop/resources/pi-extensions/connectors.ts:93-126registers every inventory tool with a direct executor and no approval exchange.services/agent-runtime/src/connector-contract.ts:18-57has an optional allowlist but no risk class, approval mode, or deny-by-default representation.Issue specification
Description
New connectors expose every advertised tool to the agent by default. Mutating and high-impact actions execute without a connector-specific permission grant or per-action user approval.
Steps to reproduce
Solution design
Make absence of a permission grant deny all tools. Before enablement, show the discovered tool inventory and require an explicit allowlist. Maintain a first-party risk policy for catalog tools; unknown tools default high risk, regardless of MCP annotations. Read-only grants may run automatically. Mutating/high-impact calls require a one-use, short-lived approval scoped to session, connector, tool, and a keyed digest over the full canonical Effect Schema-decoded argument object. Generate a random in-memory HMAC key at broker startup and compute a domain-separated HMAC-SHA-256 over version, session, connector, tool, and deterministic canonical JSON arguments. Redaction/summarization happens only after digesting and only for UI/audit presentation; it must never alter the authorization input. Record bounded metadata for approved/denied actions without secrets or raw arguments.
Acceptance criteria
Related issues
#200 covers Windows MCP process launch/cleanup. audit proposal 038 covers the deprecated GitHub server artifact. audit proposal 039 covers plugin executable trust. None duplicates this authorization boundary.
Verification commands
bun test tests/frontend/agent-runtime/connectors-service.test.ts tests/frontend/regression/connector-approval.test.tsnpm --prefix frontend run testnpm --prefix frontend run check:qualitynpm run checknpm run test:integrationScope
In scope:
services/agent-runtime/src/connector-contract.tsservices/agent-runtime/src/connectors-service.tsservices/agent-runtime/src/connector-pool.tsservices/agent-runtime/src/connector-approval.ts(create)services/agent-runtime/src/pi-runtime-helpers.tsfrontend/src/app/api/agent/connectors/**frontend/src/features/settings/connectors-section.tsxfrontend/src/features/agent/frontend/desktop/resources/pi-extensions/connectors.tstests/frontend/agent-runtime/connectors-service.test.tstests/frontend/regression/connector-approval.test.ts(create)Out of scope:
readOnlyHintas authoritative.Implementation plan
Step 1: Make permission grants explicit and deny by default
Replace optional allow-all semantics with an explicit permission model decoded by Effect Schema. Migrate existing connectors into a disabled, review-required state unless they already have an explicit allowlist. Inventory may show denied tools for review, but
connector-poolmust reject their execution server-side.Verify: service tests → omitted grant, empty grant, unknown tool, changed inventory, and explicit read grant all behave deterministically.
Step 2: Add first-party risk classification and grant UI
Define risk tiers for first-party catalog entries and default all unknown tools to high risk. In Settings, inventory tools before enablement and require a user-selected grant. Use existing UI-kit controls and Schema-decoded API responses; add no
useEffect.Verify: frontend tests → install cannot enable until a grant is saved; newly discovered tools stay denied.
Step 3: Add a scoped approval broker
Implement an in-memory, bounded approval broker with pending, approved, denied, expired, consumed, and cancelled states. Decode the complete call argument tree through the Effect Schema-owned call boundary, rejecting non-JSON/unsupported values before approval. Canonicalize the decoded value deterministically by recursively sorting object keys while preserving array order, JSON scalar types, null, and every field; do not omit or replace secret-looking keys. Bind each request with a domain-separated HMAC-SHA-256 over the canonical arguments plus session, connector, and tool using a random per-process key held only by the broker. On execution, recompute and timing-safely compare that digest before consuming the approval. Only after this authorization value is created may a separate redactor produce a UI preview or audit metadata. The call route must independently enforce approval before invoking mutating/high-risk tools. Use Effect for timeouts and cancellation.
Verify: regression tests → missing, wrong-session, wrong-args, expired, reused, and denied approvals all fail; arguments differing only in a redacted credential, nested field, array order, null/omission, or object insertion order behave correctly; one semantically identical canonical call succeeds once.
Step 4: Connect the Pi extension and UI
Pass an explicit agent session ID into the connector extension. On a protected tool call, create a pending request from the full decoded arguments, then display a separately redacted blocking approval in the agent UI and continue only after approval. Ensure abort/session close cancels pending requests. Log only connector/tool/risk/outcome/timestamps; never reuse the UI/audit projection for digest comparison.
Verify: integration fixture → read tool runs under grant; mutating tool waits; denial stops it; approval executes once; disconnect cancels.
Step 5: Run canonical gates and desktop verification
Run all listed gates. Rebuild/reinstall per AGENTS.md and smoke-test a read-only and a denied mutating connector action; confirm desktop health is 200.
Verify: all commands exit 0 and the packaged approval flow behaves as tested.
Test plan
Acceptance criteria
if (!connector.allowTools) return toolsno longer exists.Risks and stop conditions
Maintenance considerations
Approval is defense in depth, not a substitute for narrowly scoped provider credentials. Reviewers should verify deny-by-default behavior at the server boundary, full-argument keyed digest coverage before redaction, timing-safe comparison, and that connector-controlled descriptions/annotations never decide authorization.
Generated from Local Studio 2.0 main-branch audit proposal 021.