{
  "org": "Mercurius Labs",
  "note": "Regenerate with: python3 infra/scripts/build-rag.py",
  "counts": {
    "apps": 6,
    "documents": 43,
    "nodes": 39,
    "links": 50
  },
  "documents": [
    {
      "id": "doc-org",
      "type": "org",
      "title": "Mercurius Labs \u2014 estate overview",
      "text": "Mercurius Labs runs 6 application groups across two hosts. This Ubuntu Production Node is the live host for every public service; the Windows box (65.21.15.248, Hetzner) was retired on 2026-08-17. All public traffic arrives through Cloudflare Tunnels \u2014 there is no public IPv4 on this host.",
      "tags": [
        "cloudflare",
        "estate",
        "labs",
        "mercurius",
        "migration",
        "overview",
        "production",
        "tunnel"
      ]
    },
    {
      "id": "doc-srv-ubuntu",
      "type": "server",
      "title": "Ubuntu Production Node (this machine)",
      "text": "Ubuntu Production Node (this machine) (mercurius-OptiPlex-Tower-Plus-7020). OS: Ubuntu 26.04 LTS. Role: New primary production host (migration target). Runtime: Node 22, Python 3.14, PM2 7.x, cloudflared 2026.x. Public access: Cloudflare Tunnel (no port-forwarding; NAT-friendly). Status: active \u2014 sole production host; all public hostnames served here since 2026-08-17.",
      "tags": [
        "04",
        "1",
        "14",
        "168",
        "192",
        "2026",
        "211",
        "22",
        "26",
        "3",
        "7",
        "7020",
        "cloudflared",
        "host",
        "lts",
        "machine",
        "mercurius",
        "migration",
        "new",
        "node",
        "optiplex",
        "plus",
        "pm2",
        "primary",
        "production",
        "python",
        "target",
        "this",
        "tower",
        "ubuntu",
        "x"
      ]
    },
    {
      "id": "doc-srv-windows",
      "type": "server",
      "title": "Windows Server (RETIRED 2026-08-17)",
      "text": "Windows Server (RETIRED 2026-08-17) (WIN-86QA3S7CN2C). OS: Windows Server 2025 (NT 10.0.26100). Role: Former production host \u2014 migration source, retired. Runtime: Node 24, Python 3.12/3.14, PM2 6.0.14. Public access: Cloudflare Tunnels + Tailscale. Status: retired \u2014 all public traffic moved to the Ubuntu node on 2026-08-17.",
      "tags": [
        "0",
        "08",
        "10",
        "12",
        "14",
        "15",
        "17",
        "2025",
        "2026",
        "21",
        "24",
        "248",
        "26100",
        "3",
        "6",
        "65",
        "86qa3s7cn2c",
        "cancelled",
        "former",
        "hetzner",
        "host",
        "migration",
        "node",
        "nt",
        "pm2",
        "production",
        "python",
        "retired",
        "server",
        "source",
        "win",
        "windows"
      ]
    },
    {
      "id": "doc-svc-sabai-server",
      "type": "service",
      "title": "sabai-server  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-server' belongs to Sabai Command Centre. Kind: python. Entry point: server.py. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "centre",
        "command",
        "online",
        "py",
        "python",
        "sabai",
        "server"
      ]
    },
    {
      "id": "doc-svc-sabai-pulse-api",
      "type": "service",
      "title": "sabai-pulse-api  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-pulse-api' belongs to Sabai Command Centre. Kind: python/uvicorn. Entry point: tip-report-app/backend main:app. Port: 5050. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "5050",
        "api",
        "app",
        "backend",
        "centre",
        "command",
        "main",
        "online",
        "pulse",
        "python",
        "report",
        "sabai",
        "tip",
        "uvicorn"
      ]
    },
    {
      "id": "doc-svc-sabai-sales-bot",
      "type": "service",
      "title": "sabai-sales-bot  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-sales-bot' belongs to Sabai Command Centre. Kind: python. Entry point: sabai_sales_bot.py. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "bot",
        "centre",
        "command",
        "online",
        "py",
        "python",
        "sabai",
        "sales"
      ]
    },
    {
      "id": "doc-svc-sabai-reservations",
      "type": "service",
      "title": "sabai-reservations  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-reservations' belongs to Sabai Command Centre. Kind: node/tsx. Entry point: Reservations/Reservation_Bot src/index.ts. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "bot",
        "centre",
        "command",
        "index",
        "node",
        "online",
        "reservation",
        "reservations",
        "sabai",
        "src",
        "ts",
        "tsx"
      ]
    },
    {
      "id": "doc-svc-sabai-accountant-form",
      "type": "service",
      "title": "sabai-accountant-form  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-accountant-form' belongs to Sabai Command Centre. Kind: node. Entry point: sabai-accountant-form/server/index.js. Port: 3025. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "3025",
        "accountant",
        "centre",
        "command",
        "form",
        "index",
        "js",
        "node",
        "online",
        "sabai",
        "server"
      ]
    },
    {
      "id": "doc-svc-sabai-unified",
      "type": "service",
      "title": "sabai-unified  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-unified' belongs to Sabai Command Centre. Kind: next.js standalone. Entry point: .next-beta-deploy/standalone/server.js. Port: 3010. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "3010",
        "beta",
        "centre",
        "command",
        "deploy",
        "js",
        "next",
        "online",
        "sabai",
        "server",
        "standalone",
        "unified"
      ]
    },
    {
      "id": "doc-svc-sabai-sidework-cron",
      "type": "service",
      "title": "sabai-sidework-cron  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-sidework-cron' belongs to Sabai Command Centre. Kind: node cron. Entry point: scripts/run-sidework-cron.js. Port: none. Current status on source: stopped (2884 restarts). Migrate to Ubuntu: NO \u2014 dead/crash-looping, review before migrating.",
      "tags": [
        "2884",
        "centre",
        "command",
        "cron",
        "js",
        "node",
        "restarts",
        "run",
        "sabai",
        "scripts",
        "sidework",
        "stopped"
      ]
    },
    {
      "id": "doc-svc-sabai-broadcast-cron",
      "type": "service",
      "title": "sabai-broadcast-cron  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-broadcast-cron' belongs to Sabai Command Centre. Kind: node cron. Entry point: scripts/run-broadcast-cron.js. Port: none. Current status on source: stopped (3037 restarts). Migrate to Ubuntu: NO \u2014 dead/crash-looping, review before migrating.",
      "tags": [
        "3037",
        "broadcast",
        "centre",
        "command",
        "cron",
        "js",
        "node",
        "restarts",
        "run",
        "sabai",
        "scripts",
        "stopped"
      ]
    },
    {
      "id": "doc-svc-sabai-fridge-cron",
      "type": "service",
      "title": "sabai-fridge-cron  \u00b7  Sabai Command Centre",
      "text": "PM2 process 'sabai-fridge-cron' belongs to Sabai Command Centre. Kind: node cron. Entry point: scripts/run-fridge-temp-cron.js. Port: none. Current status on source: stopped (8284 restarts). Migrate to Ubuntu: NO \u2014 dead/crash-looping, review before migrating.",
      "tags": [
        "8284",
        "centre",
        "command",
        "cron",
        "fridge",
        "js",
        "node",
        "restarts",
        "run",
        "sabai",
        "scripts",
        "stopped",
        "temp"
      ]
    },
    {
      "id": "doc-sabai-command-centre",
      "type": "app",
      "title": "Sabai Command Centre",
      "text": "Sabai Command Centre \u2014 Restaurant operations hub for Sabai: ops server, tip/pulse reporting, sales bot, reservations, accountant form, unified Next.js portal, and scheduled cron jobs. Stack: Python (FastAPI/uvicorn) + Node/Next.js + tsx. Source path (Windows): C:\\Apps\\SabaiCommandCentre. Target path (Ubuntu): apps/sabai-command-centre. Services:\n- sabai-server (python) \u2014 online; entry: server.py; migrate=True\n- sabai-pulse-api (python/uvicorn) on port 5050 \u2014 online; entry: tip-report-app/backend main:app; migrate=True\n- sabai-sales-bot (python) \u2014 online; entry: sabai_sales_bot.py; migrate=True\n- sabai-reservations (node/tsx) \u2014 online; entry: Reservations/Reservation_Bot src/index.ts; migrate=True\n- sabai-accountant-form (node) on port 3025 \u2014 online; entry: sabai-accountant-form/server/index.js; migrate=True\n- sabai-unified (next.js standalone) on port 3010 \u2014 online; entry: .next-beta-deploy/standalone/server.js; migrate=True\n- sabai-sidework-cron (node cron) \u2014 stopped (2884 restarts); entry: scripts/run-sidework-cron.js; migrate=False\n- sabai-broadcast-cron (node cron) \u2014 stopped (3037 restarts); entry: scripts/run-broadcast-cron.js; migrate=False\n- sabai-fridge-cron (node cron) \u2014 stopped (8284 restarts); entry: scripts/run-fridge-temp-cron.js; migrate=False",
      "tags": [
        "accountant",
        "and",
        "apps",
        "bot",
        "c",
        "centre",
        "command",
        "cron",
        "fastapi",
        "for",
        "form",
        "hub",
        "jobs",
        "js",
        "next",
        "node",
        "operations",
        "ops",
        "portal",
        "pulse",
        "python",
        "reporting",
        "reservations",
        "restaurant",
        "sabai",
        "sabaicommandcentre",
        "sales",
        "scheduled",
        "server",
        "tip",
        "tsx",
        "unified",
        "uvicorn"
      ]
    },
    {
      "id": "doc-svc-ariadne-mcp",
      "type": "service",
      "title": "ariadne-mcp  \u00b7  Ariadne Notes App",
      "text": "PM2 process 'ariadne-mcp' belongs to Ariadne Notes App. Kind: node/mcp. Entry point: services/ariadne-mcp/dist/index.js. Port: 3200. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "3200",
        "app",
        "ariadne",
        "dist",
        "index",
        "js",
        "mcp",
        "node",
        "notes",
        "online",
        "services"
      ]
    },
    {
      "id": "doc-svc-ariande-capture-bot",
      "type": "service",
      "title": "ariande-capture-bot  \u00b7  Ariadne Notes App",
      "text": "PM2 process 'ariande-capture-bot' belongs to Ariadne Notes App. Kind: node. Entry point: services/capture-bot/index.js. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "app",
        "ariadne",
        "ariande",
        "bot",
        "capture",
        "index",
        "js",
        "node",
        "notes",
        "online",
        "services"
      ]
    },
    {
      "id": "doc-ariadne-notes",
      "type": "app",
      "title": "Ariadne Notes App",
      "text": "Ariadne Notes App \u2014 Note-capture and knowledge system: an MCP server exposing notes to AI agents, plus a Telegram/chat capture bot. Has a companion calendar backend. Stack: Node.js (MCP + cluster). Source path (Windows): C:\\Apps\\Ariande_notes_app  (+ Ariande_notes_app_cal_backend). Target path (Ubuntu): apps/ariadne-notes. Services:\n- ariadne-mcp (node/mcp) on port 3200 \u2014 online; entry: services/ariadne-mcp/dist/index.js; migrate=True\n- ariande-capture-bot (node) \u2014 online; entry: services/capture-bot/index.js; migrate=True",
      "tags": [
        "a",
        "agents",
        "ai",
        "an",
        "and",
        "app",
        "apps",
        "ariadne",
        "ariande",
        "backend",
        "bot",
        "c",
        "cal",
        "calendar",
        "capture",
        "chat",
        "cluster",
        "companion",
        "exposing",
        "has",
        "js",
        "knowledge",
        "mcp",
        "node",
        "note",
        "notes",
        "plus",
        "server",
        "system",
        "telegram",
        "to"
      ]
    },
    {
      "id": "doc-svc-mach-dashboard",
      "type": "service",
      "title": "mach-dashboard  \u00b7  Mach Dashboard",
      "text": "PM2 process 'mach-dashboard' belongs to Mach Dashboard. Kind: next.js. Entry point: next start -p 7000 -H 0.0.0.0. Port: 7000. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "0",
        "7000",
        "dashboard",
        "h",
        "js",
        "mach",
        "next",
        "online",
        "p",
        "start"
      ]
    },
    {
      "id": "doc-mach",
      "type": "app",
      "title": "Mach Dashboard",
      "text": "Mach Dashboard \u2014 Next.js 16 operations dashboard. Stack: Next.js 16. Source path (Windows): C:\\Apps\\Mach. Target path (Ubuntu): apps/mach. Services:\n- mach-dashboard (next.js) on port 7000 \u2014 online; entry: next start -p 7000 -H 0.0.0.0; migrate=True",
      "tags": [
        "16",
        "apps",
        "c",
        "dashboard",
        "js",
        "mach",
        "next",
        "operations"
      ]
    },
    {
      "id": "doc-svc-mercurius-ai-briefing",
      "type": "service",
      "title": "mercurius-ai-briefing  \u00b7  Mercurius Podcast / AI Briefing",
      "text": "PM2 process 'mercurius-ai-briefing' belongs to Mercurius Podcast / AI Briefing. Kind: python. Entry point: main.py. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "ai",
        "briefing",
        "main",
        "mercurius",
        "online",
        "podcast",
        "py",
        "python"
      ]
    },
    {
      "id": "doc-svc-mcast-api",
      "type": "service",
      "title": "mcast-api  \u00b7  Mercurius Podcast / AI Briefing",
      "text": "PM2 process 'mcast-api' belongs to Mercurius Podcast / AI Briefing. Kind: python. Entry point: api/run_server.py. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "ai",
        "api",
        "briefing",
        "mcast",
        "mercurius",
        "online",
        "podcast",
        "py",
        "python",
        "run",
        "server"
      ]
    },
    {
      "id": "doc-svc-mcast-web",
      "type": "service",
      "title": "mcast-web  \u00b7  Mercurius Podcast / AI Briefing",
      "text": "PM2 process 'mcast-web' belongs to Mercurius Podcast / AI Briefing. Kind: next.js. Entry point: web next start -H 127.0.0.1 -p 3120. Port: 3120. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "0",
        "1",
        "127",
        "3120",
        "ai",
        "briefing",
        "h",
        "js",
        "mcast",
        "mercurius",
        "next",
        "online",
        "p",
        "podcast",
        "start",
        "web"
      ]
    },
    {
      "id": "doc-mercurius-podcast",
      "type": "app",
      "title": "Mercurius Podcast / AI Briefing",
      "text": "Mercurius Podcast / AI Briefing \u2014 Mercurius AI audio briefing generator: Python engine (main.py) + API server + Next.js web frontend. Produces the Mercurius podcast/briefings. Stack: Python + Next.js 15. Source path (Windows): C:\\Apps\\Mercurius-Podcast. Target path (Ubuntu): apps/mercurius-podcast. Services:\n- mercurius-ai-briefing (python) \u2014 online; entry: main.py; migrate=True\n- mcast-api (python) \u2014 online; entry: api/run_server.py; migrate=True\n- mcast-web (next.js) on port 3120 \u2014 online; entry: web next start -H 127.0.0.1 -p 3120; migrate=True",
      "tags": [
        "15",
        "ai",
        "api",
        "apps",
        "audio",
        "briefing",
        "briefings",
        "c",
        "engine",
        "frontend",
        "generator",
        "js",
        "main",
        "mercurius",
        "next",
        "podcast",
        "produces",
        "py",
        "python",
        "server",
        "the",
        "web"
      ]
    },
    {
      "id": "doc-svc-midas-api",
      "type": "service",
      "title": "midas-api  \u00b7  Midas",
      "text": "PM2 process 'midas-api' belongs to Midas. Kind: node/tsx. Entry point: apps/api/src/index.ts. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "api",
        "apps",
        "index",
        "midas",
        "node",
        "online",
        "src",
        "ts",
        "tsx"
      ]
    },
    {
      "id": "doc-svc-midas-gateway",
      "type": "service",
      "title": "midas-gateway  \u00b7  Midas",
      "text": "PM2 process 'midas-gateway' belongs to Midas. Kind: node/tsx. Entry point: apps/gateway/src/index.ts. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "apps",
        "gateway",
        "index",
        "midas",
        "node",
        "online",
        "src",
        "ts",
        "tsx"
      ]
    },
    {
      "id": "doc-midas",
      "type": "app",
      "title": "Midas",
      "text": "Midas \u2014 Two-service monorepo: an API and a gateway, both run via tsx. Version 4.22.4. Stack: Node.js/TypeScript (tsx monorepo). Source path (Windows): C:\\Apps\\midas. Target path (Ubuntu): apps/midas. Services:\n- midas-api (node/tsx) \u2014 online; entry: apps/api/src/index.ts; migrate=True\n- midas-gateway (node/tsx) \u2014 online; entry: apps/gateway/src/index.ts; migrate=True",
      "tags": [
        "22",
        "4",
        "a",
        "an",
        "and",
        "api",
        "apps",
        "both",
        "c",
        "gateway",
        "js",
        "midas",
        "monorepo",
        "node",
        "run",
        "service",
        "tsx",
        "two",
        "typescript",
        "version",
        "via"
      ]
    },
    {
      "id": "doc-svc-pulse-nyc-portal",
      "type": "service",
      "title": "pulse-nyc-portal  \u00b7  Sabai Pulse NYC",
      "text": "PM2 process 'pulse-nyc-portal' belongs to Sabai Pulse NYC. Kind: next.js standalone. Entry point: .next-pulse/standalone/server.js. Port: 8310. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "8310",
        "js",
        "next",
        "nyc",
        "online",
        "portal",
        "pulse",
        "sabai",
        "server",
        "standalone"
      ]
    },
    {
      "id": "doc-svc-pulse-nyc-server",
      "type": "service",
      "title": "pulse-nyc-server  \u00b7  Sabai Pulse NYC",
      "text": "PM2 process 'pulse-nyc-server' belongs to Sabai Pulse NYC. Kind: python. Entry point: server.py. Port: 8320. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "8320",
        "nyc",
        "online",
        "pulse",
        "py",
        "python",
        "sabai",
        "server"
      ]
    },
    {
      "id": "doc-svc-pulse-nyc-tunnel",
      "type": "service",
      "title": "pulse-nyc-tunnel  \u00b7  Sabai Pulse NYC",
      "text": "PM2 process 'pulse-nyc-tunnel' belongs to Sabai Pulse NYC. Kind: cloudflared. Entry point: tunnel --url http://127.0.0.1:8310. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "0",
        "1",
        "127",
        "8310",
        "cloudflared",
        "http",
        "nyc",
        "online",
        "pulse",
        "sabai",
        "tunnel",
        "url"
      ]
    },
    {
      "id": "doc-svc-pulse-nyc-dash-tunnel",
      "type": "service",
      "title": "pulse-nyc-dash-tunnel  \u00b7  Sabai Pulse NYC",
      "text": "PM2 process 'pulse-nyc-dash-tunnel' belongs to Sabai Pulse NYC. Kind: cloudflared. Entry point: tunnel --url http://127.0.0.1:8320. Port: none. Current status on source: online. Migrate to Ubuntu: yes.",
      "tags": [
        "0",
        "1",
        "127",
        "8320",
        "cloudflared",
        "dash",
        "http",
        "nyc",
        "online",
        "pulse",
        "sabai",
        "tunnel",
        "url"
      ]
    },
    {
      "id": "doc-sabai-pulse-nyc",
      "type": "app",
      "title": "Sabai Pulse NYC",
      "text": "Sabai Pulse NYC \u2014 NYC deployment of the Sabai Pulse portal: a Next.js portal + Python server, each exposed to the internet through its own Cloudflare quick tunnel. Stack: Next.js + Python + cloudflared. Source path (Windows): C:\\apps\\sabai-pulse-nyc. Target path (Ubuntu): apps/sabai-pulse-nyc. Services:\n- pulse-nyc-portal (next.js standalone) on port 8310 \u2014 online; entry: .next-pulse/standalone/server.js; migrate=True\n- pulse-nyc-server (python) on port 8320 \u2014 online; entry: server.py; migrate=True\n- pulse-nyc-tunnel (cloudflared) \u2014 online; entry: tunnel --url http://127.0.0.1:8310; migrate=True\n- pulse-nyc-dash-tunnel (cloudflared) \u2014 online; entry: tunnel --url http://127.0.0.1:8320; migrate=True",
      "tags": [
        "a",
        "apps",
        "c",
        "cloudflare",
        "cloudflared",
        "deployment",
        "each",
        "exposed",
        "internet",
        "its",
        "js",
        "next",
        "nyc",
        "of",
        "own",
        "portal",
        "pulse",
        "python",
        "quick",
        "sabai",
        "server",
        "the",
        "through",
        "to",
        "tunnel"
      ]
    },
    {
      "id": "doc-svc-pm2-logrotate",
      "type": "platform",
      "title": "pm2-logrotate",
      "text": "pm2-logrotate \u2014 PM2 module that rotates process logs so disks don't fill. Migrate: True.",
      "tags": [
        "disks",
        "don",
        "fill",
        "logrotate",
        "logs",
        "module",
        "pm2",
        "process",
        "rotates",
        "so",
        "t",
        "that"
      ]
    },
    {
      "id": "doc-struct-apps",
      "type": "structure",
      "title": "Directory: apps/",
      "text": "apps/ \u2014 Business applications, one folder per app group. Mirrors C:\\Apps on the Windows source 1:1.",
      "tags": [
        "1",
        "app",
        "applications",
        "apps",
        "business",
        "c",
        "folder",
        "group",
        "mirrors",
        "on",
        "one",
        "per",
        "source",
        "the",
        "windows"
      ]
    },
    {
      "id": "doc-struct-website",
      "type": "structure",
      "title": "Directory: website/",
      "text": "website/ \u2014 This site: the Mercurius Labs overview + interactive RAG knowledge graph and machine API.",
      "tags": [
        "and",
        "api",
        "graph",
        "interactive",
        "knowledge",
        "labs",
        "machine",
        "mercurius",
        "overview",
        "rag",
        "site",
        "the",
        "this",
        "website"
      ]
    },
    {
      "id": "doc-struct-infra",
      "type": "structure",
      "title": "Directory: infra/",
      "text": "infra/ \u2014 Server infrastructure: PM2 ecosystem file, cloudflared tunnel config, and operational scripts.",
      "tags": [
        "and",
        "cloudflared",
        "config",
        "ecosystem",
        "file",
        "infra",
        "infrastructure",
        "operational",
        "pm2",
        "scripts",
        "server",
        "tunnel"
      ]
    },
    {
      "id": "doc-struct-docs",
      "type": "structure",
      "title": "Directory: docs/",
      "text": "docs/ \u2014 Human + agent documentation: STRUCTURE.md, MIGRATION.md, SERVER.md.",
      "tags": [
        "agent",
        "docs",
        "documentation",
        "human",
        "md",
        "migration",
        "server",
        "structure"
      ]
    },
    {
      "id": "doc-struct-data",
      "type": "structure",
      "title": "Directory: data/",
      "text": "data/ \u2014 Shared data, backups, and the inventory snapshot of the source server.",
      "tags": [
        "and",
        "backups",
        "data",
        "inventory",
        "of",
        "server",
        "shared",
        "snapshot",
        "source",
        "the"
      ]
    },
    {
      "id": "doc-struct-logs",
      "type": "structure",
      "title": "Directory: logs/",
      "text": "logs/ \u2014 Runtime logs for PM2-managed processes.",
      "tags": [
        "for",
        "logs",
        "managed",
        "pm2",
        "processes",
        "runtime"
      ]
    },
    {
      "id": "doc-convention-0",
      "type": "convention",
      "title": "Convention",
      "text": "One app == one folder under apps/<kebab-case-slug>. Never scatter an app across the tree.",
      "tags": [
        "across",
        "an",
        "app",
        "apps",
        "case",
        "folder",
        "kebab",
        "never",
        "one",
        "scatter",
        "slug",
        "the",
        "tree",
        "under"
      ]
    },
    {
      "id": "doc-convention-1",
      "type": "convention",
      "title": "Convention",
      "text": "Every long-running process is managed by PM2 and declared in infra/ecosystem.config.js. No app is started by hand.",
      "tags": [
        "and",
        "app",
        "by",
        "config",
        "declared",
        "ecosystem",
        "every",
        "hand",
        "in",
        "infra",
        "is",
        "js",
        "long",
        "managed",
        "no",
        "pm2",
        "process",
        "running",
        "started"
      ]
    },
    {
      "id": "doc-convention-2",
      "type": "convention",
      "title": "Convention",
      "text": "Public exposure is via Cloudflare Tunnel only. Do not open inbound firewall ports; the box is behind NAT.",
      "tags": [
        "behind",
        "box",
        "cloudflare",
        "do",
        "exposure",
        "firewall",
        "inbound",
        "is",
        "nat",
        "not",
        "only",
        "open",
        "ports",
        "public",
        "the",
        "tunnel",
        "via"
      ]
    },
    {
      "id": "doc-convention-3",
      "type": "convention",
      "title": "Convention",
      "text": "Secrets live in each app's own .env file, never committed and never in the ecosystem file in plaintext where avoidable.",
      "tags": [
        "and",
        "app",
        "avoidable",
        "committed",
        "each",
        "ecosystem",
        "env",
        "file",
        "in",
        "live",
        "never",
        "own",
        "plaintext",
        "s",
        "secrets",
        "the",
        "where"
      ]
    },
    {
      "id": "doc-convention-4",
      "type": "convention",
      "title": "Convention",
      "text": "Ports are unique per service and recorded in the knowledge base (this file). Check here before assigning a new port.",
      "tags": [
        "a",
        "and",
        "are",
        "assigning",
        "base",
        "before",
        "check",
        "file",
        "here",
        "in",
        "knowledge",
        "new",
        "per",
        "port",
        "ports",
        "recorded",
        "service",
        "the",
        "this",
        "unique"
      ]
    },
    {
      "id": "doc-convention-5",
      "type": "convention",
      "title": "Convention",
      "text": "Regenerate the RAG artifacts after any change: python3 infra/scripts/build-rag.py",
      "tags": [
        "after",
        "any",
        "artifacts",
        "build",
        "change",
        "infra",
        "py",
        "python3",
        "rag",
        "regenerate",
        "scripts",
        "the"
      ]
    }
  ]
}