diff --git a/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/permission-boundary-revision.md b/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/permission-boundary-revision.md new file mode 100644 index 00000000..a2de3645 --- /dev/null +++ b/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/permission-boundary-revision.md @@ -0,0 +1,30 @@ +# Decision: narrow plugin permissions to host authorities + +Plugin/Feature permission should mean user-approved host authority, not every contribution the feature declares. + +Purpose: + +- Explain to the user what dangerous host authority a plugin receives. +- Ensure sandboxed/external plugin code is not given APIs or handles for unapproved actions. +- Keep registry contribution integrity separate from authority grants. + +Revised model: + +- Contributions are descriptor/digest-locked declarations: + - Tools + - Hooks + - BackgroundTasks + - Service providers +- Host authorities are user-approved sandbox/object-capability grants: + - filesystem access + - network access + - secret refs + - model-visible durable notification/history append + - Pod management façade access where exposed + - store/state access such as Memory/WorkItem or persistent plugin state where applicable + +Tool/Hook/BackgroundTask/ServiceProvider declarations should be shown during install and locked by plugin descriptor/package digest. If they change, the plugin requires re-approval. They do not need separate `ContributeTool`, `ContributeHook`, `RunBackgroundTask`, or `ProvideService` authority variants. + +Service consumption is also not a blanket authority by itself. A service requirement is resolved by the host; the authority question depends on what host authority the service handle exposes. Service handles should be narrowed or authority-bound so acquiring one broad service handle cannot become an authority escalation path. + +Tool execution remains governed by the existing per-call tool permission / PreToolCall path. Feature authority grants do not replace manifest/tool permissions. diff --git a/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/pod-api-design.md b/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/pod-api-design.md index 6fad398a..74411e49 100644 --- a/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/pod-api-design.md +++ b/work-items/open/20260603-122317-plugin-feature-contribution-registry/artifacts/pod-api-design.md @@ -2,7 +2,7 @@ ## 1. Summary recommendation -Introduce a `pod::feature` public API as the single Pod-side registration layer for built-in features and future external plugins. A feature module should declare its identity, requested capabilities, service dependencies, service exports, and contributions, then install those capabilities only through typed host registrars for existing Pod/Worker surfaces: `ToolRegistry`, the hardened safe `pod::hook` surface, host-managed background tasks, host-mediated service registry, and host-owned notification/history/alert/diagnostic paths. +Introduce a `pod::feature` public API as the single Pod-side registration layer for built-in features and future external plugins. A feature module should declare its identity, contributions, service dependencies/exports, and requested host authorities. Contributions are displayed and locked by descriptor/digest; host authorities are the user-approved sandbox/object-capability grants such as filesystem, network, secrets, Pod management, and model-visible durable notification/history append. The registry should not become a second runtime, a plugin dispatcher tool, or a generic `Pod` mutation escape hatch. Feature state remains inside the feature module; the Pod owns only install metadata, diagnostics, granted host handles, and normal durable session/runtime surfaces. @@ -32,7 +32,7 @@ The design should build on these existing surfaces rather than bypassing them: - `crates/pod/src/permission.rs` - Manifest tool permissions are enforced as a `PreToolCallHook`. - - Feature tools must remain subject to the same PreToolCall permission path. Feature capability grants do not replace per-call tool permission. + - Feature tools must remain subject to the same PreToolCall permission path. Feature authority grants do not replace per-call tool permission. - `crates/llm-worker/src/tool.rs` and `crates/llm-worker/src/tool_server.rs` - `ToolDefinition`, `Tool`, `ToolMeta`, `ToolResult`, `ToolOutput`, and `ToolServerHandle` define the normal tool execution path. @@ -69,7 +69,7 @@ pub mod feature { pub mod tool; pub use background::{BackgroundTaskContribution, BackgroundTaskRegistrar}; - pub use capability::{CapabilityGrantSet, CapabilityRequest, HostCapability}; + pub use capability::{AuthorityGrantSet, AuthorityRequest, HostAuthority}; pub use diagnostic::{FeatureDiagnostic, FeatureInstallReport}; pub use notify::{FeatureAlertSink, FeatureNotifySink}; pub use registry::{FeatureDescriptor, FeatureId, FeatureInstallContext, FeatureModule, FeatureRuntimeKind}; @@ -92,7 +92,7 @@ pub struct FeatureDescriptor { pub display_name: String, pub version: Option, pub runtime: FeatureRuntimeKind, // Builtin, ExternalProcess, McpBridge, WasmPlaceholder, DeclarativePlaceholder - pub requested_capabilities: Vec, + pub requested_authorities: Vec, pub provides_services: Vec, pub requires_services: Vec, pub declared_tools: Vec, @@ -111,7 +111,7 @@ pub enum FeatureRuntimeKind { pub struct FeatureInstallContext<'a> { // No Pod or Worker reference. pub feature_id: &'a FeatureId, - pub grants: &'a CapabilityGrantSet, + pub grants: &'a AuthorityGrantSet, pub tools: ToolRegistrar<'a>, pub hooks: PublicHookRegistrar<'a>, pub services: FeatureServiceProvider<'a>, @@ -128,7 +128,7 @@ Important details: - `FeatureDescriptor` is declarative and serializable. It is safe to show in diagnostics, profile previews, and `ListFeatures`-style future tooling. - `FeatureModule::install` is runtime code that wires stateful tool/hook implementations into host registrars. - `FeatureInstallContext` must not expose `Pod`, `Worker`, raw `ToolServerHandle`, raw `Interceptor`, raw `NotifyBuffer`, raw `LogWriter`, raw `event_tx`, or direct history mutation. -- `FeatureServiceProvider` returns only host-mediated services backed by granted capabilities and resolved service dependencies, for example scoped filesystem access, WorkItem store access, memory access, Pod orchestration handles, web provider handles, secret references, or another feature's declared public service. It should return `Denied`/`Unavailable` diagnostics instead of exposing partial internals. +- `FeatureServiceProvider` returns only host-mediated services backed by granted host authorities and resolved service dependencies, for example scoped filesystem access, WorkItem store access, memory access, Pod orchestration handles, web provider handles, secret references, or another feature's declared public service. It should return `Denied`/`Unavailable` diagnostics instead of exposing partial internals. - `FeatureServiceRegistrar` lets a feature expose a narrow public service API to other features. This is an extension boundary for future plugin-to-plugin APIs, not a requirement to move already-implemented core behavior out of the host. ### Example registration snippet @@ -137,8 +137,8 @@ This is illustrative shape, not proposed final exact Rust syntax: ```rust use pod::feature::{ - CapabilityRequest, FeatureDescriptor, FeatureId, FeatureInstallContext, - FeatureModule, FeatureRuntimeKind, HostCapability, ToolContribution, + AuthorityRequest, FeatureDescriptor, FeatureId, FeatureInstallContext, + FeatureModule, FeatureRuntimeKind, HostAuthority, ToolContribution, }; pub struct WorkItemFeature { @@ -150,22 +150,18 @@ impl FeatureModule for WorkItemFeature { FeatureDescriptor::builder(FeatureId::builtin("work-item")) .display_name("WorkItem intake and routing") .runtime(FeatureRuntimeKind::Builtin) - .request(CapabilityRequest::required( - HostCapability::WorkItemStore { read: true, write: true }, + .request(AuthorityRequest::required( + HostAuthority::WorkItemStore { read: true, write: true }, "create and update WorkItem records through host-owned ticket storage", )) - .request(CapabilityRequest::optional( - HostCapability::EmitModelNotification, + .request(AuthorityRequest::optional( + HostAuthority::ModelNotification, "commit user-action-required notices through the existing Notify/SystemItem path when the model should see them", )) - .request(CapabilityRequest::optional( - HostCapability::EmitAlert, + .request(AuthorityRequest::optional( + HostAuthority::Alert, "surface short transient human-facing warnings when the watcher fails", )) - .request(CapabilityRequest::optional( - HostCapability::RunBackgroundTask, - "watch the WorkItem store for state changes without ad hoc tokio::spawn usage", - )) .tool("WorkItemCreate") .tool("WorkItemComment") .hook("work_item_intake_pre_tool_audit", pod::hook::HookPoint::PreToolCall) @@ -206,7 +202,6 @@ pub struct ToolContribution { pub feature_id: FeatureId, pub name: ToolName, pub definition: llm_worker::ToolDefinition, - pub required_capabilities: Vec, } ``` @@ -218,7 +213,7 @@ Rules: - Duplicate tool names should be rejected during feature registry preflight with a diagnostic, not discovered later through a panic or undefined ordering. - Public feature identity should be source-qualified (`builtin:memory`, `project:foo`, `plugin::bar`), while model-visible tool names should remain explicit stable names. Do not auto-prefix model tool names unless the project deliberately chooses a future namespacing policy. - Tool schemas/descriptions must be part of the normal `ToolDefinition` path so model-visible surfaces remain inspectable and bounded. -- If a required host service is not granted or configured, the tool should not be registered; the install report should explain the skipped contribution. +- If a required host authority or service is not granted or configured, the tool should not be registered; the install report should explain the skipped contribution. ### Hook contribution @@ -265,9 +260,9 @@ Recommended behavior: - `FeatureNotifySink::notify_model(...)` is the feature-facing form of the existing `Method::Notify` / `SystemItem::Notification` path. It creates a model-visible, history-backed notification by asking the host to commit a `LogEntry::SystemItem`; clients then see the same committed item through `Event::SystemItem`. Disk-side and wire-side remain 1:1. - `FeatureAlertSink::alert(...)` emits a short human-facing transient alert equivalent to `Event::Alert`. It is UI/person-facing only: not model-visible, not session history, not replayed as an input, and not a structured UI extension channel. -- `FeatureDiagnosticSink` records install/runtime/capability/task diagnostics for host logs, future `ListFeatures`, profile validation, and TUI diagnostics. Diagnostics are host-defined records, not arbitrary client-rendered UI payloads. +- `FeatureDiagnosticSink` records install/runtime/authority/task diagnostics for host logs, future `ListFeatures`, profile validation, and TUI diagnostics. Diagnostics are host-defined records, not arbitrary client-rendered UI payloads. - Dialogs, confirmations, and other interactive client UI are out of scope for the base API. If needed later, add a separate host-defined interaction protocol; do not use a generic event channel for it. -- Model-visible notifications, transient alerts, and diagnostics should require distinct capabilities such as `EmitModelNotification`, `EmitAlert`, and diagnostic/reporting rights where policy needs them. +- Model-visible notifications require a host authority grant because they become durable model/history-visible input. Transient alerts and diagnostics are host-defined reporting surfaces; normally control them with descriptor approval, host policy, size bounds, and rate limits rather than treating them as sandbox authorities. - Background feature tasks must use these sinks; they must not hold raw log writers, raw event senders, or append directly to history. ### Background task contribution @@ -284,7 +279,7 @@ pub struct BackgroundTaskContribution { pub activation: TaskActivation, pub restart: RestartPolicy, pub shutdown: ShutdownPolicy, - pub required_capabilities: Vec, + pub requested_authorities: Vec, } pub struct FeatureTaskContext<'a> { @@ -299,13 +294,13 @@ pub struct FeatureTaskContext<'a> { Rules: - The host starts, tracks, cancels, and reports background tasks. Feature modules register tasks; they do not spawn untracked runtime loops. -- Task output is limited to granted sinks/services: model-visible notification through `FeatureNotifySink`, human transient alert through `FeatureAlertSink`, diagnostics through `FeatureDiagnosticSink`, and host-granted service operations. +- Task output is limited to granted authorities, sinks, and services: model-visible notification through `FeatureNotifySink` only with a model-notification authority grant, human transient alert through `FeatureAlertSink`, diagnostics through `FeatureDiagnosticSink`, and host-granted service operations. - The host records task lifecycle/status in install/runtime diagnostics. Do not expose arbitrary feature-defined event channels for task progress. - Initial activation can be conservative (`PodLifetime` and/or `OnDemand`); restart policy may start with `Never`, but the API should make shutdown/cancellation behavior explicit from the beginning. ### Service contribution and dependency -Add a host-mediated service registry so a feature/plugin can publish a narrow API and another feature/plugin can depend on that API without importing the provider's concrete implementation or bypassing capability policy. +Add a host-mediated service registry so a feature/plugin can publish a narrow API and another feature/plugin can depend on that API without importing the provider's concrete implementation or bypassing authority policy. This is not a plan to move existing implemented core features out of the host immediately. Core-backed APIs may remain core-backed. The service form exists so future detachable features can depend on stable public interfaces rather than private Pod internals or another plugin's concrete state. @@ -337,13 +332,13 @@ pub struct FeatureServiceProvider<'a> { /* host-owned */ } Rules: -- A feature may provide a service through `FeatureServiceRegistrar`, and consumers may obtain the service only through `FeatureServiceProvider` after host dependency resolution and capability grant checks. +- A feature may provide a service through `FeatureServiceRegistrar`, and consumers may obtain the service only through `FeatureServiceProvider` after host dependency resolution and authority grant checks. - Features must not directly hold another feature's concrete module/state unless that handle was returned by the host service registry. - Service identity is source-independent interface identity (`yoi.memory.v1`, `yoi.pod-management.v1`, `project.issue-tracker.v1`), while provider identity remains a `FeatureId`. - Service dependencies are resolved during feature registry preflight. Required missing services skip the consumer feature with diagnostics; optional missing services install the consumer in a degraded mode if its installer supports that. - Service dependency cycles are rejected initially. Late-bound/cyclic service handles are out of scope until a real need appears. - In-process built-in services may use Rust trait objects internally. External plugin/WASM/MCP services should be represented by host-side proxies that implement the same service interface boundary; do not expose raw foreign runtime handles. -- Service handles should be capability-bound. Prefer narrowed interfaces such as read-only vs write-capable services, or enforce caller/grant checks through a host-provided service call context. +- Service handles should be authority-bound. Prefer narrowed interfaces such as read-only vs write-capable services, or enforce caller/grant checks through a host-provided service call context. Examples: @@ -351,34 +346,32 @@ Examples: - `builtin:pod-orchestration` may provide `yoi.pod-management.v1` and contribute Pod management tools. The actual Pod lifecycle/scope authority remains host-owned; the service is a controlled façade. - A future issue-tracker plugin may provide `project.issue-tracker.v1`, while WorkItem tooling consumes that service without knowing the concrete plugin package. -### Capability request/grant/diagnostics +### Host authority request/grant/diagnostics -Capabilities are requested by descriptors and granted by the host. A feature may request a capability, but it must not assume the capability exists. +Authority grants are the user-approved sandbox/object-capability boundary. They control which host-provided APIs or handles a feature receives. They are not a mirror of every contribution a feature declares. -Initial capability categories: +Contributions such as tools, hooks, background tasks, and service providers should be displayed and locked by descriptor/digest at install time. They do not need separate `ContributeTool` / `ContributeHook` / `RunBackgroundTask` / `ProvideService` authority variants. If a plugin changes its contributed tools/hooks/tasks/services, the descriptor or package hash changes and the user must re-approve the changed plugin. + +Initial authority categories: ```rust -pub enum HostCapability { - ContributeTool { name: ToolName }, - ContributeHook { point: pod::hook::HookPoint }, - RunBackgroundTask, - ProvideService { id: ServiceId }, - UseService { id: ServiceId, access: ServiceAccess }, - EmitModelNotification, - EmitAlert, - ScopedFs { read: bool, write: bool, execute: bool }, +pub enum HostAuthority { + Fs(FsGrant), + Network(NetworkGrant), + SecretRef { id: String }, + ModelNotification, // durable Notify/SystemItem/Event::SystemItem path WorkItemStore { read: bool, write: bool }, MemoryStore { read: bool, write: bool }, PodManagement { spawn: bool, message: bool, restore: bool }, - Network { purpose: NetworkPurpose }, - SecretRef { id: String }, + PersistentPluginState { read: bool, write: bool }, } ``` Important separation: -- Capability grants decide whether a feature may install and receive host services. -- Tool permissions decide whether an installed tool call may execute for a specific Pod/run. +- Descriptor/contribution approval decides whether a feature with this digest may be installed with its declared tools/hooks/tasks/services. +- Authority grants decide whether a feature may receive dangerous host handles such as filesystem, network, secret, Pod-management, store, or model-visible notification access. +- Tool permissions decide whether an installed model-visible tool call may execute for a specific Pod/run. - Scope permissions decide which filesystem paths or delegated Pod capabilities a host service may touch. Diagnostics should be first-class: @@ -387,8 +380,8 @@ Diagnostics should be first-class: pub struct FeatureInstallReport { pub feature_id: FeatureId, pub enabled: bool, - pub granted: Vec, - pub denied: Vec, + pub granted: Vec, + pub denied: Vec, pub installed_tools: Vec, pub installed_hooks: Vec, pub installed_background_tasks: Vec, @@ -425,7 +418,7 @@ Public features/plugins must not be able to perform these operations: - Access raw `Worker`, raw `Pod`, raw `ToolServerHandle`, raw `llm_worker::Interceptor`, raw `NotifyBuffer`, raw session log writer, or raw event sender. - Register tools outside `ToolRegistry` or bypass normal tool-result history recording. - Bypass `PreToolCall` permission policy. -- Grant themselves capabilities or infer grants from successful construction. +- Grant themselves host authorities or infer grants from successful construction. - Mutate manifest/profile/scope state directly. - Perform filesystem/process/network/secret access outside granted host services. - Depend on another feature/plugin by concrete implementation, private state, raw process handle, or direct module pointer instead of a host-resolved service interface. @@ -444,7 +437,7 @@ Recommended placement: - public feature traits/types - feature registry builder - install reports/diagnostics - - capability request/grant model + - authority request/grant model - typed registrars/sinks - service registry, service declarations, and dependency resolution reports @@ -467,10 +460,10 @@ Recommended placement: Install location in Pod startup: -1. Resolve manifest/profile and host capability policy. +1. Resolve manifest/profile and host authority policy. 2. Construct `Pod` and internal safety surfaces. 3. Install host/internal hooks such as manifest permission enforcement. -4. Build enabled feature descriptors, collect declared service providers/requirements, resolve the service dependency DAG, and compute capability grants. +4. Build enabled feature descriptors, collect declared service providers/requirements, resolve the service dependency DAG, and compute authority grants. 5. Install enabled feature modules through `FeatureRegistryBuilder`, including service exports, tools, hooks, and background task declarations. 6. Flush/register tools through the existing Worker tool registry. 7. Freeze/install the Pod interceptor, start host-managed background tasks at their activation point, and start normal run/attach behavior. @@ -486,7 +479,7 @@ Recommended migration is incremental and behavior-preserving: - Define which hook decisions are safe for external contributors. 2. Add `pod::feature` with no behavior change. - - Implement descriptors, service declarations/requirements, capability grants, install reports, and registrars. + - Implement descriptors, service declarations/requirements, authority grants, install reports, and registrars. - Initially register no external plugins. 3. Add the service registry as a host-mediated boundary. @@ -515,7 +508,7 @@ Recommended migration is incremental and behavior-preserving: - Do not expose arbitrary workflow-like context injection to plugins until there is a safe typed command/input-contribution API with durable append semantics. 8. Add profile/manifest enablement after built-ins work through the same registry. - - Built-ins and external plugins should share descriptor/capability/install-report mechanics. + - Built-ins and external plugins should share descriptor/authority/install-report mechanics. - Host policy may grant built-ins by default, but built-ins should still declare what they use. ## 8. Impact on WorkItem / MCP / plugin distribution follow-ups @@ -523,14 +516,14 @@ Recommended migration is incremental and behavior-preserving: WorkItem / intake routing: - WorkItem routing can become a built-in feature that contributes WorkItem tools, optional routing hooks, and host-managed background tasks such as a watcher. -- It should request `WorkItemStore`, `RunBackgroundTask`, and notification/alert capabilities instead of reaching into ticket files ad hoc. +- It should request `WorkItemStore` and model-notification authority instead of reaching into ticket files or prompt context ad hoc. Its background task is a declared contribution, not a separate sandbox authority. - Model-visible routing hints or intake results must be committed through notification/history append paths. - This registry gives the WorkItem feature a clean way to install without making WorkItem a special Pod runtime mode. - If WorkItem, Memory, or Pod orchestration are split into smaller feature modules later, they should communicate through declared services such as `yoi.work-item-store.v1`, `yoi.memory.v1`, or `yoi.pod-management.v1` rather than private module references. Feature-to-feature service APIs: -- The service registry lets plugins expose stable APIs without requiring the host to make every domain capability a permanent core API. +- The service registry lets plugins expose stable APIs without requiring the host to make every domain service a permanent core API. - This does not force existing Memory or Pod management implementations to be extracted immediately. They may stay core-backed while still being representable as service façades for consumers. - External plugins should consume service proxies provided by the host, not another plugin's raw process/WASM/MCP handle. @@ -544,8 +537,8 @@ MCP: Plugin distribution: - Archive validation, cache extraction, signing/trust, WASM execution, external process supervision, and package update policy should remain separate follow-up designs. -- Distribution mechanisms should eventually produce the same descriptor/capability/contribution objects as built-ins. -- Capability grants are the host trust boundary; package installation alone must not grant runtime authority. +- Distribution mechanisms should eventually produce the same descriptor/authority/contribution objects as built-ins. +- Authority grants are the host trust boundary; package installation alone must not grant runtime authority. ## 9. Open questions / risks @@ -561,8 +554,8 @@ Plugin distribution: - `Alert`-like output is transient human-facing text only. - Diagnostics/status are host-defined operational records, not plugin-defined UI channels. -4. Capability granularity can easily become either too coarse or too noisy. - - Start with coarse host-service capabilities plus normal tool permissions, then split only when real features need finer grants. +4. Authority granularity can easily become either too coarse or too noisy. + - Start with coarse host authorities plus normal tool permissions, then split only when real features need finer grants. 5. Runtime enable/disable is not designed here. - Initial registry should be install-at-startup. Hot reload or dynamic plugin enablement needs separate lifecycle, cleanup, and schema-refresh design. @@ -575,7 +568,7 @@ Plugin distribution: 8. Service API design needs an in-process vs external-plugin boundary. - Built-in services can use Rust traits or typed handles, but external plugins need host-side proxies and operation schemas. The first implementation should avoid baking in Rust trait-object assumptions as the only service representation. - - Service handles must be capability-bound, or one broad service handle can accidentally become an authority escalation path. + - Service handles must be authority-bound, or one broad service handle can accidentally become an authority escalation path. 9. Existing workflow/input expansion is close to the forbidden boundary. - It is safe only because it commits system items before model visibility. Any future plugin command/input contribution must preserve that durable replay property. diff --git a/work-items/open/20260603-122317-plugin-feature-contribution-registry/item.md b/work-items/open/20260603-122317-plugin-feature-contribution-registry/item.md index 0a5a278b..e25cb66b 100644 --- a/work-items/open/20260603-122317-plugin-feature-contribution-registry/item.md +++ b/work-items/open/20260603-122317-plugin-feature-contribution-registry/item.md @@ -7,7 +7,7 @@ kind: feature priority: P1 labels: [plugin, registry, tools, hooks, orchestration] created_at: 2026-06-03T12:23:17Z -updated_at: 2026-06-04T21:46:04Z +updated_at: 2026-06-04T21:51:09Z assignee: null legacy_ticket: null --- @@ -20,14 +20,14 @@ The immediate need is not package distribution or WASM execution. The immediate ## Direction -Introduce a feature registry boundary for Pod runtime capability installation. +Introduce a feature registry boundary for Pod runtime contribution installation and host-authority grants. - Feature state remains owned by the feature/extension module, not by Pod history or prompt context. - Pod interaction happens through existing surfaces: - Tool contributions registered into the normal ToolRegistry / permission / history path. - Hook contributions registered through the public Pod Hook boundary. - BackgroundTask contributions are host-managed for async feature work; feature modules must not create untracked runtime loops. - - Service provider/consumer contributions allow a feature/plugin to expose a narrow public API and another feature/plugin to acquire it through host dependency resolution and capability grants. + - Service provider/consumer declarations allow a feature/plugin to expose a narrow public API and another feature/plugin to acquire it through host dependency resolution and host-authority grants where the service exposes host authority. - Model-visible notifications use the existing durable Notify / SystemItem / Event::SystemItem path rather than invisible context injection. - Transient human-facing alerts and diagnostics are separate host-defined outputs, not arbitrary plugin UI channels. - The registry is responsible for discovery/enablement diagnostics and installation into existing surfaces; it must not create a parallel execution path. @@ -49,8 +49,8 @@ Pure descriptor types may later move to a separate `plugin` / `extension` crate - Hooks - BackgroundTasks - Service providers and service requirements - - Model-visible notification, transient alert, and diagnostic capabilities where needed -- Define capability request / host grant data structures suitable for policy diagnostics. + - Model-visible notification, transient alert, and diagnostic surfaces where needed +- Define host-authority request / grant data structures suitable for user approval and policy diagnostics. Tool/Hook/BackgroundTask/ServiceProvider declarations are contributions to display and lock by descriptor/digest, not separate sandbox authorities. - Add a registry/builder/install context that can install enabled feature contributions into existing Pod/Worker surfaces. - Preserve current behavior while moving registration toward the registry. - Existing built-in tool registration must still work. @@ -72,7 +72,7 @@ Pure descriptor types may later move to a separate `plugin` / `extension` crate ## Suggested phases 1. **Registry design** - - Define feature descriptor, source, runtime kind, contribution kinds, capability request/grant, and diagnostics. + - Define feature descriptor, source, runtime kind, contribution kinds, host-authority request/grant, and diagnostics. 2. **Pod runtime registry skeleton** - Add a Pod-layer feature registry/builder and install context. - Keep behavior unchanged initially. @@ -87,8 +87,8 @@ Pure descriptor types may later move to a separate `plugin` / `extension` crate ## Acceptance criteria - The codebase has a first-class feature contribution registry boundary for Pod runtime installation. -- At least one built-in capability group is registered through the new registry without changing behavior. -- The registry can describe Tool, Hook, BackgroundTask, and Service provider/requirement contributions and records source/runtime/capability diagnostics. +- At least one built-in contribution group is registered through the new registry without changing behavior. +- The registry can describe Tool, Hook, BackgroundTask, and Service provider/requirement contributions and records source/runtime/authority diagnostics. - Feature installation uses existing ToolRegistry, HookRegistry, host-managed BackgroundTask lifecycle, host-mediated Service resolution, and notification/history paths; no parallel Pod context injection path or arbitrary plugin UI channel is introduced. - WorkItem and MCP follow-up tickets can target this registry instead of adding ad hoc registration code. -- Focused tests cover the migrated built-in registration and capability/diagnostic behavior. +- Focused tests cover the migrated built-in registration and authority/diagnostic behavior. diff --git a/work-items/open/20260603-122317-plugin-feature-contribution-registry/thread.md b/work-items/open/20260603-122317-plugin-feature-contribution-registry/thread.md index c4b78ca3..5c953ab6 100644 --- a/work-items/open/20260603-122317-plugin-feature-contribution-registry/thread.md +++ b/work-items/open/20260603-122317-plugin-feature-contribution-registry/thread.md @@ -973,4 +973,42 @@ I did not rerun full `cargo test -p pod --lib`, workspace check, or `nix build . No new generic event channel, custom UI/dialog path, hidden context/history injection, raw `Pod`, raw `ToolServerHandle`, raw `Interceptor`, raw history writer, raw event sender, or raw `NotifyBuffer` exposure was found in the feature API changes. Once the tool materialization/registration identity issue is fixed, the remaining risk looks appropriate for the intended descriptor-first Phase 1/2 slice. +--- + + + +## Decision + +# Decision: narrow plugin permissions to host authorities + +Plugin/Feature permission should mean user-approved host authority, not every contribution the feature declares. + +Purpose: + +- Explain to the user what dangerous host authority a plugin receives. +- Ensure sandboxed/external plugin code is not given APIs or handles for unapproved actions. +- Keep registry contribution integrity separate from authority grants. + +Revised model: + +- Contributions are descriptor/digest-locked declarations: + - Tools + - Hooks + - BackgroundTasks + - Service providers +- Host authorities are user-approved sandbox/object-capability grants: + - filesystem access + - network access + - secret refs + - model-visible durable notification/history append + - Pod management façade access where exposed + - store/state access such as Memory/WorkItem or persistent plugin state where applicable + +Tool/Hook/BackgroundTask/ServiceProvider declarations should be shown during install and locked by plugin descriptor/package digest. If they change, the plugin requires re-approval. They do not need separate `ContributeTool`, `ContributeHook`, `RunBackgroundTask`, or `ProvideService` authority variants. + +Service consumption is also not a blanket authority by itself. A service requirement is resolved by the host; the authority question depends on what host authority the service handle exposes. Service handles should be narrowed or authority-bound so acquiring one broad service handle cannot become an authority escalation path. + +Tool execution remains governed by the existing per-call tool permission / PreToolCall path. Feature authority grants do not replace manifest/tool permissions. + + ---