ADR-0026: Component trust tiers, vetting, and MCP as an import layer¶
- Status: Accepted
- Date: 2026-10-06
- Deciders: Jian (product owner)
Context¶
ADR-0025 creates a component repository. The dev team is its only author now, but a contributed marketplace is an intended later step, so the trust model must not preclude it. A review of supply chain incidents in comparable ecosystems (npm event-stream and the Shai-Hulud worm, tj-actions, n8n community nodes, postmark-mcp, MCP tool-poisoning and rug-pull attacks, OpenClaw/ClawHub, VS Code extensions, Chrome Web Store) found one pattern: account and token takeover, and malicious updates, defeat code review, and mutable references defeat approval.
The closest analogue is n8n community nodes, which run unsandboxed with decrypted credentials; eight malicious ones stole OAuth credentials in January 2026. postmark-mcp shipped fifteen clean versions and then one that BCC'd all mail to the attacker through the legitimate API, so an egress allow-list alone would not have stopped it.
Kampong is exposed on three surfaces: the canvas and local engine, the hosted multi-tenant server that runs components with a tenant's secrets (ADR-0024), and exported projects that bundle a component and cannot be revoked remotely.
Evidence caveat: the incident details and sandbox comparisons came from search summaries, not full page fetches. The tier model, egress-injected secrets and release cooldown are our own design.
Decision¶
1. Three trust tiers¶
| Tier | Who | May contain | Controls |
|---|---|---|---|
| 0 first-party | dev team | manifests and in-repo TypeScript | two-person review, keyless Sigstore signing |
| 1 verified | publisher-verified | manifests; TypeScript only in a permission-scoped sandbox | our countersignature on the reviewed digest, human review when permissions or code change, release cooldown |
| 2 community | anyone | declarative manifests only, no custom code | consent screen on install, no auto-update, labelled unreviewed |
Only tier 0 ships at first. A workflow template is capped at the lowest tier among its components.
2. Do now, while first-party only (hard to retrofit)¶
- Permission manifest on every component, enforced on first-party components too: per-host egress allow-list, named secret slots bound to hosts, and declared filesystem, env and exec permissions. A permission-widening update forces re-approval.
- Secrets are injected at the egress layer by slot. A declarative component never sees plaintext;
TypeScript modules never get ambient
process.env. - Components are addressed by
namespace/name@versionplus a content digest, locked inkampong.lock. No mutable tags. Tool descriptions and schemas are hashed at install; any change is a reviewed event (CVE-2025-54136 shows approval does not survive a later server-side change). - No install scripts, no
eval, no remote code fetch. - Release signing with Sigstore, an SBOM, and digests shipped inside every export, so a user can verify what an exported project holds.
Runnerisolation interface for TypeScript modules, so the sandbox can be chosen later without changing component code.- A revocation field in the registry index from day one, and reserved
kampong/*namespace. - Tool outputs are treated as untrusted input to the model (prompt injection).
3. Minimum vetting pipeline (required before tier 1 or 2 opens)¶
- Manifest schema and permission linter.
- Reproducible bundle with a digest.
- Static checks: no install scripts, eval or remote code; AST-inferred capabilities must be a subset of the manifest; scan for invisible and bidirectional Unicode, leaked secrets and obfuscation; scan documentation and descriptions for embedded instructions.
- Dependency checks: pinned, malware and OSV feeds, SBOM.
- Recorded-mock conformance tests in a sandbox with a canary secret.
- CI gate failing on any permission or description change without a code owner.
- Sigstore signing and provenance written to the index.
- Revocation honoured by the engine and the hosted server.
4. MCP is an import and adaptation layer, not the primary format¶
- The primary format is the native manifest (ADR-0025).
kampong import mcpgenerates a manifest withsource: mcpthat wraps a pinned MCP tool. The exporter emits either a direct HTTP call or an@mastra/mcpclient. - Reasons: MCP has no trigger, webhook or poll concept; tool outputs are often free text, so typed outputs must be declared anyway; servers are black boxes that drift, so the server version and a tool-schema hash are pinned and a mismatch fails visibly; the specification is still moving (the 2026-07-28 revision is stateless-first and was reported as both final and a release candidate, to be confirmed before building); and MCP says nothing about idempotency or retries.
- Imported write tools default to
requires_approval: true, because MCP annotations are advisory. - Self-host may use remote bearer-token servers. Hosted mode never runs arbitrary stdio servers in
the shared process (remote code execution by design); it allows a curated, pinned list of remote
HTTP servers with
allowedHosts, per-request toolsets and vault tokens, and audit logging. - The official MCP Registry is in preview and authenticates namespaces only. "Registry-listed" does not mean safe and is not a trust signal for us.
5. Deferred¶
Community publishing, ratings, payments, and hosted execution of custom code. A sandbox technology (isolates, Deno permissions, gVisor, microVMs) is chosen only after a dedicated threat review.
Alternatives considered¶
| Option | Why not |
|---|---|
| Open, unvetted community registry | n8n and ClawHub show open registries are flooded within weeks. |
| TypeScript community components in-process | The n8n failure: untrusted code with decrypted credentials. |
| MCP as the primary format | Section 4. |
| Review-only trust, no sandbox or permissions | Incidents show review alone does not catch account takeover or later updates. |
Consequences¶
- Permission manifests and slot-bound secrets are part of the manifest schema from the first release, which raises the cost of the first connectors slightly and avoids a breaking change later.
- Exported projects cannot be revoked remotely (threat T9); digests and an SBOM are the mitigation.
- The hosted connector-secret vault (ADR-0024) must support slot-to-host binding before any tier 1 component runs there.
- A
security.txtand a vulnerability disclosure path are added with the first public release.