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

5.0 KiB

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.