aidokitwiki

Security Model

Audience-Contributor Status-Shipped v0.5 Spec-Security ADR-0008

Purpose #

Summarise aidokit's threat model, trust boundaries, and the mitigations the architecture enforces. Authoritative long-form lives in .docs/docs/specs/security-model.md; user-facing policy lives in SECURITY.md.

Big Picture #

aidokit is closer to cookiecutter than to "an AI service in the cloud" — it writes files locally and runs declared shell commands. The threat surface is code that writes files and runs shell commands on the user's machine.

Five guiding principles, from .docs/docs/specs/security-model.md §3:

  1. Defense in depth. Capability declarations + conformance harness + signed packages + dry-run + audit log all overlap.
  2. Fail closed. Unknown trigger → don't suggest. Missing install spec → refuse install. Manifest tampering → refuse to use.
  3. Honest disclosure. Capabilities must be declared truthfully. Misdeclaration is a conformance violation.
  4. No magic, no surprises. --dry-run, audit log, no background activity, no phone-home.
  5. Authority over identity. Trust flows from verified capability (conformance + signing), not from organisational claims.

How It Works #

Trust boundaries #

From .docs/docs/specs/security-model.md §5:

Boundary Trust Mitigation
User's machine trusted (the user already runs code there)
Project directory scoped adapters write only declared paths
User's home directory limited only $AIDO_HOME (default ~/.aido/) writes; never dotfiles or creds
First-party @aidokit/* packages trusted (verifiable) npm provenance from v1.0; npm audit signatures
Third-party adapters / stack packs untrusted by default conformance CI + capability declarations + signing as trust signals
MCP servers (catalog, non-sensitive) scoped securitySensitive flag for elevated capability
MCP servers (catalog, sensitive) gated non-bypassable confirmation, never --yes-bypass
MCP servers (custom via --custom-url) explicitly untrusted forced confirmation; source: 'custom' in audit log
npm registry relied upon pinned versions, lockfile, provenance verification
User's AI coding CLI (Claude Code, Codex, …) trusted (user-chosen) aidokit only writes config; never runs the model

Threat model (abridged) #

Full table at .docs/docs/specs/security-model.md §6.1. High-severity threats and primary mitigations:

Threat Mitigation
Malicious adapter writes outside declared writesPaths Capability declaration + conformance harness + path validation
Malicious adapter executes undeclared shell commands in postInstall Capability declaration + conformance review
Malicious stack pack writes files directly via Node fs Stack packs MUST NOT emit files — cardinal rule + harness
Compromised npm package replaces a first-party adapter npm provenance (v1.0); lockfile integrity
Supply-chain attack via transitive dep pnpm strict mode; pinned exact versions; dep review
Tampering with .aido/adapter.md to claim higher conformance aidokit doctor re-verification; conformance snapshot
--yes bypassing safety confirmations securitySensitive and custom MCPs cannot be bypassed
aidokit sync overwriting user-customised files silently Diff preview required; syncOverwritePolicy: 'on-confirm'

Out-of-scope threats include OS-level privilege escalation, network MITM (TLS assumed), and compromise of the user's AI coding CLI.

Mitigations summary #

From .docs/docs/specs/security-model.md §7:

What v1.0 adds over v0.5 #

From .docs/ROADMAP.md §v1.0 and CHANGELOG [Unreleased]:

Example: aidokit mcp add filesystem (security-sensitive) #

$ aidokit mcp add filesystem --yes

⚠  Security-sensitive MCP

   Filesystem MCP can write or delete files outside the engine directory.
   --yes does NOT bypass this confirmation.

   Roles to scope: (none — manual choice required for sensitive entries)

? Proceed?  (y/N)  n

ℹ  Cancelled. No changes made.

If confirmed, the audit log records userConfirmed: true and source: 'catalog'.

Diagram #

%%{init: {
  "theme": "base",
  "themeVariables": {
    "fontFamily": "ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif",
    "fontSize": "14px",
    "primaryColor": "#eff6ff",
    "primaryTextColor": "#0f172a",
    "primaryBorderColor": "#2563eb",
    "lineColor": "#475569",
    "secondaryColor": "#f1f5f9",
    "tertiaryColor": "#ffffff",
    "clusterBkg": "#f8fafc",
    "clusterBorder": "#cbd5e1"
  }
}}%%
flowchart LR
  classDef actor    fill:#ede9fe,stroke:#6d28d9,color:#1e1b4b,stroke-width:1.2px;
  classDef cli      fill:#dbeafe,stroke:#1d4ed8,color:#0c1f4a,stroke-width:1.4px;
  classDef adapter  fill:#cffafe,stroke:#0e7490,color:#083344;
  classDef pack     fill:#dcfce7,stroke:#15803d,color:#052e16;
  classDef core     fill:#fef9c3,stroke:#a16207,color:#422006;
  classDef artifact fill:#f1f5f9,stroke:#475569,color:#0f172a;
  classDef stop     fill:#fee2e2,stroke:#b91c1c,color:#7f1d1d,stroke-dasharray:4 3;
  classDef ok       fill:#ecfdf5,stroke:#047857,color:#064e3b;
  classDef external fill:#fff7ed,stroke:#c2410c,color:#431407;

  user["User runs aidokit"]:::actor --> cli["@aidokit/cli"]:::cli
  cli --> manifest["adapter.manifest.capabilityDeclarations"]:::core
  manifest --> harness{"Conformance check"}:::stop
  harness -->|verifies emissions vs declarations| pass["✔"]:::ok
  harness -->|misdeclaration| fail["✘ block publish"]:::stop
  cli --> staging[".aido-staging/ (atomic)"]:::artifact
  cli --> audit[".aido/state.json"]:::artifact
  cli --> sens{"MCP securitySensitive?"}:::stop
  sens -->|yes| confirm["explicit confirm (no --yes bypass)"]:::stop
  sens -->|no| auto["scope per suggestedFor"]:::ok

Common Mistakes #

Checklist #

For any change that touches IO or shell execution: