{
  "schema_version": 1,
  "diagram_type": "architecture",
  "meta": {
    "title": "KTMS Architecture",
    "output": "diagrams/ktms.html",
    "quality_profile": "showcase",
    "repository": {
      "url": "https://github.com/ai-process/ktms",
      "revision": "fcb03af1ad102dcebf53dae7377a0999de9607e1"
    }
  },
  "components": [
    {
      "id": "clients",
      "type": "external",
      "label": "Client Services",
      "sublabel": "gRPC / OpenAI SDKs",
      "pos": [40, 260],
      "size": [130, 64],
      "sources": [
        { "path": "proto/ktms/v1/gateway.proto", "line": 1, "end_line": 40, "label": "gRPC contract" }
      ]
    },
    {
      "id": "ingress",
      "type": "security",
      "label": "gRPC & HTTP Ingress",
      "sublabel": ":9090 RPC / :8080 /v1",
      "tag": "Bearer ktms_*",
      "pos": [260, 260],
      "size": [160, 64],
      "sources": [
        { "path": "internal/server/grpc.go", "line": 29, "end_line": 41, "label": "gRPC auth & server" },
        { "path": "internal/server/openai.go", "line": 46, "end_line": 60, "label": "OpenAI HTTP /v1" }
      ]
    },
    {
      "id": "admin",
      "type": "backend",
      "label": "Admin Service",
      "sublabel": "Models, Rules, Keys",
      "tag": "admin scope",
      "pos": [260, 440],
      "size": [160, 64],
      "sources": [
        { "path": "internal/grpcapi/admin.go", "line": 26, "end_line": 72, "label": "Admin mutations" }
      ]
    },
    {
      "id": "router",
      "type": "backend",
      "label": "Router & Switchboard",
      "sublabel": "Rule match & fallback",
      "pos": [520, 260],
      "size": [160, 64],
      "sources": [
        { "path": "internal/grpcapi/handler.go", "line": 59, "end_line": 100, "label": "Proxy handler" },
        { "path": "internal/router/execute.go", "line": 98, "end_line": 120, "label": "Chain execution" }
      ]
    },
    {
      "id": "redis",
      "type": "database",
      "label": "Redis Throttle",
      "sublabel": "RPM & daily budgets",
      "tag": "fail-open",
      "pos": [390, 80],
      "size": [160, 64],
      "sources": [
        { "path": "internal/throttle/throttle.go", "line": 38, "end_line": 94, "label": "RPM & token budgets" }
      ]
    },
    {
      "id": "postgres",
      "type": "database",
      "label": "PostgreSQL + Keyring",
      "sublabel": "Config, keys, batches",
      "tag": "AES-256-GCM",
      "pos": [520, 440],
      "size": [160, 64],
      "sources": [
        { "path": "internal/proxydb/db.go", "line": 14, "end_line": 70, "label": "Postgres store" },
        { "path": "internal/registry/loader.go", "line": 99, "end_line": 144, "label": "Snapshot & decrypt" }
      ]
    },
    {
      "id": "poller",
      "type": "backend",
      "label": "Batch Poller",
      "sublabel": "Async jobs & retention",
      "pos": [520, 600],
      "size": [160, 64],
      "sources": [
        { "path": "internal/batch/poller.go", "line": 45, "end_line": 99, "label": "Batch polling loop" }
      ]
    },
    {
      "id": "vendors",
      "type": "cloud",
      "label": "LLM Vendors",
      "sublabel": "OpenAI / Anthropic / Gemini / DeepSeek / Ollama / TypeSafe",
      "pos": [810, 260],
      "size": [300, 64],
      "sources": [
        { "path": "internal/registry/vendors.go", "line": 10, "end_line": 79, "label": "Vendor registry" }
      ]
    },
    {
      "id": "tokenmeter",
      "type": "external",
      "label": "light-tokenmeter",
      "sublabel": "gRPC Usage Sink",
      "pos": [810, 80],
      "size": [170, 64],
      "sources": [
        { "path": "internal/usagereport/client.go", "line": 21, "end_line": 95, "label": "Usage collector client" }
      ]
    }
  ],
  "boundaries": [
    {
      "kind": "security-group",
      "label": "KTMS Runtime & Stores",
      "wraps": ["ingress", "admin", "router", "redis", "postgres", "poller"]
    }
  ],
  "connections": [
    { "id": "client-req", "from": "clients", "to": "ingress", "label": "gRPC / HTTP", "variant": "emphasis" },
    { "id": "ingress-route", "from": "ingress", "to": "router", "label": "Generate / Batch" },
    { "id": "ingress-admin", "from": "ingress", "to": "admin", "label": "Admin RPC", "variant": "security", "fromSide": "bottom", "toSide": "top", "labelDy": 24 },
    { "id": "admin-db", "from": "admin", "to": "postgres", "label": "Validated tx" },
    { "id": "router-redis", "from": "router", "to": "redis", "label": "RPM & budget", "fromSide": "top", "toSide": "bottom" },
    { "id": "router-db", "from": "router", "to": "postgres", "label": "Snapshot & batch", "fromSide": "bottom", "toSide": "top", "labelDy": 24 },
    { "id": "router-vendors", "from": "router", "to": "vendors", "label": "Fallback chain", "variant": "emphasis", "labelDy": 20 },
    { "id": "poller-db", "from": "poller", "to": "postgres", "label": "Lease & complete", "fromSide": "top", "toSide": "bottom" },
    { "id": "poller-vendors", "from": "poller", "to": "vendors", "label": "Poll batch", "variant": "dashed", "fromSide": "right", "toSide": "bottom" },
    { "id": "router-usage", "from": "router", "to": "tokenmeter", "label": "RecordUsageEvent", "variant": "dashed" }
  ],
  "cards": [
    {
      "dot": "cyan",
      "title": "Routing & Fallbacks",
      "items": [
        "Rules match on effort (LOW/MEDIUM/HIGH) and free-form attributes",
        "Ordered fallback chains retry across vendors before locking streams",
        "Supports text, JSON-schema, tools, streaming SSE/gRPC, 50%-off batches, TTS, image, and Judge"
      ]
    },
    {
      "dot": "emerald",
      "title": "Limits & Persistence",
      "items": [
        "Redis enforces per-key RPM and per-key / per-user daily token budgets",
        "Postgres stores models, rules, batch state, and AES-256-GCM vendor keys",
        "Replicas reload snapshots periodically or immediately on local admin writes"
      ]
    }
  ]
}
