diff --git a/extensibility/package-management.md b/extensibility/package-management.md index 480f1c4..c3ff3fe 100644 --- a/extensibility/package-management.md +++ b/extensibility/package-management.md @@ -318,7 +318,14 @@ but Nix-like storage is not a first-stage requirement. Status: **accepted direction ([DIR-030](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/decisions/index.md))**, including the v1 component commands below (DIR-030 refinement of -2026-10-02). The process, install, and authority model is +2026-10-02); the manifest `[components]` grammar is **accepted** per the +2026-10-02 amendment to the +[Plugin Manifest and Capability Grammar +Authority](../specifications/manifest-capability-authority.md#amendment-2026-10-02-accepting-components-and-networkegress) +(it is also part of the [Plugin Platform +RFC](../specifications/plugin-platform-rfc.md#accepted-manifest-schema) +accepted manifest schema); the remaining package-manager install/resolution +spelling below is candidate. The process, install, and authority model is defined in the [Native Component Boundary](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/development/native-component-boundary.md); this section records only the package-manager consequences. @@ -358,6 +365,19 @@ net = "^0.0.1" Version requirements use semver caret matching with Cargo semantics: `^0.0.1` admits exactly `0.0.1`, and `^0.1` admits `>=0.1.0, <0.2.0`. +A plugin that uses the `net` component to reach a specific destination also +declares the structured egress entry so Core can compute its grant (the +intersection of granted `network.connect:HOST[:PORT]` capabilities and +`[[network.egress]]` declarations, per the [Plugin Manifest and Capability +Grammar +Authority](../specifications/manifest-capability-authority.md#amendment-2026-10-02-accepting-components-and-networkegress)): + +```toml +[[network.egress]] +host = "api.example.com" +ports = [443] +``` + Rules: - **No `PATH` discovery.** Components resolve only from the component root @@ -381,7 +401,7 @@ Rules: No component command, install, resolution, or `[components]` validation is implemented in the package manager yet. The plugin-facing request surface is -the `bitty.net` candidate request surface. +the [bitty.net candidate](../sdk/net-request-surface-candidate.md). ## Package manager versus runtime host diff --git a/sdk/net-request-surface-candidate.md b/sdk/net-request-surface-candidate.md index 226db65..8924fa5 100644 --- a/sdk/net-request-surface-candidate.md +++ b/sdk/net-request-surface-candidate.md @@ -459,12 +459,15 @@ the surface: additive minor version adds the `bitty.net` namespace, the Result event class, and the four event names to the closed set. - [Plugin Platform RFC](../specifications/plugin-platform-rfc.md): the - accepted manifest schema must admit `[components]` and `[[network.egress]]`, - and the event pipeline gains the Result class delivery rules above. -- [Plugin Manifest and Capability Grammar Authority](../specifications/manifest-capability-authority.md): - section 6 currently classifies `[[network.egress]]` as rejected, while - DIR-030 and the Core grant computation depend on it. That classification - must be amended by a reviewed change before this surface can be accepted. + accepted manifest schema admits `[components]` and `[[network.egress]]` + (open point resolved, see below), and the event pipeline gains the Result + class delivery rules above. +- [Plugin Manifest and Capability Grammar Authority](../specifications/manifest-capability-authority.md#amendment-2026-10-02-accepting-components-and-networkegress): + section 6 previously classified `[[network.egress]]` as rejected, while + DIR-030 and the Core grant computation depend on it. The 2026-10-02 + amendment resolves this by accepting `[components]` and + `[[network.egress]]`; the manifest schema conflict this page depended on is + closed. - [Isolation and Resource RFC](../runtime/isolation-resource-rfc.md): the per-plugin network bounds above become a new resource-ceiling row. - The SDK surface file (`bitty-plugin-api-v1.json` in the bitty-plugin-sdk @@ -475,9 +478,6 @@ the surface: ## Open points -- Amend the manifest authority section 6 and the Plugin Platform RFC manifest - schema to accept `[components]` and `[[network.egress]]` (owner: plugin - platform contract owners). - Whether revoking a `network.connect` grant cancels in-flight requests of that plugin, or only refuses new ones. - Whether the Result class needs a cross-plugin global pending bound in @@ -489,7 +489,6 @@ the surface: ## Acceptance criteria -- The manifest schema conflict in [Open points](#open-points) is resolved. - The category owner, the docs curator, and a security reviewer approve the namespace, events, errors, and bounds. - The verification plan items have named owning tasks in the `bitty` diff --git a/specifications/manifest-capability-authority.md b/specifications/manifest-capability-authority.md index f99f1fb..c742f1f 100644 --- a/specifications/manifest-capability-authority.md +++ b/specifications/manifest-capability-authority.md @@ -273,46 +273,143 @@ TOML inline table forms; they are correct and must not regress. ### 6. Host-only manifest forms classification -**Decision**: `[limits]`, `[[network.egress]]`, and `[services.required]` are -**rejected** as accepted manifest fields. +**Decision**: `[limits]` and `[services.required]` are **rejected** as +accepted manifest fields. `[components]` and `[[network.egress]]` are +**accepted** manifest fields, admitted by this amendment (dated 2026-10-02, +commander decision, recorded below). -**Rationale**: The Plugin Platform RFC section 2.2 (lines 169-224) defines the -accepted manifest schema. These three forms do not appear in that schema: +**Rationale**: The Plugin Platform RFC section 2.2 (lines 169-224) originally +defined the accepted manifest schema without these four forms: - `[limits]`: Not in accepted schema. Resource limits are owned by the Isolation Resource RFC and enforced by the runtime, not declared in manifests. -- `[[network.egress]]`: Not in accepted schema. Network destinations are - expressed via `network.connect:DESTINATION` capability grants. +- `[[network.egress]]`: Was classified as not in the accepted schema at the + time this specification was first accepted (2026-09-26). See the amendment + below: the accepted [DIR-030 Native Component + Boundary](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/development/native-component-boundary.md) + and the `bitty` host parser (`NetworkEgress` in + `bitty-plugin-host/src/manifest.rs`) now depend on this field, so the + rejection no longer matches the accepted direction and this specification + closes the conflict instead of silently diverging from DIR-030. - `[services.required]`: Not in accepted schema. Service dependencies are declared in `[dependencies]` with version requirements. +- `[components]`: Was absent from the original accepted schema. DIR-030 + introduces it as the plugin-side declaration of a required native + component (for example `net`), and this amendment admits it for the same + reason as `[[network.egress]]`. -**Classification**: These are implementation experiments in the reference host. -They must not appear in: +**Classification**: `[limits]` and `[services.required]` remain +implementation experiments in the reference host. They must not appear in: - SDK validators (must reject with "unknown field" error) - Template scaffolds or examples - Canonical documentation as accepted alternatives -**Action required**: Document these as experimental forms that may be removed -or reworked in a future manifest evolution RFC. They are not part of the stable -v1 contract. +**Action required**: Document `[limits]` and `[services.required]` as +experimental forms that may be removed or reworked in a future manifest +evolution RFC. They are not part of the stable v1 contract. `[components]` +and `[[network.egress]]` are now part of the stable v1 contract per the +amendment below. + +#### Amendment (2026-10-02): accepting `[components]` and `[[network.egress]]` + +**Status**: accepted amendment, dated 2026-10-02, recorded as a commander +decision following the DIR-030 dependency conflict raised in +[`sdk/net-request-surface-candidate.md`](../sdk/net-request-surface-candidate.md#open-points). + +**Why the original rejection no longer holds**: the 2026-09-26 acceptance of +this specification rejected both fields because neither appeared in the +Plugin Platform RFC's accepted manifest schema at that time. Since then, +[DIR-030 Native Component +Boundary](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/development/native-component-boundary.md) +was accepted (2026-10-01) and fixed a plugin dependency declaration +(`[components]`) and the Core grant computation +(`network.connect:*` intersected with `[[network.egress]]`) as load-bearing +parts of the native-component authority model. The `bitty` host parser +already implements `NetworkEgress` validation +(`bitty-plugin-host/src/manifest.rs`) and the grant intersection +(`bitty-runtime/src/component/grant.rs`). Continuing to classify these two +fields as rejected would contradict an already-accepted direction and leave +the SDK, template, and host without one shared grammar. This amendment +resolves the conflict by accepting both fields rather than by downgrading +DIR-030. + +**`[components]` grammar (new)**: + +```toml +[components] +net = "^0.0.1" +``` + +- Each key is a component name matching `[a-z][a-z0-9-]{0,31}` (the same + grammar DIR-030 uses for `bitty-component.toml`'s `[component].name`). +- Each value is a version requirement in the closed resolver constraint + grammar (Package Follow-up RFC), interpreted with caret semantics: `^0.0.1` + admits exactly `0.0.1`; `^0.1` admits `>=0.1.0, <0.2.0`. +- At most `MAX_COMPONENTS_PER_PLUGIN` entries (candidate bound: 8, matching + `MAX_DEPENDENCIES` and `MAX_TOOLS`'s order of magnitude; the host parser + does not yet enforce a dedicated ceiling, so this bound is a candidate + pending host implementation, not yet an enforced limit). +- A missing or incompatible component fails the install with a diagnostic, or + makes the capability unavailable at runtime, per DIR-030's plugin + dependency declaration section. + +**`[[network.egress]]` grammar (reinstated)**: + +```toml +[[network.egress]] +host = "api.example.com" # exact DNS name, no wildcard, no port, no path +ports = [443] # 1..=65535, at least one, at most MAX_NETWORK_PORTS_PER_HOST (16) +``` + +- `host` is validated as a bare DNS name: 1..=253 bytes total, dot-separated + labels of 1..=63 bytes each, lowercase ASCII alphanumerics and hyphens, no + leading/trailing hyphen per label, no wildcard (`*`), no port, no path, no + control characters (host parser: + `bitty-plugin-host/src/manifest.rs::validate_egress_host`). +- `ports` lists at least one and at most `MAX_NETWORK_PORTS_PER_HOST` (16) + ports; each port is in `1..=65535` (port `0` is rejected as not a + connectable destination). +- At most `MAX_NETWORK_EGRESS` (16) `[[network.egress]]` entries per + manifest (host parser constant). +- **Pairing is fail-closed in both directions**: every `network.connect:*` + capability needs a covering `[[network.egress]]` entry and every entry + needs a covering capability, mirroring the `process.spawn:` / + `[tools.]` rule in section 9 of the Plugin Platform RFC's accepted + manifest schema. A `network.connect:HOST` capability with no egress entry + for `HOST`, or an egress entry for a host with no covering + `network.connect` capability, is a validation error. +- **Grant computation**: the effective grant for a plugin is the + intersection of its granted `network.connect:HOST[:PORT]` capabilities and + its `[[network.egress]]` declarations. A bare `network.connect:HOST` + capability admits every port the matching egress entry declares for + `HOST`; a `network.connect:HOST:PORT` capability admits only `PORT`, and + only when the matching egress entry declares that port. A host without a + matching egress entry contributes nothing to the grant (host parser: + `bitty-runtime/src/component/grant.rs::PluginGrant::compute`). + +**Classification update**: `[components]` and `[[network.egress]]` move from +"rejected" to "accepted" in the authority table below. SDK validators, +template scaffolds, and canonical documentation must accept both forms using +the grammar above; `[limits]` and `[services.required]` remain rejected. ## Authority table This table maps every disputed grammar element to its single authoritative owner and canonical form: -| Grammar element | Authority | Canonical form | Consumers | -| ------------------------------ | ----------------------------- | ---------------------------------------- | ------------------------------------------------ | -| Environment capability family | This specification, section 1 | `env.read:` | SDK, host, template, docs | -| Environment wildcard semantics | This specification, section 1 | `PREFIX_*` allowed; `*` rejected | SDK, host grant evaluator | -| `layout.provider` capability | This specification, section 2 | Deferred (not in v1 closed set) | SDK (reject), host (remove), docs (do not claim) | -| Dependency inline table | This specification, section 3 | `{ version = "...", prerelease = bool }` | SDK, host, template, resolver | -| Compatibility range grammar | This specification, section 4 | Resolver version-requirement grammar | SDK, host, registry validator | -| Service schema representation | This specification, section 5 | TOML inline tables | SDK, host parser, template | -| `[limits]` manifest field | This specification, section 6 | Rejected (not accepted) | SDK (reject), docs (do not claim) | -| `[[network.egress]]` field | This specification, section 6 | Rejected (not accepted) | SDK (reject), docs (do not claim) | -| `[services.required]` field | This specification, section 6 | Rejected (not accepted) | SDK (reject), docs (do not claim) | +| Grammar element | Authority | Canonical form | Consumers | +| ------------------------------ | ---------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------ | +| Environment capability family | This specification, section 1 | `env.read:` | SDK, host, template, docs | +| Environment wildcard semantics | This specification, section 1 | `PREFIX_*` allowed; `*` rejected | SDK, host grant evaluator | +| `layout.provider` capability | This specification, section 2 | Deferred (not in v1 closed set) | SDK (reject), host (remove), docs (do not claim) | +| Dependency inline table | This specification, section 3 | `{ version = "...", prerelease = bool }` | SDK, host, template, resolver | +| Compatibility range grammar | This specification, section 4 | Resolver version-requirement grammar | SDK, host, registry validator | +| Service schema representation | This specification, section 5 | TOML inline tables | SDK, host parser, template | +| `[limits]` manifest field | This specification, section 6 | Rejected (not accepted) | SDK (reject), docs (do not claim) | +| `[[network.egress]]` field | This specification, section 6 amendment (2026-10-02) | Accepted: `{ host, ports }`, exact host, bounded ports, paired with `network.connect:*` | SDK, host, template, docs | +| `[services.required]` field | This specification, section 6 | Rejected (not accepted) | SDK (reject), docs (do not claim) | +| `[components]` manifest field | This specification, section 6 amendment (2026-10-02) | Accepted: component name (`[a-z][a-z0-9-]{0,31}`) -> caret semver requirement | SDK, host, template, docs | ## Valid and invalid example corpus @@ -336,6 +433,13 @@ required = [ "network.connect:api.example.com:443" ] +[components] +net = "^0.0.1" + +[[network.egress]] +host = "api.example.com" +ports = [443] + [dependencies] "xuepoo.gitcore" = ">=2.0.0" "xuepoo.experimental" = { version = "^1.0", prerelease = true } @@ -381,11 +485,16 @@ bitty = "==0.1.0" # Use = not == [limits] memory_mb = 128 # Not an accepted manifest field -[[network.egress]] -host = "example.com" # Not an accepted manifest field - [services.required] "xuepoo.bar" = "1.0" # Not an accepted manifest field + +# INVALID EXAMPLE 8: Invalid [components] / [[network.egress]] shapes +[components] +Net = "^0.0.1" # Name must be lowercase: [a-z][a-z0-9-]{0,31} + +[[network.egress]] +host = "*.example.com" # No wildcards (no allow-all) +ports = [] # At least one port is required ``` ## SDK and host implementation requirements @@ -399,8 +508,8 @@ host = "example.com" # Not an accepted manifest field `{ version = "...", prerelease = }`. 4. Apply resolver version-requirement validation to `compat.bitty` and `compat.plugin-api`. -5. Reject `[limits]`, `[[network.egress]]`, and `[services.required]` as - unknown fields. +5. Reject `[limits]` and `[services.required]` as unknown fields; accept + `[components]` and `[[network.egress]]` per the amendment grammar above. 6. Ensure service schemas are TOML inline tables, never JSON strings. ### Host parser changes @@ -411,8 +520,10 @@ host = "example.com" # Not an accepted manifest field 4. Apply resolver constraint validation to compatibility ranges. 5. Update manifest parser to handle nested TOML inline tables for service schemas; remove JSON string shortcut. -6. Classify `[limits]`, `[[network.egress]]`, and `[services.required]` as - experimental (warn or reject). +6. Classify `[limits]` and `[services.required]` as experimental (warn or + reject); `[components]` and `[[network.egress]]` are already implemented + (`bitty-plugin-host/src/manifest.rs`, `bitty-runtime/src/component/grant.rs`) + and must stay accepted. ### Template generator changes @@ -420,7 +531,9 @@ host = "example.com" # Not an accepted manifest field 2. Remove any `layout.provider` capability from examples. 3. Show both string and inline table dependency forms in comments. 4. Use TOML inline tables for service schemas in examples. -5. Do not generate `[limits]`, `[[network.egress]]`, or `[services.required]`. +5. Do not generate `[limits]` or `[services.required]`. Scaffold + `[components]` / `[[network.egress]]` only when the plugin requests a + native component. ### Documentation changes @@ -429,8 +542,10 @@ host = "example.com" # Not an accepted manifest field 3. Document dependency inline table as accepted (remove "not yet enforced"). 4. Clarify that compatibility ranges use resolver grammar. 5. Show only TOML inline table service schemas. -6. Do not mention `[limits]`, `[[network.egress]]`, or `[services.required]` as - accepted fields. +6. Do not mention `[limits]` or `[services.required]` as accepted fields. +7. Document `[components]` and `[[network.egress]]` as accepted per the + amendment grammar above, including the fail-closed pairing rule and the + grant-intersection computation. ## Verification plan @@ -439,7 +554,9 @@ host = "example.com" # Not an accepted manifest field - Run `just check` in `bitty-plugins-docs`: markdownlint, link checker, and metadata validator must pass. - Verify no document claims `env:`, `layout.provider`, JSON string - schemas, or rejected manifest fields as accepted forms. + schemas, or the still-rejected `[limits]`/`[services.required]` fields as + accepted forms; verify `[components]` and `[[network.egress]]` examples + match the amendment grammar. ### Cross-repository synchronization @@ -460,10 +577,17 @@ grammar fork. This specification clarifies but does not change the contracts in: - [Plugin Platform RFC](plugin-platform-rfc.md): manifest schema and capability - model remain normative; this document resolves specific spelling ambiguities. + model remain normative; this document resolves specific spelling ambiguities + and, per the 2026-10-02 amendment, admits `[components]` and + `[[network.egress]]` into the accepted manifest schema (the Plugin Platform + RFC's own schema fragment is updated in the same change). - [Package Follow-up RFC](../packaging/package-followup-rfc.md): resolver constraint grammar remains normative; this document applies it to - compatibility ranges. + compatibility ranges and to `[components]` version requirements. +- [DIR-030 Native Component + Boundary](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/development/native-component-boundary.md): + the amendment admits the manifest fields DIR-030 already depends on; this + document does not change DIR-030's process, install, or authority model. ## Security review @@ -500,5 +624,9 @@ following criteria are satisfied: capability model. - [Package Follow-up RFC](../packaging/package-followup-rfc.md): resolver constraint grammar and prerelease policy. +- [DIR-030 Native Component + Boundary](https://github.com/bitty-terminal/bitty-docs/blob/main/docs/development/native-component-boundary.md): + accepted direction that depends on `[components]` and + `[[network.egress]]`, admitted by the 2026-10-02 amendment in section 6. - Issues: bitty-plugins-docs #95 (parent contract decision), #96 (grammar publication). diff --git a/specifications/plugin-platform-rfc.md b/specifications/plugin-platform-rfc.md index bdce832..227b5cd 100644 --- a/specifications/plugin-platform-rfc.md +++ b/specifications/plugin-platform-rfc.md @@ -170,6 +170,13 @@ claims = ["tabline"] [tools.git] # optional; accepted Layer-2 system-CLI slice required = true # boolean; true gates activation on git presence and range version = ">=2.30" # version range; max 128 bytes + +[components] # optional; required native components (DIR-030) +net = "^0.0.1" # component name [a-z][a-z0-9-]{0,31} -> caret semver requirement + +[[network.egress]] # optional; structured egress paired with network.connect:* capabilities +host = "api.example.com" # exact DNS name; no wildcard, no port, no path +ports = [443] # 1..=65535, at least one, at most MAX_NETWORK_PORTS_PER_HOST (16) ``` Accepted validation rules: @@ -221,6 +228,20 @@ Accepted validation rules: `false` to `true` is a capability increase whose grant must be re-confirmed. Any other `[tools.*]` table fails closed until its own slice is accepted. +10. The optional `[components]` table and `[[network.egress]]` array, + admitted by the + [Plugin Manifest and Capability Grammar Authority](manifest-capability-authority.md#amendment-2026-10-02-accepting-components-and-networkegress) + 2026-10-02 amendment, declare the native components a plugin requires + (DIR-030) and the structured network destinations that pair with + `network.connect:HOST[:PORT]` capability grants. `[components]` keys + follow the `[a-z][a-z0-9-]{0,31}` grammar and map to caret semver + requirements; `[[network.egress]]` entries carry an exact `host` (no + wildcard) and a bounded `ports` list. Every `network.connect` capability + needs a covering egress entry and every entry needs a covering + capability (fail-closed in both directions, mirroring rule 9's + `[tools.*]` pairing); the effective grant is the intersection of the two, + computed by the host (`bitty-runtime::component::grant`) and never + constructed by the plugin. > **Open reconciliation item — manifest dependency prerelease TOML shape.** > The accepted per-edge `prerelease` opt-in defines no manifest TOML shape for it, and