{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "version": "1.2.0",
  "lastUpdated": "2026-06-10",
  "_canon": "Canonical source for FabricBloc network topology, access patterns, and route exposure. Hand-edited. hub/network.js renders the UI from this file; agents read it directly. Update this file, never the JS data inline. internal_tools[].function is the stable lookup key: canon names the function, this registry names the tool (parent ADR 0005). External SaaS tools get entries too, with access \"external\" and no Tailscale fields. Status lifecycle: \"needed\" (no tool picked yet — name is the [ tool needed ] placeholder; these rows ARE the human audit list) -> \"planned\" (tool decided, not yet live) -> \"ok\" (live). Unknown fields are omitted, never guessed.",
  "_consumers": [
    "hub/network.js (UI render)",
    "parent fabricbloc/llms.txt + CANON_INDEX.md (agent routing)",
    "Future: services-platform.json env-URL templating"
  ],

  "access_patterns": [
    {
      "id": "local_external",
      "url_template": "http://localhost:{host_port_local}",
      "zone": "local",
      "audience": "engineer (laptop, dev-stack running)",
      "auth": "none / dev defaults",
      "use_case": "Local probe of a single service in fabric-dev-stack. Host port from services-platform.json services[].host_port_local."
    },
    {
      "id": "local_internal",
      "url_template": "http://fabric-{service}:8094",
      "zone": "local",
      "audience": "service-to-service inside docker-compose",
      "auth": "HMAC (mirrors deployed internal-ALB posture)",
      "use_case": "Container-to-container DNS inside fabric-dev-stack. Mirrors deployed internal-ALB shape."
    },
    {
      "id": "dev_customer_kong",
      "url_template": "https://api.dev.fabricbloc.com/v1/{service}",
      "zone": "public",
      "audience": "external customer (Creator / Client / Consumer / Agent SDK)",
      "auth": "JWT (web/iOS/Android/macOS) or X-API-Key (server). HMAC injected by Kong, verified by service.",
      "use_case": "Customer-facing API on dev. Kong-routed. Same shape as prod, testnet chains."
    },
    {
      "id": "dev_internal_kong",
      "url_template": "http://internal-api.dev.fabricbloc.com:8000/v1/{service}",
      "zone": "internal",
      "audience": "service-to-service (East-West, VPC)",
      "auth": "X-Internal-Service-Key (per-caller) + HMAC. Layered: SG ACL + host-pin + key + HMAC.",
      "use_case": "East-West in dev. Internal-tier Kong listener pins host to internal-api.*. Never route customer traffic here."
    },
    {
      "id": "dev_tailscale_service_discovery",
      "url_template": "http://{service}.fabric.test:8094",
      "zone": "tailscale",
      "audience": "engineer probe + Prometheus scrape (Tailscale-attached only)",
      "auth": "network-fenced via Tailscale ACL (no app-layer auth)",
      "use_case": "Direct probe of a deployed dev service container, bypassing both Kong and the internal ALB. Engineer smoke tests, scrape configs. Reachable ONLY via the Tailscale sidecar in the cluster."
    },
    {
      "id": "prod_customer_kong",
      "url_template": "https://api.fabricbloc.com/v1/{service}",
      "zone": "public",
      "audience": "external customer",
      "auth": "JWT or X-API-Key + HMAC (same as dev_customer_kong, prod posture).",
      "use_case": "Customer-facing API on prod. Kong-routed. Mainnet chains in prod_mainnet, testnet chains in prod_testnet."
    },
    {
      "id": "prod_internal_kong",
      "url_template": "http://internal-api.fabricbloc.com:8000/v1/{service}",
      "zone": "internal",
      "audience": "service-to-service (East-West, VPC)",
      "auth": "X-Internal-Service-Key (per-caller) + HMAC.",
      "use_case": "East-West in prod. Same security model as dev_internal_kong."
    }
  ],

  "domains": [
    { "domain": "api.fabricbloc.com",              "zone": "public",    "purpose": "Production gateway — Creator/Client API traffic" },
    { "domain": "api.dev.fabricbloc.com",          "zone": "public",    "purpose": "Dev gateway — same as prod but testnet chains" },
    { "domain": "app.fabricbloc.com",              "zone": "public",    "purpose": "Production dashboard (fabricbloc-shell)" },
    { "domain": "app.dev.fabricbloc.com",          "zone": "public",    "purpose": "Dev dashboard" },
    { "domain": "internal-api.dev.fabricbloc.com", "zone": "internal",  "purpose": "Dev ALB — service-to-service only (VPC)" },
    { "domain": "internal-api.fabricbloc.com",     "zone": "internal",  "purpose": "Prod ALB — service-to-service only (VPC)" },
    { "domain": "*.fabric.test",                   "zone": "tailscale", "purpose": "Local dev DNS via Tailscale (Docker services)" },
    { "domain": "apigateway.fabric.test",          "zone": "tailscale", "purpose": "Local Kong gateway — Tailscale only" },
    { "domain": "docs.fabric.test",                "zone": "tailscale", "purpose": "Internal developer hub — Tailscale only" },
    { "domain": "hub.bloclabs.test",               "zone": "tailscale", "purpose": "Bloclabs AI/internal hub — private LLM infrastructure and tooling" },
    { "domain": "grafana.fabric.test",             "zone": "tailscale", "purpose": "Grafana dashboards — Tailscale only" },
    { "domain": "*.ts.net",                        "zone": "tailscale", "purpose": "Tailscale machine names — private network" }
  ],

  "internal_tools": [
    { "name": "ATLAS / OpenWebUI", "function": "llm workbench",     "role": "private LLM workbench",     "canonical": "https://ai.tail67dcea.ts.net",     "access": "tailscale", "status": "ok",             "note": "WEBUI_URL is HTTPS; http alias may have SPA issues" },
    { "name": "Postiz",            "function": "social publisher",  "role": "social publishing",         "canonical": "https://social.tail67dcea.ts.net", "alias": "http://social.bloclabs.test", "access": "tailscale", "status": "alias-broken",   "note": "http alias SPA black-screens — use canonical" },
    { "name": "Hub dashboard",     "function": "internal hub",      "role": "internal hub",              "canonical": "https://hub.tail67dcea.ts.net",    "alias": "http://hub.bloclabs.test",    "access": "tailscale", "status": "ok",             "note": "static; http alias OK" },
    { "name": "Docs reader",       "function": "docs reader",       "role": "docs reader",               "canonical": "https://docs.tail67dcea.ts.net",   "alias": "http://docs.bloclabs.test",   "access": "tailscale", "status": "alias-dns-fail", "note": "docs.bloclabs.test DNS fails — use canonical" },
    { "name": "claude.ai/design",  "function": "ui design tool",    "role": "UI design for the Ship UI workflow", "canonical": "https://claude.ai/design", "access": "external", "status": "ok", "note": "external SaaS — no Tailscale fields (parent ADR 0005)" },
    { "name": "CodeRabbit",        "function": "code review bot",   "role": "automated review on every PR",       "canonical": "https://coderabbit.ai",    "access": "external", "status": "ok", "note": "external SaaS — runs on GitHub PRs" },
    { "name": "Google Docs",       "function": "shared document tool", "role": "doc finalization + review (legal, content)", "canonical": "https://docs.google.com", "access": "external", "status": "ok", "note": "external SaaS — live doc links in parent workflows/publish-content.md Working Surfaces" },
    { "name": "Microsoft Excel",   "function": "content register",  "role": "content record + artifacts register", "access": "external", "status": "planned", "note": "decided target for the Store socket; current surface is the review doc (parent workflows/publish-content.md Working Surfaces)" },
    { "name": "PostHog",           "function": "product analytics", "role": "product + site analytics", "status": "planned", "note": "analytics tool of record; setup pending" },
    { "name": "[ tool needed ]",   "function": "image generation tool",   "role": "Design capability — generation call behind the brand prompt scaffold", "status": "needed", "note": "socket in parent workflows/capabilities/design.md; pick lands here first (fabricbloc#354)" },
    { "name": "[ tool needed ]",   "function": "video generation tool",   "role": "Design capability — generation call behind the brand prompt scaffold", "status": "needed", "note": "socket in parent workflows/capabilities/design.md; pick lands here first (fabricbloc#354)" },
    { "name": "[ tool needed ]",   "function": "long-form toolchain",     "role": "Publish Content — Content Producer writing station",                  "status": "needed", "note": "socket in parent workflows/publish-content.md; pick lands here first (fabricbloc#354)" },
    { "name": "[ tool needed ]",   "function": "analytics source",        "role": "Measure socket — stats pull + usage read (Publish Content, Ship UI)", "status": "needed", "note": "socket in parent workflows/DEPARTMENTS.md + ship-ui.md; pick lands here first (fabricbloc#354)" },
    { "name": "[ tool needed ]",   "function": "outreach adapter",        "role": "Sell — Publish socket for outreach",                                  "status": "needed", "note": "socket in parent workflows/DEPARTMENTS.md; pick lands here first (fabricbloc#354)" },
    { "name": "[ tool needed ]",   "function": "client-feedback intake",  "role": "Engineering — Measure socket",                                        "status": "needed", "note": "socket in parent workflows/DEPARTMENTS.md; pick lands here first (fabricbloc#354)" }
  ],

  "routes": [
    { "svc": "auth",         "path": "/v1/auth/health",            "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "auth",         "path": "/v1/auth/swagger",           "auth": "none",     "hosts": null,       "flag": true  },
    { "svc": "auth",         "path": "/v1/auth/schema",            "auth": "none",     "hosts": null,       "flag": true  },
    { "svc": "auth",         "path": "/v1/auth/redoc",             "auth": "none",     "hosts": null,       "flag": true  },
    { "svc": "auth",         "path": "/v1/auth/static",            "auth": "none",     "hosts": null,       "flag": true  },
    { "svc": "auth",         "path": "/v1/auth/signup",            "auth": "HMAC",     "hosts": null,       "flag": false },
    { "svc": "auth",         "path": "/v1/auth/login",             "auth": "HMAC",     "hosts": null,       "flag": false },
    { "svc": "auth",         "path": "/v1/auth/*",                 "auth": "JWT+HMAC", "hosts": null,       "flag": false },
    { "svc": "auth",         "path": "/v1/auth/webhooks",          "auth": "HMAC",     "hosts": "internal", "flag": false },
    { "svc": "nft",          "path": "/v1/nft/health",             "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "nft",          "path": "/v1/nft/swagger",            "auth": "none",     "hosts": null,       "flag": true  },
    { "svc": "nft",          "path": "/v1/nft/*",                  "auth": "JWT+HMAC", "hosts": null,       "flag": false },
    { "svc": "playground",   "path": "/v1/nft/playground",         "auth": "none",     "hosts": null,       "flag": true  },
    { "svc": "playground",   "path": "/v1/nft/playground/healthz", "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "wallet",       "path": "/v1/wallet/health",          "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "wallet",       "path": "/v1/wallet/chains",          "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "wallet",       "path": "/v1/wallet/permissions",     "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "wallet",       "path": "/v1/wallet/webhooks",        "auth": "HMAC",     "hosts": "internal", "flag": false },
    { "svc": "wallet",       "path": "/v1/wallet/*",               "auth": "JWT",      "hosts": null,       "flag": false },
    { "svc": "subscription", "path": "/v1/subscription/health",    "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "subscription", "path": "/v1/subscription/*",         "auth": "HMAC",     "hosts": null,       "flag": false },
    { "svc": "events",       "path": "/v1/events/health",          "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "events",       "path": "/v1/events/webhooks",        "auth": "BLOCKED",  "hosts": null,       "flag": false },
    { "svc": "events",       "path": "/v1/events/*",               "auth": "none",     "hosts": "internal", "flag": false },
    { "svc": "event-worker", "path": "/v1/event-worker/health",    "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "event-worker", "path": "/v1/event-worker/*",         "auth": "none",     "hosts": "internal", "flag": false },
    { "svc": "bundler",      "path": "/v1/bundler/health",         "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "bundler",      "path": "/v1/bundler/*",              "auth": "none",     "hosts": "internal", "flag": false },
    { "svc": "token",        "path": "/v1/tokens/*",               "auth": "none",     "hosts": null,       "flag": false },
    { "svc": "token",        "path": "/v1/tokenvalidator/*",       "auth": "JWT",      "hosts": null,       "flag": false },
    { "svc": "echo",         "path": "/v1/echo/*",                 "auth": "none",     "hosts": null,       "flag": true  }
  ],

  "discovery": {
    "live_url":     "http://docs.fabric.test/generated/v1/network.json",
    "offline_path": "docs/fabricbloc-docs/data/network.json",
    "ui_view":      "http://docs.fabric.test/#network"
  }
}
