{
  "model": "MCP is an untrusted API. Inputs and outputs are hostile.",
  "defend": [
    "server-identity — TLS to the published MCP URL; verify signatures with /.well-known/jwks.json",
    "tool-integrity — schemaHash + Ed25519 signature on /tools.json; reject drift",
    "request-integrity — allowlisted ids, strict schema, API key, rate limit, kill switch, request id",
    "data-integrity — catalog SHA-256, signed INTEGRITY.json, envelope kind=data instructions=false"
  ],
  "layers": [
    {
      "layer": "Identity",
      "rule": "Every client proves who it is (site session or bk_live_ key)."
    },
    {
      "layer": "Authentication",
      "rule": "Site: Google/X. MCP connect: unauthenticated HTTP. Gated tools: apiKey. Target: OAuth 2.1 with audience bound to this server."
    },
    {
      "layer": "Authorization",
      "rule": "Each tool has permissions (registry.read, component.generate, account.credits). Least privilege."
    },
    {
      "layer": "Input",
      "rule": "Treat as hostile. Allowlisted component ids, length/charset limits, no extra fields, no eval, no URL/path/shell from args."
    },
    {
      "layer": "Output",
      "rule": "Treat as hostile. Envelope is kind=data, instructions=false. Do not execute returned files as orders."
    },
    {
      "layer": "Tools",
      "rule": "Versioned manifests with schemaHash, publisher, permissions, rate limits. Signed."
    },
    {
      "layer": "Credentials",
      "rule": "Generator has no GitHub owner token and no database owner role. Keys are hashed at rest."
    },
    {
      "layer": "Execution",
      "rule": "Catalog copy + stamp only. No subprocess, no fetch-from-args."
    },
    {
      "layer": "Abuse",
      "rule": "Per-account per-tool rate limit, max 12 components, max payload, timeouts."
    },
    {
      "layer": "Integrity",
      "rule": "Catalog SHA-256 must match or delivery is refused. Manifests and INTEGRITY.json are signed."
    },
    {
      "layer": "Replay",
      "rule": "Each generate has a request id. Duplicate ids are rejected."
    },
    {
      "layer": "Isolation",
      "rule": "Tenant = signed-in account. Credits and keys do not cross accounts."
    },
    {
      "layer": "Monitoring",
      "rule": "Audit events without secrets (no apiKey, tokens, cookies)."
    },
    {
      "layer": "Recovery",
      "rule": "Per-tool kill switch. Disable a tool without redeploying."
    },
    {
      "layer": "Cloning",
      "rule": "Protocol is public. Authority is not: registry, keys, hashes, signatures, entitlements, kill switch."
    }
  ],
  "input": {
    "treatAs": "hostile",
    "allowlist": "catalog component ids only",
    "noEval": true,
    "noFetchFromArgs": true,
    "noShellFromArgs": true,
    "maxComponents": 12
  },
  "output": {
    "treatAs": "data-not-instructions",
    "envelope": {
      "kind": "data",
      "instructions": false,
      "source": "botkelp-catalog"
    },
    "integrityFile": "INTEGRITY.json",
    "signed": true
  },
  "authToday": {
    "website": "Google or X sign-in",
    "mcpConnection": "unauthenticated HTTP",
    "gatedTools": "apiKey argument, prefix bk_live_",
    "productionTarget": "OAuth 2.1 with audience bound to this MCP server"
  },
  "permissions": [
    "registry.read",
    "component.generate",
    "account.credits",
    "knowledge.read"
  ],
  "cloning": "The protocol is public. Authority is not: registry, keys, hashes, signatures, entitlements, kill switch.",
  "keys": "/.well-known/jwks.json",
  "tools": "/tools.json",
  "catalog": "/catalog.json",
  "integrity": "/integrity",
  "docs": "https://www.botkelp.com/security"
}
