Files
HiveGrid-Wireframes/hivegrid-cloud/docs/plugins/capability-matrix.md
T

84 lines
5.0 KiB
Markdown

# Capability Matrix
Every host function exposed to a plugin sits behind a capability. The
manifest must declare a capability before the runtime binds the
corresponding host functions; calling an undeclared host-fn throws
`CapabilityDeniedError` and audits as `outcome=denied`.
## Closed enum
The runtime maintains a closed enum of capability names in
`packages/plugin-runtime/src/capabilities.ts`. Anything outside this
list is rejected at manifest parse time.
| Capability | Risk | Host binding availability | Unlocks / intended binding | Scope shape |
| ---------------- | -------- | ------------------------- | --------------------------------------------------------------- | ----------------------------------------- |
| `log` | none | available | `ctx.log.{info,warn,error}` - structured logging into host pino | none |
| `audit` | none | available | `ctx.audit.record({ action, target?, meta? })` | none |
| `tenant.info` | low | reserved / fail-closed | Reserved tenant metadata binding; not bound today | none |
| `secret.read` | medium | reserved / fail-closed | Reserved per-tenant secret reads; not bound today | `{ keyPattern: "^slack-" }` planned |
| `storage.read` | medium | reserved / fail-closed | Reserved tenant-scoped key/value reads; not bound today | per-prefix locking planned |
| `storage.write` | medium | reserved / fail-closed | Reserved tenant-scoped key/value writes; not bound today | per-prefix locking planned |
| `http.outbound` | high | reserved / fail-closed | Reserved outbound fetch binding; not bound today | `{ allowedHosts: ["hooks.slack.com"] }` |
| `db.read` | high | reserved / fail-closed | Reserved DB binding for marketplace work; not bound today | `{ tablePrefix: "tx_" }` planned |
| `db.write` | critical | reserved / fail-closed | Reserved DB binding for marketplace work; not bound today | `{ tablePrefix: "tx_" }` planned |
`PLUGIN_CAPABILITY_NAMES` is intentionally broader than the production host
binding set. As of this foundation, `PLUGIN_AVAILABLE_HOST_CAPABILITY_NAMES`
contains only `log` and `audit`; declaring higher-risk capabilities parses, but
production install/grant/enable paths reject them until their bindings, tests,
and security review ship.
## Risk legend
- **none** — no tenant data, no outbound — safe for any plugin.
- **low** — tenant identity only; no payload exposure.
- **medium** — touches per-tenant data inside the host; operator
review recommended.
- **high** — issues network requests OR reads tenant business data;
audit-trail required and operator must explicitly approve at install.
- **critical** — mutates tenant business data; install requires
out-of-band operator sign-off; auto-disable on the slightest
anomaly.
## Common pitfalls
- **Declaring `http.outbound` without `allowedHosts`** is the most
common authoring mistake. The runtime denies every request unless
the host appears in the allowlist. The lint command warns on this
shape.
- **Assuming `db.read` is bound today** — the DB capability is
reserved-only in the foundation. A plugin that declares it parses
fine but the host returns `no_host_implementation` on call.
- **Using `audit` as a payload queue** — see the async-task example;
the audit body has a cap. Pass identifiers, not blobs.
## How to request a new capability
1. Propose the name (lowercase, dot-separated).
2. Write a one-paragraph threat model: what data can leak, what
privileges are gained, what reverse-direction trust does the
capability imply?
3. Add to `PLUGIN_CAPABILITY_NAMES` in `capabilities.ts`.
4. Add the host-fn implementation in `capability-gateway.ts` via the
provided `hostFns` map; never bypass the gateway.
5. Update this table.
6. Operator-side: external security review when the new capability
ships in production (Phase 06 #23 sign-off — see audit-docs.md).
## Audit trail mapping
Every successful or failed call to a host-fn produces one row in
`PluginCapabilityUse`:
| Column | Meaning |
| ------------------- | ---------------------------------------------------- |
| `capability` | Capability name (e.g. `http.outbound`) |
| `outcome` | `success` / `denied` / `error` |
| `payloadHash` | SHA-256 of stable call descriptor; never the body |
| `durationMs` | Time spent in the host-fn (excluding gateway frame) |
| `errorCode` | Short code on `denied` / `error` (e.g. `not_declared_in_manifest`) |
Tenant admins see this list under
`/app/:tenant/settings/plugins/:id/audit`.