{
  "schema_version": 1,
  "diagram_type": "architecture",
  "meta": {
    "title": "Central AI Hub",
    "output": "docs/hub-architecture-map.html",
    "quality_profile": "standard",
    "views": [
      {
        "id": "fast-command",
        "label": "Fast command",
        "focus": ["esp32", "hub", "ha", "spk"],
        "note": "A fast command goes straight to the destination. No LLM waits in the action path."
      },
      {
        "id": "slow-job",
        "label": "Slow job",
        "focus": ["phone", "hub", "pc"],
        "note": "A slow job goes to Paperclip. The result returns to the Hub later."
      },
      {
        "id": "restricted",
        "label": "Restricted action",
        "focus": ["admin", "hub", "net"],
        "note": "Only the admin app can change parental controls. Voice may ask; the app confirms."
      }
    ]
  },
  "components": [
    { "id": "phone", "type": "frontend", "label": "Phone app", "sublabel": "wake word + voice", "pos": [40, 80], "size": [150, 64] },
    { "id": "admin", "type": "security", "label": "Admin app", "sublabel": "restricted actions", "pos": [40, 200], "size": [150, 64] },
    { "id": "esp32", "type": "external", "label": "ESP32 voice", "sublabel": "Hey Jervis", "pos": [40, 320], "size": [150, 64] },
    { "id": "dsh", "type": "frontend", "label": "dsh chat", "sublabel": "per person", "pos": [40, 440], "size": [150, 64] },
    { "id": "hub", "type": "backend", "label": "Hub", "sublabel": "hear - understand - check - route - answer", "pos": [390, 260], "size": [190, 80], "tag": "one shared service" },
    { "id": "ha", "type": "external", "label": "Home Assistant", "sublabel": "devices, timers", "pos": [790, 40], "size": [160, 64] },
    { "id": "spk", "type": "external", "label": "Speakers", "sublabel": "Jervis TTS", "pos": [790, 150], "size": [160, 64] },
    { "id": "net", "type": "backend", "label": "Internet control", "sublabel": "parental limits", "pos": [790, 260], "size": [160, 64] },
    { "id": "art", "type": "backend", "label": "Artemis", "sublabel": "phone screen", "pos": [790, 370], "size": [160, 64] },
    { "id": "pc", "type": "backend", "label": "Paperclip", "sublabel": "slow jobs", "pos": [790, 480], "size": [160, 64] },
    { "id": "web", "type": "cloud", "label": "Web search", "sublabel": "Tavily", "pos": [790, 590], "size": [160, 64] },
    { "id": "app", "type": "backend", "label": "Apprise / NTFY", "sublabel": "notify + email", "pos": [790, 700], "size": [160, 64] },
    { "id": "email", "type": "backend", "label": "Email Agent", "sublabel": "inbox triage :8790", "pos": [790, 810], "size": [160, 64], "tag": "live" }
  ],
  "boundaries": [
    { "kind": "region", "label": "Ways in", "wraps": ["phone", "admin", "esp32", "dsh"], "pad": 26 },
    { "kind": "region", "label": "Peers and services", "wraps": ["ha", "spk", "net", "art", "pc", "web", "app", "email"], "pad": 26 }
  ],
  "connections": [
    { "id": "phone-in", "from": "phone", "to": "hub", "label": "voice or text" },
    { "id": "admin-in", "from": "admin", "to": "hub", "label": "restricted" },
    { "id": "esp-in", "from": "esp32", "to": "hub", "label": "command" },
    { "id": "dsh-in", "from": "dsh", "to": "hub", "label": "action request" },
    { "id": "to-ha", "from": "hub", "to": "ha", "label": "do it" },
    { "id": "to-spk", "from": "hub", "to": "spk", "label": "speak" },
    { "id": "to-net", "from": "hub", "to": "net", "label": "confirm" },
    { "id": "to-art", "from": "hub", "to": "art", "label": "screen control" },
    { "id": "to-pc", "from": "hub", "to": "pc", "label": "file job" },
    { "id": "to-web", "from": "hub", "to": "web", "label": "search" },
    { "id": "to-app", "from": "hub", "to": "app", "label": "notify" },
    { "id": "to-email", "from": "hub", "to": "email", "label": "ask + notify" },
    { "id": "pc-back", "from": "pc", "to": "hub", "label": "result", "variant": "dashed" }
  ]
}
