External Integrations (P1: verify and install)
Storm Pulse can verify and install signed third-party integration packages locally. This first phase does one thing and states its limits plainly: it proves which exact bytes were installed and which approved key signed them. It does not run any of that code.
An installed package sits on disk as a read-only, content-addressed tree with a receipt beside it. Nothing imports it, nothing executes it, and installing it grants it no ability to do anything. Loading and running integrations arrives in a later phase, behind its own separate approval.
What a receipt is, and what it is not
A receipt is an attestation. It records that a specific set of bytes was installed and that an approved publisher key signed them. Keep these four distinct:
- A receipt is not a load grant. An installed package is inert. Nothing imports it at startup or at runtime in this phase.
- A load grant is not a command grant. Being allowed to run is separate from being allowed to contribute commands the agent will execute.
- A command grant is not a sandbox. Permission to contribute a command says nothing about confinement. This phase makes no confinement claim at all.
Storm Pulse is honest about this boundary rather than overselling it: the
guarantee here is auditability of exactly what landed on disk and who signed it,
not safety of the code. The rationale and the full phase plan live in
CORE-007 (decision 7 in particular), in the repository's
_architecture/adrs/core/; this page does not restate it.
The trust model
You approve a publisher's public key once, locally, on the box. Approval and
revocation are operator acts, tied to the machine you run them on. After that,
inspect and install will report a package signed by that key as trusted; a
package signed by any other key is unknown or invalid. A revoked key is never
implicitly un-revoked.
Approving and revoking a publisher changes a trust decision on the host, so both require you to name the host you are on:
stormpulse integration publisher add ./publisher.pub \
--label "acme releases" --confirm-hostname "$(hostname)"
Without a matching --confirm-hostname, the command refuses and exits 2.
How-to: approve, inspect, install, diagnose, revoke
The full local lifecycle, in order.
1. Approve the publisher's key. The key file is a raw 32-byte Ed25519 public key or a PEM public key. A private key is refused.
stormpulse integration publisher add ./publisher.pub \
--label "acme releases" --confirm-hostname "$(hostname)"
stormpulse integration publisher list
2. Inspect the package before doing anything with it. Inspection reads the package and reports its identity, digest, and trust status. It never executes the package.
stormpulse integration inspect ./acme-backup-1.2.0/
Look for trust_status: trusted and signature_status: valid. An unknown
publisher reads as unverifiable; a tampered package reads as invalid.
3. Install. Installation copies the package into a private staging tree, re-hashes the destination (the destination bytes are the sole authority, so a source that changes mid-copy cannot smuggle in an unsigned tree), verifies the signature against an approved, active publisher, then atomically commits a read-only tree and writes the receipt.
stormpulse integration install ./acme-backup-1.2.0/
stormpulse integration list
Installing the same package again is idempotent: same tree, same receipt, no duplicate.
4. Diagnose. doctor re-checks installed state: it re-hashes each tree,
re-verifies the signature it excluded from the digest, flags drift, corruption,
orphans, and revoked publishers, and reports findings in a stable order. It
never repairs and never imports a package. It exits 5 if any finding is an
error.
stormpulse integration doctor
stormpulse integration doctor acme-backup # narrow to one integration id
5. Revoke. Revoking a publisher stops future installs signed by that key and
makes doctor flag anything already installed under it.
stormpulse integration publisher revoke sha256:<fingerprint> \
--confirm-hostname "$(hostname)"
Command reference
Every command accepts --json for one canonical JSON object on stdout (no ANSI,
no absolute source paths leaked) and --config PATH to point at a non-default
agent config.
| Command | What it does |
|---|---|
integration inspect <source> |
Report a package's identity, digest, and trust status without executing it. |
integration install <source> |
Verify and immutably install a signed package; write a receipt. |
integration list |
List install receipts. |
integration doctor [<id>] |
Diagnose installed state; re-verify digests and signatures. |
integration publisher add <key_file> --label <label> --confirm-hostname <host> |
Approve a publisher's public key locally. |
integration publisher list |
List approved publishers. |
integration publisher revoke <fingerprint> --confirm-hostname <host> |
Revoke a publisher for future trust decisions. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Operational failure: state lock unavailable, disk/permission, source race, or an unexpected error. |
| 2 | Usage error, or a missing/mismatched --confirm-hostname. |
| 3 | Structural rejection: missing source, unsafe path or file type, or a bad manifest. |
| 4 | Trust rejection: unsupported key, signature mismatch, or an unknown/revoked publisher. |
| 5 | State problem: a corrupt receipt, a missing package tree, a corrupt installed digest, or a doctor error finding. |
Not yet in this phase
There is deliberately no tutorial for loading or running an integration, because running installed integrations does not exist yet. A walkthrough arrives with the runtime loader in a later phase, gated by its own approval. What this phase gives you is the verifiable, auditable base that phase will build on: proof of exactly what is installed and who signed it.