{
 "format": "topoforge",
 "version": 1,
 "name": "inputloop: the shop, drawn",
 "settings": {
  "grid": 20,
  "snap": true,
  "showGrid": true,
  "guides": true,
  "netLabels": true,
  "ifLabels": true,
  "minimap": true,
  "defaultRoute": "orthogonal"
 },
 "pages": [
  {
   "id": "page-shop",
   "name": "The shop",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "shop-title",
     "type": "text",
     "x": -225,
     "y": -150,
     "w": 1200,
     "h": 110,
     "text": "The shop, drawn: what inputloop makes, where it runs, how work moves and who answers for it.\nEach project has its own page: the tabs along the bottom. Layers (right) switch views: people, projects, hosts, flows.\nEverything here is a drawing you can move, change and keep; it stays in this browser until you save it yourself (File).",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "shop-title"
     },
     "style": {}
    },
    {
     "id": "shop-you",
     "type": "user",
     "x": 450,
     "y": 338,
     "w": 110,
     "h": 110,
     "text": "You\n(this browser)",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "shop-you",
      "what": "What you draw here stays in this browser. Nothing is sent anywhere."
     },
     "style": {}
    },
    {
     "id": "shop-pillar-part",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 1010,
     "h": 298,
     "text": "Part: what is made",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "shop-pillar-part"
     },
     "style": {}
    },
    {
     "id": "shop-t-topology",
     "type": "rounded",
     "x": 15,
     "y": 40,
     "w": 150,
     "h": 48,
     "text": "topology",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-topology",
      "what": "Draws what you have, from the files that describe it. This is the home page."
     },
     "style": {}
    },
    {
     "id": "shop-t-obsmage",
     "type": "rounded",
     "x": 180,
     "y": 40,
     "w": 150,
     "h": 48,
     "text": "obsmage\n(not public yet)",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-obsmage",
      "what": "OBS Studio kept as readable settings, with a composer for multi-monitor scenes."
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "shop-t-obsmage-corwin",
     "type": "rounded",
     "x": 345,
     "y": 40,
     "w": 150,
     "h": 48,
     "text": "obsmage-corwin\n(not public yet)",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-obsmage-corwin",
      "what": "One person's rig for obsmage: their cameras, scenes and hosts."
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "shop-t-vibrobeast",
     "type": "rounded",
     "x": 510,
     "y": 40,
     "w": 150,
     "h": 48,
     "text": "vibrobeast",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-vibrobeast",
      "what": "A memory Claude keeps on your own computer, in plain files."
     },
     "style": {}
    },
    {
     "id": "shop-t-vibrobeast-skills",
     "type": "rounded",
     "x": 675,
     "y": 40,
     "w": 150,
     "h": 48,
     "text": "vibrobeast-skills",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-vibrobeast-skills",
      "what": "Written ways of working that Claude reads when a task calls for them."
     },
     "style": {}
    },
    {
     "id": "shop-t-nexttime-dev",
     "type": "rounded",
     "x": 840,
     "y": 40,
     "w": 150,
     "h": 48,
     "text": "NextTime-Dev",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-nexttime-dev",
      "what": "Time banking for Nextcloud: post a request, volunteer, earn hours, public ledger."
     },
     "style": {}
    },
    {
     "id": "shop-t-fourswordsfromthepast",
     "type": "rounded",
     "x": 15,
     "y": 102,
     "w": 150,
     "h": 48,
     "text": "fourSwordsFromThePast",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-fourswordsfromthepast",
      "what": "A Link to the Past as a native PC game with four-player drop-in co-op."
     },
     "style": {}
    },
    {
     "id": "shop-t-quantum3d5",
     "type": "rounded",
     "x": 180,
     "y": 102,
     "w": 150,
     "h": 48,
     "text": "quantum3d5",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-quantum3d5",
      "what": "A 3D atom and molecule simulator for learning chemistry and physics, offline."
     },
     "style": {}
    },
    {
     "id": "shop-t-suit-crm-6-5-php-sec-backport",
     "type": "rounded",
     "x": 345,
     "y": 102,
     "w": 150,
     "h": 48,
     "text": "suit-crm-6-5-php-sec-backport",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-suit-crm-6-5-php-sec-backport",
      "what": "Builds patched end-of-life PHP for SuiteCRM and proves SuiteCRM installs on it."
     },
     "style": {}
    },
    {
     "id": "shop-t-card-games-el",
     "type": "rounded",
     "x": 510,
     "y": 102,
     "w": 150,
     "h": 48,
     "text": "card-games.el",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-card-games-el",
      "what": "About thirty card games for GNU Emacs. Corwin's own, outside the inputloop org."
     },
     "style": {}
    },
    {
     "id": "shop-t-gitcha",
     "type": "rounded",
     "x": 675,
     "y": 102,
     "w": 150,
     "h": 48,
     "text": "gitcha",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gitcha",
      "what": "A free, self-hosted forge for people and their agents: packages, git, site hosting and CI, with two-factor sign-in on every account. Its README says which parts are tested only against stand-ins."
     },
     "style": {}
    },
    {
     "id": "shop-t-c4",
     "type": "rounded",
     "x": 840,
     "y": 102,
     "w": 150,
     "h": 48,
     "text": "c4\n(not public yet)",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-c4",
      "what": "The registry and signing layer the shop's other tools are filed under."
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "shop-t-github",
     "type": "rounded",
     "x": 15,
     "y": 164,
     "w": 150,
     "h": 48,
     "text": ".github",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-github",
      "what": "The organization's front page, and the conduct, support, security and accessibility files every repository inherits."
     },
     "style": {}
    },
    {
     "id": "shop-t-corwin-cloud",
     "type": "rounded",
     "x": 180,
     "y": 164,
     "w": 150,
     "h": 48,
     "text": "corwin.cloud",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-corwin-cloud",
      "what": "This site: the shell around topology, the tabs, the directory, the packages."
     },
     "style": {}
    },
    {
     "id": "shop-t-about-corwin",
     "type": "rounded",
     "x": 345,
     "y": 164,
     "w": 150,
     "h": 48,
     "text": "about-corwin",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-about-corwin",
      "what": "The source of the personal page at aboutcorwin.com."
     },
     "style": {}
    },
    {
     "id": "shop-t-ta",
     "type": "rounded",
     "x": 510,
     "y": 164,
     "w": 150,
     "h": 48,
     "text": "ta",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-ta",
      "what": "Technology Architecture: a portable operating-model kit, one file you hand an AI so it grounds itself in a host's own public identity and prefers that host's own canon."
     },
     "style": {}
    },
    {
     "id": "shop-t-three-phase-transcription",
     "type": "rounded",
     "x": 675,
     "y": 164,
     "w": 150,
     "h": 48,
     "text": "three-phase-transcription",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-three-phase-transcription",
      "what": "Recorded talk in three layers that each cite the one below: quotes by time, subjects drawn from the quotes, and a result in which every sentence can be followed back to the second it was said."
     },
     "style": {}
    },
    {
     "id": "shop-t-journal",
     "type": "rounded",
     "x": 840,
     "y": 164,
     "w": 150,
     "h": 48,
     "text": "journal",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-journal",
      "what": "The open shelf: dated, PGP-clearsigned accounts of the shop's work, beginning with two companion accounts of the 2026 GNU Savannah forensic review."
     },
     "style": {}
    },
    {
     "id": "shop-t-pubs",
     "type": "rounded",
     "x": 15,
     "y": 226,
     "w": 150,
     "h": 48,
     "text": "pubs",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-pubs",
      "what": "The reading room: a copy of the shop's publication shelf, with papers, posts, persona sheets, the canon and four game books, refreshed from the working folder."
     },
     "style": {}
    },
    {
     "id": "shop-t-egad",
     "type": "rounded",
     "x": 180,
     "y": 226,
     "w": 150,
     "h": 48,
     "text": "egad",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-egad",
      "what": "The house design system: one set of colours, type and spacing traced to GNU Emacs's Modus themes, carried to the web, Org, Emacs and the apps you theme."
     },
     "style": {}
    },
    {
     "id": "shop-t-hello-taxgnomes",
     "type": "rounded",
     "x": 345,
     "y": 226,
     "w": 150,
     "h": 48,
     "text": "hello-taxgnomes",
     "layer": "operational",
     "parent": "shop-pillar-part",
     "props": {},
     "data": {
      "tf_ref": "shop-t-hello-taxgnomes",
      "what": "The Gnemonic Word Mines: pick a word, move a four-way slider (simpler, denser, moodier, drier), and every sentence says who wrote it."
     },
     "style": {}
    },
    {
     "id": "shop-pillar-person",
     "type": "zone",
     "x": -225,
     "y": 0,
     "w": 185,
     "h": 236,
     "text": "Person: who answers",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "shop-pillar-person"
     },
     "style": {}
    },
    {
     "id": "shop-t-bryant",
     "type": "user",
     "x": -210,
     "y": 40,
     "w": 60,
     "h": 48,
     "text": "bryant",
     "layer": "abstract",
     "parent": "shop-pillar-person",
     "props": {},
     "data": {
      "tf_ref": "shop-t-bryant"
     },
     "style": {}
    },
    {
     "id": "shop-t-corwin",
     "type": "user",
     "x": -210,
     "y": 102,
     "w": 60,
     "h": 48,
     "text": "corwin",
     "layer": "abstract",
     "parent": "shop-pillar-person",
     "props": {},
     "data": {
      "tf_ref": "shop-t-corwin"
     },
     "style": {}
    },
    {
     "id": "shop-t-definitelynotaspren",
     "type": "user",
     "x": -210,
     "y": 164,
     "w": 60,
     "h": 48,
     "text": "definitelynotaspren",
     "layer": "abstract",
     "parent": "shop-pillar-person",
     "props": {},
     "data": {
      "tf_ref": "shop-t-definitelynotaspren"
     },
     "style": {}
    },
    {
     "id": "shop-pillar-place",
     "type": "zone",
     "x": 0,
     "y": 498,
     "w": 515,
     "h": 298,
     "text": "Place: where it runs and lives",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "shop-pillar-place"
     },
     "style": {}
    },
    {
     "id": "shop-t-workstation-0",
     "type": "server",
     "x": 15,
     "y": 538,
     "w": 150,
     "h": 48,
     "text": "a workstation",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-workstation-0"
     },
     "style": {}
    },
    {
     "id": "shop-t-code-bru-st",
     "type": "rounded",
     "x": 180,
     "y": 538,
     "w": 150,
     "h": 48,
     "text": "code.bru.st",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-code-bru-st"
     },
     "style": {}
    },
    {
     "id": "shop-t-github-com-inputloop",
     "type": "rounded",
     "x": 345,
     "y": 538,
     "w": 150,
     "h": 48,
     "text": "github.com/inputloop",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-github-com-inputloop"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw10",
     "type": "server",
     "x": 15,
     "y": 600,
     "w": 150,
     "h": 48,
     "text": "gw10",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw10"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw15",
     "type": "server",
     "x": 180,
     "y": 600,
     "w": 150,
     "h": 48,
     "text": "gw15",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw15"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw19",
     "type": "server",
     "x": 345,
     "y": 600,
     "w": 150,
     "h": 48,
     "text": "gw19",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw19"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw26",
     "type": "server",
     "x": 15,
     "y": 662,
     "w": 150,
     "h": 48,
     "text": "gw26",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw26"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw33",
     "type": "server",
     "x": 180,
     "y": 662,
     "w": 150,
     "h": 48,
     "text": "gw33",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw33"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw4",
     "type": "server",
     "x": 345,
     "y": 662,
     "w": 150,
     "h": 48,
     "text": "gw4",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw4"
     },
     "style": {}
    },
    {
     "id": "shop-t-gw7",
     "type": "server",
     "x": 15,
     "y": 724,
     "w": 150,
     "h": 48,
     "text": "gw7",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-gw7"
     },
     "style": {}
    },
    {
     "id": "shop-t-workstation-10",
     "type": "rounded",
     "x": 180,
     "y": 724,
     "w": 150,
     "h": 48,
     "text": "a workstation",
     "layer": "physical",
     "parent": "shop-pillar-place",
     "props": {},
     "data": {
      "tf_ref": "shop-t-workstation-10"
     },
     "style": {}
    },
    {
     "id": "shop-pillar-path",
     "type": "zone",
     "x": 555,
     "y": 498,
     "w": 515,
     "h": 112,
     "text": "Path: how work moves",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "shop-pillar-path"
     },
     "style": {}
    },
    {
     "id": "shop-t-atoms-roundtrip",
     "type": "parallelogram",
     "x": 570,
     "y": 538,
     "w": 150,
     "h": 48,
     "text": "atoms-roundtrip",
     "layer": "operational",
     "parent": "shop-pillar-path",
     "props": {},
     "data": {
      "tf_ref": "shop-t-atoms-roundtrip"
     },
     "style": {}
    },
    {
     "id": "shop-t-mirror",
     "type": "parallelogram",
     "x": 735,
     "y": 538,
     "w": 150,
     "h": 48,
     "text": "mirror",
     "layer": "operational",
     "parent": "shop-pillar-path",
     "props": {},
     "data": {
      "tf_ref": "shop-t-mirror"
     },
     "style": {}
    },
    {
     "id": "shop-t-recipe-tangle",
     "type": "parallelogram",
     "x": 900,
     "y": 538,
     "w": 150,
     "h": 48,
     "text": "recipe-tangle",
     "layer": "operational",
     "parent": "shop-pillar-path",
     "props": {},
     "data": {
      "tf_ref": "shop-t-recipe-tangle"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "shop-l1",
     "a": {
      "id": "shop-t-gw26",
      "port": null
     },
     "b": {
      "id": "shop-t-gw33",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l1"
     }
    },
    {
     "id": "shop-l2",
     "a": {
      "id": "shop-t-atoms-roundtrip",
      "port": null
     },
     "b": {
      "id": "shop-t-obsmage",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l2"
     }
    },
    {
     "id": "shop-l3",
     "a": {
      "id": "shop-t-mirror",
      "port": null
     },
     "b": {
      "id": "shop-t-code-bru-st",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l3"
     }
    },
    {
     "id": "shop-l4",
     "a": {
      "id": "shop-t-mirror",
      "port": null
     },
     "b": {
      "id": "shop-t-github-com-inputloop",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l4"
     }
    },
    {
     "id": "shop-l5",
     "a": {
      "id": "shop-t-recipe-tangle",
      "port": null
     },
     "b": {
      "id": "shop-t-topology",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l5"
     }
    },
    {
     "id": "shop-l6",
     "a": {
      "id": "shop-t-bryant",
      "port": null
     },
     "b": {
      "id": "shop-t-quantum3d5",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l6"
     }
    },
    {
     "id": "shop-l7",
     "a": {
      "id": "shop-t-corwin",
      "port": null
     },
     "b": {
      "id": "shop-t-topology",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l7"
     }
    },
    {
     "id": "shop-l8",
     "a": {
      "id": "shop-t-corwin",
      "port": null
     },
     "b": {
      "id": "shop-t-obsmage",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l8"
     }
    },
    {
     "id": "shop-l9",
     "a": {
      "id": "shop-t-corwin",
      "port": null
     },
     "b": {
      "id": "shop-t-vibrobeast",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l9"
     }
    },
    {
     "id": "shop-l10",
     "a": {
      "id": "shop-t-definitelynotaspren",
      "port": null
     },
     "b": {
      "id": "shop-t-nexttime-dev",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l10"
     }
    },
    {
     "id": "shop-l11",
     "a": {
      "id": "shop-t-nexttime-dev",
      "port": null
     },
     "b": {
      "id": "shop-t-definitelynotaspren",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l11"
     }
    },
    {
     "id": "shop-l12",
     "a": {
      "id": "shop-t-c4",
      "port": null
     },
     "b": {
      "id": "shop-t-gw33",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l12"
     }
    },
    {
     "id": "shop-l13",
     "a": {
      "id": "shop-t-corwin-cloud",
      "port": null
     },
     "b": {
      "id": "shop-t-workstation-10",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l13"
     }
    },
    {
     "id": "shop-l14",
     "a": {
      "id": "shop-t-obsmage",
      "port": null
     },
     "b": {
      "id": "shop-t-workstation-0",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l14"
     }
    },
    {
     "id": "shop-l15",
     "a": {
      "id": "shop-t-obsmage-corwin",
      "port": null
     },
     "b": {
      "id": "shop-t-obsmage",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l15"
     }
    },
    {
     "id": "shop-l16",
     "a": {
      "id": "shop-t-quantum3d5",
      "port": null
     },
     "b": {
      "id": "shop-t-bryant",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l16"
     }
    },
    {
     "id": "shop-l17",
     "a": {
      "id": "shop-t-topology",
      "port": null
     },
     "b": {
      "id": "shop-t-gw26",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l17"
     }
    },
    {
     "id": "shop-l18",
     "a": {
      "id": "shop-t-topology",
      "port": null
     },
     "b": {
      "id": "shop-t-obsmage",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l18"
     }
    },
    {
     "id": "shop-l19",
     "a": {
      "id": "shop-t-topology",
      "port": null
     },
     "b": {
      "id": "shop-t-vibrobeast",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l19"
     }
    },
    {
     "id": "shop-l20",
     "a": {
      "id": "shop-t-workstation-10",
      "port": null
     },
     "b": {
      "id": "shop-t-gw26",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l20"
     }
    },
    {
     "id": "shop-l21",
     "a": {
      "id": "shop-t-vibrobeast",
      "port": null
     },
     "b": {
      "id": "shop-t-gw33",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l21"
     }
    },
    {
     "id": "shop-l22",
     "a": {
      "id": "shop-t-vibrobeast",
      "port": null
     },
     "b": {
      "id": "shop-t-workstation-0",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l22"
     }
    },
    {
     "id": "shop-l23",
     "a": {
      "id": "shop-t-vibrobeast-skills",
      "port": null
     },
     "b": {
      "id": "shop-t-vibrobeast",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l23"
     }
    },
    {
     "id": "shop-l24",
     "a": {
      "id": "shop-you",
      "port": null
     },
     "b": {
      "id": "shop-t-corwin-cloud",
      "port": null
     },
     "route": "orthogonal",
     "text": "you are here",
     "layer": "abstract",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "shop-l24"
     }
    }
   ]
  },
  {
   "id": "page-topology",
   "name": "topology",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "topology-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "topology: Draws what you have, from the files that describe it. This is the home page.\nMade by: Corwin and Bryant; the drawing app, TopoForge, is Bryant's.\nRepository: https://github.com/inputloop/topology/tree/corwin\nDemo: corwin.cloud/demo/topology/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-title"
     },
     "style": {}
    },
    {
     "id": "topology-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 430,
     "text": "topology",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-zone",
      "what": "Draws what you have, from the files that describe it. This is the home page.",
      "credit": "Corwin and Bryant; the drawing app, TopoForge, is Bryant's",
      "public": "yes",
      "repo": "https://github.com/inputloop/topology/tree/corwin",
      "status": "Built on a working branch (corwin); the repository has been public since 2026-11-01. The browser app and the three packages exist. No tool that effects changes is shipped yet: with writing switched on, the server says so.",
      "licence": "Code GPL-3.0-or-later OR LGPL-3.0-or-later; prose CC-BY-SA-4.0.",
      "demo": "/demo/topology/",
      "described by": "examples/inputloop/projects/topology/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "topology-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "Draw what you have, from the files that describe it. Reads TOPOLOGY.md from the git repositories you keep, sorts what they say by place, part, path and person, and draws it with TopoForge.",
     "layer": "words",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-about"
     },
     "style": {}
    },
    {
     "id": "topology-part-topology-command",
     "type": "rect",
     "x": 30,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "topology-command",
     "layer": "operational",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-part-topology-command",
      "file": "perl/Topology/script/topology",
      "what": "perl/Topology/script/topology: reads repos.d, fetches the repositories listed, builds the graph as small files (r/ and c/, written twice) and serves it read-only. Modules: Config, Doc, Repo, Graph,..."
     },
     "style": {}
    },
    {
     "id": "topology-part-topology-www",
     "type": "rounded",
     "x": 230,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "topology-www",
     "layer": "operational",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-part-topology-www",
      "file": "www/",
      "what": "www/: the front door (index.html), the app (app.html), the stylesheets (css/topology.css, with egad's tokens in css/egad.css) and TopoForge. Works with no server (Zero Sharing). Checked for..."
     },
     "style": {}
    },
    {
     "id": "topology-part-topology-core",
     "type": "rect",
     "x": 430,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "topology-core",
     "layer": "operational",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-part-topology-core",
      "file": "www/js/topology-core.js",
      "what": "www/js/topology-core.js: the rules, not the pictures: reading TOPOLOGY.md, deciding what private parts show, the settings kept in the browser, and the CSV TopoForge opens. The same rules are written...",
      "depth": "1"
     },
     "style": {}
    },
    {
     "id": "topology-part-topoforge",
     "type": "rect",
     "x": 30,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "topoforge",
     "layer": "operational",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-part-topoforge",
      "file": "www/topoforge/index.html",
      "what": "TopoForge, the single-file network and diagram designer Bryant Frankford made. It is kept in this repository unchanged; improvements to it belong in Bryant's own copy first.",
      "depth": "1"
     },
     "style": {}
    },
    {
     "id": "topology-part-topology-core",
     "type": "rect",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "topology-core",
     "layer": "operational",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-part-topology-core",
      "file": "www/js/topology-core.js",
      "what": "www/js/topology-core.js: the rules, not the pictures: reading TOPOLOGY.md, deciding what private parts show, the settings kept in the browser, and the CSV TopoForge opens. The same rules are written..."
     },
     "style": {}
    },
    {
     "id": "topology-part-topoforge",
     "type": "rect",
     "x": 430,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "topoforge",
     "layer": "operational",
     "parent": "topology-zone",
     "props": {},
     "data": {
      "tf_ref": "topology-part-topoforge",
      "file": "www/topoforge/index.html",
      "what": "TopoForge, the single-file network and diagram designer Bryant Frankford made. It is kept in this repository unchanged; improvements to it belong in Bryant's own copy first."
     },
     "style": {}
    },
    {
     "id": "topology-near-gw26",
     "type": "server",
     "x": 760,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "gw26",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-near-gw26",
      "what": "The host topology is put on first: Debian 13, answering over HTTPS. The by-hand steps are in runcards/RUNCARD-2026-10-31-gw26.md. Nothing has been run there from this repository yet."
     },
     "style": {}
    },
    {
     "id": "topology-near-obsmage",
     "type": "rounded",
     "x": 760,
     "y": 150,
     "w": 150,
     "h": 90,
     "text": "obsmage\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-near-obsmage",
      "what": "OBS Studio kept as a living, literate configuration, with plugins that sense the room. A downstream build of OBS whose whole configuration is split into key/value atoms that a person or a tool can..."
     },
     "style": {}
    },
    {
     "id": "topology-near-vibrobeast",
     "type": "rounded",
     "x": 760,
     "y": 280,
     "w": 150,
     "h": 90,
     "text": "vibrobeast\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-near-vibrobeast",
      "what": "A memory Claude keeps on your own computer in plain files, with the skills, persona sheets and shared rooms built around it. Ships as a plugin carrying three stdio MCP servers, and works as a plugin..."
     },
     "style": {}
    },
    {
     "id": "topology-near-recipe-tangle",
     "type": "parallelogram",
     "x": 760,
     "y": 410,
     "w": 150,
     "h": 90,
     "text": "recipe-tangle",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-near-recipe-tangle",
      "what": "RECIPE.org is tangled by sh bin/tangle.sh into bin/, debian/ and etc/; tests/recipe-check.sh says whether the tree still agrees with the recipe. Edit the recipe, then tangle; never edit a tangled..."
     },
     "style": {}
    },
    {
     "id": "topology-who-corwin",
     "type": "user",
     "x": -260,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "corwin",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-who-corwin",
      "what": "Corwin Brust, corwin@bru.st. Commits are authored as him, unsigned; signing is his act, by hand. The agent's part in a commit is a byline on its last line."
     },
     "style": {}
    },
    {
     "id": "topology-flow-cap",
     "type": "text",
     "x": 0,
     "y": 600,
     "w": 900,
     "h": 30,
     "text": "How it works: From repositories to a drawing.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-flow-cap"
     },
     "style": {}
    },
    {
     "id": "topology-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Repositories\nthe ones you keep",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "topology-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "TOPOLOGY.md\neach says what it is",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "topology-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Cells\none fact per cell, kept twice",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "topology-flow-3",
     "type": "rounded",
     "x": 690,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Four pillars\nplace, part, path, person",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "topology-flow-4",
     "type": "ellipse",
     "x": 920,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "TopoForge\nthe drawing you move",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-flow-4",
      "step": "5"
     },
     "style": {}
    },
    {
     "id": "topology-how",
     "type": "text",
     "x": 0,
     "y": 760,
     "w": 1100,
     "h": 60,
     "text": "Cells are written as two files, one filed by row and one by column, and a check reads both sides and reports any cell they disagree on. A rebuild makes a fresh graph beside the old one and swaps it in, so nothing a repository stopped saying survives.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "topology-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "topology-l1",
     "a": {
      "id": "topology-zone",
      "port": null
     },
     "b": {
      "id": "topology-near-gw26",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l1"
     }
    },
    {
     "id": "topology-l2",
     "a": {
      "id": "topology-zone",
      "port": null
     },
     "b": {
      "id": "topology-near-obsmage",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l2"
     }
    },
    {
     "id": "topology-l3",
     "a": {
      "id": "topology-zone",
      "port": null
     },
     "b": {
      "id": "topology-near-vibrobeast",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l3"
     }
    },
    {
     "id": "topology-l4",
     "a": {
      "id": "topology-zone",
      "port": null
     },
     "b": {
      "id": "topology-near-recipe-tangle",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l4"
     }
    },
    {
     "id": "topology-l5",
     "a": {
      "id": "topology-who-corwin",
      "port": null
     },
     "b": {
      "id": "topology-zone",
      "port": null
     },
     "route": "orthogonal",
     "text": "answers for",
     "layer": "abstract",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l5"
     }
    },
    {
     "id": "topology-l6",
     "a": {
      "id": "topology-flow-0",
      "port": null
     },
     "b": {
      "id": "topology-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l6"
     }
    },
    {
     "id": "topology-l7",
     "a": {
      "id": "topology-flow-1",
      "port": null
     },
     "b": {
      "id": "topology-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l7"
     }
    },
    {
     "id": "topology-l8",
     "a": {
      "id": "topology-flow-2",
      "port": null
     },
     "b": {
      "id": "topology-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l8"
     }
    },
    {
     "id": "topology-l9",
     "a": {
      "id": "topology-flow-3",
      "port": null
     },
     "b": {
      "id": "topology-flow-4",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "topology-l9"
     }
    }
   ]
  },
  {
   "id": "page-obsmage",
   "name": "obsmage",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "obsmage-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "obsmage: OBS Studio kept as readable settings, with a composer for multi-monitor scenes.\nMade by: Corwin and Bryant.\nRepository: not public yet.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-title"
     },
     "style": {}
    },
    {
     "id": "obsmage-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 430,
     "text": "obsmage",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-zone",
      "what": "OBS Studio kept as readable settings, with a composer for multi-monitor scenes.",
      "credit": "Corwin and Bryant",
      "public": "not public yet",
      "status": "Today: the atom splitter and joiner, the roll-up, the composer and the build healer, each with tests. Designed: hand control and calibration. Private for now; this tab is here because it is the lab's main focus.",
      "licence": "Code GPL-3.0-or-later OR LGPL-3.0-or-later; prose CC-BY-SA-4.0.",
      "described by": "examples/inputloop/projects/obsmage/TOPOLOGY.md"
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "obsmage-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "OBS Studio kept as a living, literate configuration, with plugins that sense the room. A downstream build of OBS whose whole configuration is split into key/value atoms that a person or a tool can read, diff, version and change, with credentials kept out of everything published.\n[1 private part kept back: this site never shows private parts.]",
     "layer": "words",
     "parent": "obsmage-zone",
     "props": {},
     "data": {
      "tf_ref": "obsmage-about"
     },
     "style": {}
    },
    {
     "id": "obsmage-part-omg-atoms",
     "type": "rect",
     "x": 30,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "omg-atoms (built)",
     "layer": "operational",
     "parent": "obsmage-zone",
     "props": {},
     "data": {
      "tf_ref": "obsmage-part-omg-atoms",
      "file": "tools/omg_atoms.py",
      "what": "tools/omgatoms.py: find, verify, split, join, rollup, unroll over an OBS configuration. Secret atoms go to a local vault (mode 600) and appear in anything published only as @secret(len=N). Tests:..."
     },
     "style": {}
    },
    {
     "id": "obsmage-part-omg-compose",
     "type": "rect",
     "x": 230,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "omg-compose (built; plugin wiring open)",
     "layer": "operational",
     "parent": "obsmage-zone",
     "props": {},
     "data": {
      "tf_ref": "obsmage-part-omg-compose",
      "file": "tools/omg_compose.py",
      "what": "tools/omgcompose.py: audits which captures bind to a display by index (fragile) or by stable id, rebinds them, keeps screen-map scenes current. Never deletes; hides and reports. Every write is behind..."
     },
     "style": {}
    },
    {
     "id": "obsmage-part-omg-heal",
     "type": "rect",
     "x": 430,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "omg-heal (built)",
     "layer": "operational",
     "parent": "obsmage-zone",
     "props": {},
     "data": {
      "tf_ref": "obsmage-part-omg-heal",
      "file": "packaging/healer/omg-heal",
      "what": "packaging/healer/omg-heal: the build healer. Follows main (the default), project meta, tags, or signed tags only. Test: tests/e2e-healer.sh."
     },
     "style": {}
    },
    {
     "id": "obsmage-part-obsmage-core",
     "type": "rect",
     "x": 30,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "obsmage-core (skeleton)",
     "layer": "abstract",
     "parent": "obsmage-zone",
     "props": {},
     "data": {
      "tf_ref": "obsmage-part-obsmage-core",
      "file": "plugin/obsmage-core/",
      "what": "The core plugin skeleton: where AR desktop control and camera calibration will attach (docs/AR-CONTROL.md). Designed, not built."
     },
     "style": {}
    },
    {
     "id": "obsmage-near-workstation-0",
     "type": "server",
     "x": 760,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "a workstation",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-near-workstation-0"
     },
     "style": {}
    },
    {
     "id": "obsmage-near-atoms-roundtrip",
     "type": "parallelogram",
     "x": 760,
     "y": 150,
     "w": 150,
     "h": 90,
     "text": "atoms-roundtrip",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-near-atoms-roundtrip",
      "what": "An OBS configuration is split into atoms, rolled up into STAGES.md, edited by a person or a tool, and unrolled back. If both sides changed since the pin, the tool stops and names the atoms in..."
     },
     "style": {}
    },
    {
     "id": "obsmage-who-corwin",
     "type": "user",
     "x": -260,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "corwin",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-who-corwin",
      "what": "Corwin Brust, corwin@bru.st. Commits are authored as him, unsigned; signing is his act, by hand. The agent's part in a commit is a byline on its last line."
     },
     "style": {}
    },
    {
     "id": "obsmage-near-obsmage-corwin",
     "type": "rounded",
     "x": 760,
     "y": 280,
     "w": 150,
     "h": 90,
     "text": "obsmage-corwin\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-near-obsmage-corwin",
      "what": "A personal recipe repository that consumes obsmage. It is private: this page says only that it exists."
     },
     "style": {}
    },
    {
     "id": "obsmage-near-topology",
     "type": "rounded",
     "x": 760,
     "y": 410,
     "w": 150,
     "h": 90,
     "text": "topology\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-near-topology",
      "what": "Draw what you have, from the files that describe it. Reads TOPOLOGY.md from the git repositories you keep, sorts what they say by place, part, path and person, and draws it with TopoForge."
     },
     "style": {}
    },
    {
     "id": "obsmage-flow-cap",
     "type": "text",
     "x": 0,
     "y": 600,
     "w": 900,
     "h": 30,
     "text": "How it works: How a setting travels.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-flow-cap"
     },
     "style": {}
    },
    {
     "id": "obsmage-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "OBS configuration\nthe folder OBS keeps",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "obsmage-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Atoms\nsplit, lossless, secrets held back",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "obsmage-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "STAGES.md\none file to read and edit",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "obsmage-flow-3",
     "type": "rounded",
     "x": 690,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Unroll\nback to atoms, conflicts shown",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "obsmage-flow-4",
     "type": "ellipse",
     "x": 920,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Join\nyour OBS files, with a dated backup",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-flow-4",
      "step": "5"
     },
     "style": {}
    },
    {
     "id": "obsmage-how",
     "type": "text",
     "x": 0,
     "y": 760,
     "w": 1100,
     "h": 60,
     "text": "Nothing writes your live OBS files without an explicit write flag, and every write leaves a dated backup. A tool may point out a pattern, such as settings that always change together; a person decides.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "obsmage-l1",
     "a": {
      "id": "obsmage-zone",
      "port": null
     },
     "b": {
      "id": "obsmage-near-workstation-0",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l1"
     }
    },
    {
     "id": "obsmage-l2",
     "a": {
      "id": "obsmage-zone",
      "port": null
     },
     "b": {
      "id": "obsmage-near-atoms-roundtrip",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l2"
     }
    },
    {
     "id": "obsmage-l3",
     "a": {
      "id": "obsmage-who-corwin",
      "port": null
     },
     "b": {
      "id": "obsmage-zone",
      "port": null
     },
     "route": "orthogonal",
     "text": "answers for",
     "layer": "abstract",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l3"
     }
    },
    {
     "id": "obsmage-l4",
     "a": {
      "id": "obsmage-zone",
      "port": null
     },
     "b": {
      "id": "obsmage-near-obsmage-corwin",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l4"
     }
    },
    {
     "id": "obsmage-l5",
     "a": {
      "id": "obsmage-zone",
      "port": null
     },
     "b": {
      "id": "obsmage-near-topology",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l5"
     }
    },
    {
     "id": "obsmage-l6",
     "a": {
      "id": "obsmage-flow-0",
      "port": null
     },
     "b": {
      "id": "obsmage-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l6"
     }
    },
    {
     "id": "obsmage-l7",
     "a": {
      "id": "obsmage-flow-1",
      "port": null
     },
     "b": {
      "id": "obsmage-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l7"
     }
    },
    {
     "id": "obsmage-l8",
     "a": {
      "id": "obsmage-flow-2",
      "port": null
     },
     "b": {
      "id": "obsmage-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l8"
     }
    },
    {
     "id": "obsmage-l9",
     "a": {
      "id": "obsmage-flow-3",
      "port": null
     },
     "b": {
      "id": "obsmage-flow-4",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-l9"
     }
    }
   ]
  },
  {
   "id": "page-obsmage-workflows",
   "name": "obsmage: workflows",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "obsmage-workflows-title",
     "type": "text",
     "x": 0,
     "y": -120,
     "w": 1200,
     "h": 80,
     "text": "obsmage: three things you do with it, each as the steps it takes.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-title"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-roundtrip-cap",
     "type": "text",
     "x": 0,
     "y": 0,
     "w": 900,
     "h": 30,
     "text": "Edit by hand: Read and change a whole setup as one file.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-roundtrip-cap"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-roundtrip-0",
     "type": "ellipse",
     "x": 0,
     "y": 50,
     "w": 170,
     "h": 80,
     "text": "Split\nthe config into atoms",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-roundtrip-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-roundtrip-1",
     "type": "rounded",
     "x": 230,
     "y": 50,
     "w": 170,
     "h": 80,
     "text": "Roll up\natoms into STAGES.md",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-roundtrip-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-roundtrip-2",
     "type": "rounded",
     "x": 460,
     "y": 50,
     "w": 170,
     "h": 80,
     "text": "Edit\nin any editor",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-roundtrip-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-roundtrip-3",
     "type": "rounded",
     "x": 690,
     "y": 50,
     "w": 170,
     "h": 80,
     "text": "Unroll\nconflicts checked both ways",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-roundtrip-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-roundtrip-4",
     "type": "ellipse",
     "x": 920,
     "y": 50,
     "w": 170,
     "h": 80,
     "text": "Join\nback to OBS files, backed up",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-roundtrip-4",
      "step": "5"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-compose-cap",
     "type": "text",
     "x": 0,
     "y": 200,
     "w": 900,
     "h": 30,
     "text": "Compose screen maps: Keep the hand-made scenes in step with the displays you have.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-compose-cap"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-compose-0",
     "type": "ellipse",
     "x": 0,
     "y": 250,
     "w": 170,
     "h": 80,
     "text": "Read displays\nthe live machine, or a file",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-compose-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-compose-1",
     "type": "rounded",
     "x": 230,
     "y": 250,
     "w": 170,
     "h": 80,
     "text": "Audit\nwhich captures bind by number",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-compose-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-compose-2",
     "type": "rounded",
     "x": 460,
     "y": 250,
     "w": 170,
     "h": 80,
     "text": "Bind\nby stable id, keeping the number",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-compose-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-compose-3",
     "type": "rounded",
     "x": 690,
     "y": 250,
     "w": 170,
     "h": 80,
     "text": "Place\nnew displays; yours stay put",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-compose-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-compose-4",
     "type": "ellipse",
     "x": 920,
     "y": 250,
     "w": 170,
     "h": 80,
     "text": "Report\nevery hide, in words",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-compose-4",
      "step": "5"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-heal-cap",
     "type": "text",
     "x": 0,
     "y": 400,
     "w": 900,
     "h": 30,
     "text": "Heal builds: Keep Debian and source builds of OBS current.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-heal-cap"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-heal-0",
     "type": "ellipse",
     "x": 0,
     "y": 450,
     "w": 170,
     "h": 80,
     "text": "Plan\nsay what it would build and why",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-heal-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-heal-1",
     "type": "rounded",
     "x": 230,
     "y": 450,
     "w": 170,
     "h": 80,
     "text": "Pull\nthe followed ref, if asked",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-heal-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-heal-2",
     "type": "rounded",
     "x": 460,
     "y": 450,
     "w": 170,
     "h": 80,
     "text": "Patch\nour queue for that version",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-heal-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-heal-3",
     "type": "rounded",
     "x": 690,
     "y": 450,
     "w": 170,
     "h": 80,
     "text": "Build\nDebian or source",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-heal-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "obsmage-workflows-heal-4",
     "type": "ellipse",
     "x": 920,
     "y": 450,
     "w": 170,
     "h": 80,
     "text": "Record\nwhen, which commit, the result",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-workflows-heal-4",
      "step": "5"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "obsmage-workflows-l1",
     "a": {
      "id": "obsmage-workflows-roundtrip-0",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-roundtrip-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l1"
     }
    },
    {
     "id": "obsmage-workflows-l2",
     "a": {
      "id": "obsmage-workflows-roundtrip-1",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-roundtrip-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l2"
     }
    },
    {
     "id": "obsmage-workflows-l3",
     "a": {
      "id": "obsmage-workflows-roundtrip-2",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-roundtrip-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l3"
     }
    },
    {
     "id": "obsmage-workflows-l4",
     "a": {
      "id": "obsmage-workflows-roundtrip-3",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-roundtrip-4",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l4"
     }
    },
    {
     "id": "obsmage-workflows-l5",
     "a": {
      "id": "obsmage-workflows-compose-0",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-compose-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l5"
     }
    },
    {
     "id": "obsmage-workflows-l6",
     "a": {
      "id": "obsmage-workflows-compose-1",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-compose-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l6"
     }
    },
    {
     "id": "obsmage-workflows-l7",
     "a": {
      "id": "obsmage-workflows-compose-2",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-compose-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l7"
     }
    },
    {
     "id": "obsmage-workflows-l8",
     "a": {
      "id": "obsmage-workflows-compose-3",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-compose-4",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l8"
     }
    },
    {
     "id": "obsmage-workflows-l9",
     "a": {
      "id": "obsmage-workflows-heal-0",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-heal-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l9"
     }
    },
    {
     "id": "obsmage-workflows-l10",
     "a": {
      "id": "obsmage-workflows-heal-1",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-heal-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l10"
     }
    },
    {
     "id": "obsmage-workflows-l11",
     "a": {
      "id": "obsmage-workflows-heal-2",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-heal-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l11"
     }
    },
    {
     "id": "obsmage-workflows-l12",
     "a": {
      "id": "obsmage-workflows-heal-3",
      "port": null
     },
     "b": {
      "id": "obsmage-workflows-heal-4",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-workflows-l12"
     }
    }
   ]
  },
  {
   "id": "page-obsmage-scenes",
   "name": "obsmage: scenes",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "obsmage-scenes-title",
     "type": "text",
     "x": 0,
     "y": -140,
     "w": 1200,
     "h": 100,
     "text": "A scene is a layout OBS shows. A screen map is a scene that draws every monitor you own as a rectangle, so you can cut between them.\nThe names and sizes here are made up for the picture.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-title"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-desk",
     "type": "site",
     "x": 0,
     "y": 0,
     "w": 880,
     "h": 280,
     "text": "Scene: Screenmap (the canvas)",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-desk"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-display-left",
     "type": "rect",
     "x": 20,
     "y": 50,
     "w": 200,
     "h": 140,
     "text": "Left",
     "layer": "physical",
     "parent": "obsmage-scenes-desk",
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-display-left"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-display-centre",
     "type": "rect",
     "x": 230,
     "y": 50,
     "w": 270,
     "h": 140,
     "text": "Centre",
     "layer": "physical",
     "parent": "obsmage-scenes-desk",
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-display-centre"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-display-right",
     "type": "rect",
     "x": 510,
     "y": 50,
     "w": 200,
     "h": 140,
     "text": "Right",
     "layer": "physical",
     "parent": "obsmage-scenes-desk",
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-display-right"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-display-spare",
     "type": "rect",
     "x": 720,
     "y": 50,
     "w": 130,
     "h": 140,
     "text": "Spare\nunplugged: hidden, kept",
     "layer": "physical",
     "parent": "obsmage-scenes-desk",
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-display-spare"
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "obsmage-scenes-point-0",
     "type": "note",
     "x": 0,
     "y": 330,
     "w": 420,
     "h": 110,
     "text": "Bound by number\nFragile. Add, remove or reorder a display and the same number points at another screen.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-point-0"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-point-1",
     "type": "note",
     "x": 450,
     "y": 330,
     "w": 420,
     "h": 110,
     "text": "Bound by stable id\nFollows the hardware. The composer adds this and keeps the number for older OBS.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-point-1"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-point-2",
     "type": "note",
     "x": 0,
     "y": 460,
     "w": 420,
     "h": 110,
     "text": "Never deleted\nA display that goes away hides its item and marks it; when it returns, it is shown again. Items you hid yourself are left alone.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-point-2"
     },
     "style": {}
    },
    {
     "id": "obsmage-scenes-point-3",
     "type": "note",
     "x": 450,
     "y": 460,
     "w": 420,
     "h": 110,
     "text": "Your layout teaches it\ninfer-layout reads a hand-made screen map and uses it as the physical layout. No tape measure.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-scenes-point-3"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-obsmage-corwin",
   "name": "obsmage-corwin",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "obsmage-corwin-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "obsmage-corwin: One person's rig for obsmage: their cameras, scenes and hosts.\nMade by: Corwin.\nRepository: not public yet.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-corwin-title"
     },
     "style": {}
    },
    {
     "id": "obsmage-corwin-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "obsmage-corwin",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-corwin-zone",
      "what": "One person's rig for obsmage: their cameras, scenes and hosts.",
      "credit": "Corwin",
      "public": "not public yet",
      "described by": "examples/inputloop/projects/obsmage-corwin/TOPOLOGY.md"
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "obsmage-corwin-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A personal recipe repository that consumes obsmage. It is private: this page says only that it exists.\n[1 private part kept back: this site never shows private parts.]",
     "layer": "words",
     "parent": "obsmage-corwin-zone",
     "props": {},
     "data": {
      "tf_ref": "obsmage-corwin-about"
     },
     "style": {}
    },
    {
     "id": "obsmage-corwin-near-obsmage",
     "type": "rounded",
     "x": 760,
     "y": 20,
     "w": 150,
     "h": 90,
     "text": "obsmage\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "obsmage-corwin-near-obsmage",
      "what": "OBS Studio kept as a living, literate configuration, with plugins that sense the room. A downstream build of OBS whose whole configuration is split into key/value atoms that a person or a tool can..."
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "obsmage-corwin-l1",
     "a": {
      "id": "obsmage-corwin-zone",
      "port": null
     },
     "b": {
      "id": "obsmage-corwin-near-obsmage",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "obsmage-corwin-l1"
     }
    }
   ]
  },
  {
   "id": "page-vibrobeast",
   "name": "vibrobeast",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "vibrobeast-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "vibrobeast: A memory Claude keeps on your own computer, in plain files.\nMade by: Corwin and Bryant.\nRepository: https://github.com/inputloop/vibrobeast\nDemo: corwin.cloud/demo/vibrobeast/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-title"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 310,
     "text": "vibrobeast",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-zone",
      "what": "A memory Claude keeps on your own computer, in plain files.",
      "credit": "Corwin and Bryant",
      "public": "yes",
      "repo": "https://github.com/inputloop/vibrobeast",
      "status": "Works for its makers. Not yet reviewed by anyone outside the two of us; its own page says that first.",
      "licence": "Writing CC-BY-SA-4.0; scripts GPL-3.0-or-later.",
      "demo": "/demo/vibrobeast/",
      "described by": "examples/inputloop/projects/vibrobeast/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A memory Claude keeps on your own computer in plain files, with the skills, persona sheets and shared rooms built around it. Ships as a plugin carrying three stdio MCP servers, and works as a plugin marketplace for people who add the repository. Its skills are kept in their own repository, vibrobeast-skills, so a person can take the skills without the servers.",
     "layer": "words",
     "parent": "vibrobeast-zone",
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-about"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-part-vibrobeast-skills",
     "type": "container",
     "x": 30,
     "y": 190,
     "w": 170,
     "h": 80,
     "text": "vibrobeast-skills",
     "layer": "operational",
     "parent": "vibrobeast-zone",
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-part-vibrobeast-skills",
      "what": "Written ways of working that Claude reads when a task calls for them; each skill is also a slash command. 78 skills, counted 2026-10-30. Installs into Claude Desktop or Claude Code as a plugin. Each..."
     },
     "style": {}
    },
    {
     "id": "vibrobeast-near-gw33",
     "type": "server",
     "x": 760,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "gw33",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-near-gw33",
      "what": "The ghostwheel host for the development site dev.ghostwheel.chat, where the vibrobeast account works."
     },
     "style": {}
    },
    {
     "id": "vibrobeast-near-workstation-1",
     "type": "server",
     "x": 760,
     "y": 150,
     "w": 90,
     "h": 90,
     "text": "a workstation",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-near-workstation-1"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-who-corwin",
     "type": "user",
     "x": -260,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "corwin",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-who-corwin",
      "what": "Corwin Brust, corwin@bru.st. Commits are authored as him, unsigned; signing is his act, by hand. The agent's part in a commit is a byline on its last line."
     },
     "style": {}
    },
    {
     "id": "vibrobeast-near-topology",
     "type": "rounded",
     "x": 760,
     "y": 280,
     "w": 150,
     "h": 90,
     "text": "topology\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-near-topology",
      "what": "Draw what you have, from the files that describe it. Reads TOPOLOGY.md from the git repositories you keep, sorts what they say by place, part, path and person, and draws it with TopoForge."
     },
     "style": {}
    },
    {
     "id": "vibrobeast-near-vibrobeast-skills",
     "type": "rounded",
     "x": 760,
     "y": 410,
     "w": 150,
     "h": 90,
     "text": "vibrobeast-skills\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-near-vibrobeast-skills",
      "what": "Written ways of working that Claude reads when a task calls for them; each skill is also a slash command. 78 skills, counted 2026-10-30. Installs into Claude Desktop or Claude Code as a plugin. Each..."
     },
     "style": {}
    },
    {
     "id": "vibrobeast-flow-cap",
     "type": "text",
     "x": 0,
     "y": 600,
     "w": 900,
     "h": 30,
     "text": "How it works: A note, kept.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-flow-cap"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "A note\nsomething worth keeping",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "A new generation\nbeside the old, never over it",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "Plain files\nin your home folder",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 650,
     "w": 170,
     "h": 80,
     "text": "You\nopen, read, or turn a door on",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-how",
     "type": "text",
     "x": 0,
     "y": 760,
     "w": 1100,
     "h": 60,
     "text": "Every room is off at first. The memory is a folder of files; anything that reads it reads the same files you do.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "vibrobeast-l1",
     "a": {
      "id": "vibrobeast-zone",
      "port": null
     },
     "b": {
      "id": "vibrobeast-near-gw33",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l1"
     }
    },
    {
     "id": "vibrobeast-l2",
     "a": {
      "id": "vibrobeast-zone",
      "port": null
     },
     "b": {
      "id": "vibrobeast-near-workstation-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l2"
     }
    },
    {
     "id": "vibrobeast-l3",
     "a": {
      "id": "vibrobeast-who-corwin",
      "port": null
     },
     "b": {
      "id": "vibrobeast-zone",
      "port": null
     },
     "route": "orthogonal",
     "text": "answers for",
     "layer": "abstract",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l3"
     }
    },
    {
     "id": "vibrobeast-l4",
     "a": {
      "id": "vibrobeast-zone",
      "port": null
     },
     "b": {
      "id": "vibrobeast-near-topology",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l4"
     }
    },
    {
     "id": "vibrobeast-l5",
     "a": {
      "id": "vibrobeast-zone",
      "port": null
     },
     "b": {
      "id": "vibrobeast-near-vibrobeast-skills",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l5"
     }
    },
    {
     "id": "vibrobeast-l6",
     "a": {
      "id": "vibrobeast-flow-0",
      "port": null
     },
     "b": {
      "id": "vibrobeast-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l6"
     }
    },
    {
     "id": "vibrobeast-l7",
     "a": {
      "id": "vibrobeast-flow-1",
      "port": null
     },
     "b": {
      "id": "vibrobeast-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l7"
     }
    },
    {
     "id": "vibrobeast-l8",
     "a": {
      "id": "vibrobeast-flow-2",
      "port": null
     },
     "b": {
      "id": "vibrobeast-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-l8"
     }
    }
   ]
  },
  {
   "id": "page-vibrobeast-skills",
   "name": "vibrobeast-skills",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "vibrobeast-skills-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "vibrobeast-skills: Written ways of working that Claude reads when a task calls for them.\nMade by: Corwin and Bryant.\nRepository: https://github.com/inputloop/vibrobeast-skills\nDemo: corwin.cloud/demo/vibrobeast-skills/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-title"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "vibrobeast-skills",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-zone",
      "what": "Written ways of working that Claude reads when a task calls for them.",
      "credit": "Corwin and Bryant",
      "public": "yes",
      "repo": "https://github.com/inputloop/vibrobeast-skills",
      "status": "Today. Public.",
      "licence": "Writing CC-BY-SA-4.0; scripts GPL-3.0-or-later.",
      "demo": "/demo/vibrobeast-skills/",
      "described by": "examples/inputloop/projects/vibrobeast-skills/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "Written ways of working that Claude reads when a task calls for them; each skill is also a slash command. 78 skills, counted 2026-10-30. Installs into Claude Desktop or Claude Code as a plugin. Each skill says when it applies, what it does, how it can fail and how to switch it off; several carry checks that are shown able to fail before anyone trusts them.",
     "layer": "words",
     "parent": "vibrobeast-skills-zone",
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-about"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-near-vibrobeast",
     "type": "rounded",
     "x": 760,
     "y": 20,
     "w": 150,
     "h": 90,
     "text": "vibrobeast\n(its own page)",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-near-vibrobeast",
      "what": "A memory Claude keeps on your own computer in plain files, with the skills, persona sheets and shared rooms built around it. Ships as a plugin carrying three stdio MCP servers, and works as a plugin..."
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-flow-cap",
     "type": "text",
     "x": 0,
     "y": 260,
     "w": 900,
     "h": 30,
     "text": "How it works: A skill, used.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-flow-cap"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "A task\nthe work in front of Claude",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "The skill\na short written method",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Its check\nshown able to fail first",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "The result\nhanded back, with its reasons",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "vibrobeast-skills-how",
     "type": "text",
     "x": 0,
     "y": 420,
     "w": 1100,
     "h": 60,
     "text": "Most of the ideas the shop keeps coming back to live here in written form, so anyone can read exactly what we ask of our agents and hold us to it.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "vibrobeast-skills-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "vibrobeast-skills-l1",
     "a": {
      "id": "vibrobeast-skills-zone",
      "port": null
     },
     "b": {
      "id": "vibrobeast-skills-near-vibrobeast",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-skills-l1"
     }
    },
    {
     "id": "vibrobeast-skills-l2",
     "a": {
      "id": "vibrobeast-skills-flow-0",
      "port": null
     },
     "b": {
      "id": "vibrobeast-skills-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-skills-l2"
     }
    },
    {
     "id": "vibrobeast-skills-l3",
     "a": {
      "id": "vibrobeast-skills-flow-1",
      "port": null
     },
     "b": {
      "id": "vibrobeast-skills-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-skills-l3"
     }
    },
    {
     "id": "vibrobeast-skills-l4",
     "a": {
      "id": "vibrobeast-skills-flow-2",
      "port": null
     },
     "b": {
      "id": "vibrobeast-skills-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "vibrobeast-skills-l4"
     }
    }
   ]
  },
  {
   "id": "page-nexttime-dev",
   "name": "NextTime-Dev",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "nexttime-dev-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "NextTime-Dev: Time banking for Nextcloud: post a request, volunteer, earn hours, public ledger.\nMade by: Brigidt.\nRepository: https://github.com/inputloop/NextTime-Dev\nDemo: corwin.cloud/demo/NextTime-Dev/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-title"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "NextTime-Dev",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-zone",
      "what": "Time banking for Nextcloud: post a request, volunteer, earn hours, public ledger.",
      "credit": "Brigidt",
      "public": "yes",
      "repo": "https://github.com/inputloop/NextTime-Dev",
      "status": "Today. Public. The repository carries a browser demo.",
      "licence": "See the repository.",
      "demo": "/demo/NextTime-Dev/",
      "described by": "examples/inputloop/projects/NextTime-Dev/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "Community time banking for Nextcloud: post a request, volunteer, earn hours. An hour of help is worth an hour whoever gives it. An admin, or a three-vote community process, approves hours, and every movement of hours lands in a public ledger. It is Brigidt's work (GitHub: definitelynotaspren), tested on seven Nextcloud versions as of 2026-10-29. A browser demo is in demo/index.html.",
     "layer": "words",
     "parent": "nexttime-dev-zone",
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-about"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-who-definitelynotaspren",
     "type": "user",
     "x": -260,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "definitelynotaspren",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-who-definitelynotaspren",
      "what": "Credited by this GitHub handle. A lab partner in inputloop since 2026-10-25; NextTime-Dev is her work."
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-flow-cap",
     "type": "text",
     "x": 0,
     "y": 260,
     "w": 900,
     "h": 30,
     "text": "How it works: An hour, from request to ledger.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-flow-cap"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "A request\na member posts a need",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "A volunteer\nsomeone offers help",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Approval\nan admin, or three votes",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "The ledger\npublic, written once",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "nexttime-dev-how",
     "type": "text",
     "x": 0,
     "y": 420,
     "w": 1100,
     "h": 60,
     "text": "It is built as infrastructure against hierarchy: transparency and community oversight are preferred over admin-only power, and changes that would concentrate control are flagged in review.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "nexttime-dev-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "nexttime-dev-l1",
     "a": {
      "id": "nexttime-dev-who-definitelynotaspren",
      "port": null
     },
     "b": {
      "id": "nexttime-dev-zone",
      "port": null
     },
     "route": "orthogonal",
     "text": "answers for",
     "layer": "abstract",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "nexttime-dev-l1"
     }
    },
    {
     "id": "nexttime-dev-l2",
     "a": {
      "id": "nexttime-dev-flow-0",
      "port": null
     },
     "b": {
      "id": "nexttime-dev-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "nexttime-dev-l2"
     }
    },
    {
     "id": "nexttime-dev-l3",
     "a": {
      "id": "nexttime-dev-flow-1",
      "port": null
     },
     "b": {
      "id": "nexttime-dev-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "nexttime-dev-l3"
     }
    },
    {
     "id": "nexttime-dev-l4",
     "a": {
      "id": "nexttime-dev-flow-2",
      "port": null
     },
     "b": {
      "id": "nexttime-dev-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "nexttime-dev-l4"
     }
    }
   ]
  },
  {
   "id": "page-fourswordsfromthepast",
   "name": "fourSwordsFromThePast",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "fourswordsfromthepast-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "fourSwordsFromThePast: A Link to the Past as a native PC game with four-player drop-in co-op.\nMade by: Bryant's repository; credit not yet written in its README.\nRepository: https://github.com/inputloop/fourSwordsFromThePast\nDemo: corwin.cloud/demo/fourSwordsFromThePast/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-title"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "fourSwordsFromThePast",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-zone",
      "what": "A Link to the Past as a native PC game with four-player drop-in co-op.",
      "credit": "Bryant's repository; credit not yet written in its README",
      "public": "yes",
      "repo": "https://github.com/inputloop/fourSwordsFromThePast",
      "status": "Today. Public.",
      "licence": "See the repository; built on snesrev/zelda3 (MIT), vendored at 45a149d8.",
      "demo": "/demo/fourSwordsFromThePast/",
      "described by": "examples/inputloop/projects/fourSwordsFromThePast/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A Link to the Past as a native PC game with up to four players, same PC, LAN or internet, from your own cartridge. You bring your own US ROM; none of the game is in the repository. Who made it, and its credit line, are not yet written in its README; add them when they are.",
     "layer": "words",
     "parent": "fourswordsfromthepast-zone",
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-about"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-flow-cap",
     "type": "text",
     "x": 0,
     "y": 260,
     "w": 900,
     "h": 30,
     "text": "How it works: From your cartridge to four players.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-flow-cap"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Your game file\nyou bring it; US version",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "zelda3\nthe C reimplementation, MIT",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Extract\nart and sound, on your machine",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Up to four\nsame PC, LAN or internet",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "fourswordsfromthepast-how",
     "type": "text",
     "x": 0,
     "y": 420,
     "w": 1100,
     "h": 60,
     "text": "Players: up to 4, counted 2026-10-29. Ways to join: 3 (same PC, LAN, internet).",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "fourswordsfromthepast-l1",
     "a": {
      "id": "fourswordsfromthepast-flow-0",
      "port": null
     },
     "b": {
      "id": "fourswordsfromthepast-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-l1"
     }
    },
    {
     "id": "fourswordsfromthepast-l2",
     "a": {
      "id": "fourswordsfromthepast-flow-1",
      "port": null
     },
     "b": {
      "id": "fourswordsfromthepast-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-l2"
     }
    },
    {
     "id": "fourswordsfromthepast-l3",
     "a": {
      "id": "fourswordsfromthepast-flow-2",
      "port": null
     },
     "b": {
      "id": "fourswordsfromthepast-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "fourswordsfromthepast-l3"
     }
    }
   ]
  },
  {
   "id": "page-quantum3d5",
   "name": "quantum3d5",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "quantum3d5-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "quantum3d5: A 3D atom and molecule simulator for learning chemistry and physics, offline.\nMade by: Bryant.\nRepository: https://github.com/inputloop/quantum3d5\nDemo: corwin.cloud/demo/quantum3d5/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-title"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "quantum3d5",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-zone",
      "what": "A 3D atom and molecule simulator for learning chemistry and physics, offline.",
      "credit": "Bryant",
      "public": "yes",
      "repo": "https://github.com/inputloop/quantum3d5",
      "status": "Today. Public.",
      "licence": "See the repository.",
      "demo": "/demo/quantum3d5/",
      "described by": "examples/inputloop/projects/quantum3d5/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A 3D particle, atom and molecule simulator for learning chemistry and physics in the browser, offline. Bryant's. Needs a browser and nothing else: no account, no server, 0 network connections as of 2026-10-29. A Realism page says which parts are real physics, simplified, or faked.",
     "layer": "words",
     "parent": "quantum3d5-zone",
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-about"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-who-bryant",
     "type": "user",
     "x": -260,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "bryant",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-who-bryant",
      "what": "Bryant Frankford. Made TopoForge and quantum3d5."
     },
     "style": {}
    },
    {
     "id": "quantum3d5-flow-cap",
     "type": "text",
     "x": 0,
     "y": 260,
     "w": 900,
     "h": 30,
     "text": "How it works: Learning by trying.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-flow-cap"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Particles\nprotons, neutrons, electrons",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Fields\nheat, gravity, electric, magnetic",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "What happens\nbonds, decay, light",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "A lesson or a scene\nguided, or saved to share",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "quantum3d5-how",
     "type": "text",
     "x": 0,
     "y": 420,
     "w": 1100,
     "h": 60,
     "text": "Everything runs on your own computer.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "quantum3d5-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "quantum3d5-l1",
     "a": {
      "id": "quantum3d5-who-bryant",
      "port": null
     },
     "b": {
      "id": "quantum3d5-zone",
      "port": null
     },
     "route": "orthogonal",
     "text": "answers for",
     "layer": "abstract",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "quantum3d5-l1"
     }
    },
    {
     "id": "quantum3d5-l2",
     "a": {
      "id": "quantum3d5-flow-0",
      "port": null
     },
     "b": {
      "id": "quantum3d5-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "quantum3d5-l2"
     }
    },
    {
     "id": "quantum3d5-l3",
     "a": {
      "id": "quantum3d5-flow-1",
      "port": null
     },
     "b": {
      "id": "quantum3d5-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "quantum3d5-l3"
     }
    },
    {
     "id": "quantum3d5-l4",
     "a": {
      "id": "quantum3d5-flow-2",
      "port": null
     },
     "b": {
      "id": "quantum3d5-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "quantum3d5-l4"
     }
    }
   ]
  },
  {
   "id": "page-suit-crm-6-5-php-sec-backport",
   "name": "suit-crm-6-5-php-sec-backport",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "suit-crm-6-5-php-sec-backport-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "suit-crm-6-5-php-sec-backport: Builds patched end-of-life PHP for SuiteCRM and proves SuiteCRM installs on it.\nMade by: Corwin.\nRepository: https://github.com/inputloop/suit-crm-6-5-php-sec-backport\nDemo: corwin.cloud/demo/suit-crm-6-5-php-sec-backport/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-title"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "suit-crm-6-5-php-sec-backport",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-zone",
      "what": "Builds patched end-of-life PHP for SuiteCRM and proves SuiteCRM installs on it.",
      "credit": "Corwin",
      "public": "yes",
      "repo": "https://github.com/inputloop/suit-crm-6-5-php-sec-backport",
      "status": "Today. Public.",
      "licence": "See the repository.",
      "demo": "/demo/suit-crm-6-5-php-sec-backport/",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "Builds patched end-of-life PHP for SuiteCRM and proves SuiteCRM installs on it.",
     "layer": "words",
     "parent": "suit-crm-6-5-php-sec-backport-zone",
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-about"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-flow-cap",
     "type": "text",
     "x": 0,
     "y": 260,
     "w": 900,
     "h": 30,
     "text": "How it works: Patch, build, install, log in.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-flow-cap"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Patch\nthe PHP project's commits, pinned",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Build\na branch that has no releases",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Install\nSuiteCRM on that build",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Log in\neach step waits for the one before",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-how",
     "type": "text",
     "x": 0,
     "y": 420,
     "w": 1100,
     "h": 60,
     "text": "It is a build-and-prove apparatus, not a fork: the patches are the PHP project's, unchanged, and updating PHP is still the only complete fix. The repository says so on its first page.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "suit-crm-6-5-php-sec-backport-l1",
     "a": {
      "id": "suit-crm-6-5-php-sec-backport-flow-0",
      "port": null
     },
     "b": {
      "id": "suit-crm-6-5-php-sec-backport-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-l1"
     }
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-l2",
     "a": {
      "id": "suit-crm-6-5-php-sec-backport-flow-1",
      "port": null
     },
     "b": {
      "id": "suit-crm-6-5-php-sec-backport-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-l2"
     }
    },
    {
     "id": "suit-crm-6-5-php-sec-backport-l3",
     "a": {
      "id": "suit-crm-6-5-php-sec-backport-flow-2",
      "port": null
     },
     "b": {
      "id": "suit-crm-6-5-php-sec-backport-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "suit-crm-6-5-php-sec-backport-l3"
     }
    }
   ]
  },
  {
   "id": "page-card-games-el",
   "name": "card-games.el",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "card-games-el-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "card-games.el: About thirty card games for GNU Emacs. Corwin's own, outside the inputloop org.\nMade by: Corwin.\nRepository: https://github.com/mplscorwin/card-games.el\nDemo: corwin.cloud/demo/card-games.el/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-title"
     },
     "style": {}
    },
    {
     "id": "card-games-el-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "card-games.el",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-zone",
      "what": "About thirty card games for GNU Emacs. Corwin's own, outside the inputloop org.",
      "credit": "Corwin",
      "public": "yes",
      "repo": "https://github.com/mplscorwin/card-games.el",
      "status": "Today. Public. On MELPA.",
      "licence": "Code GPL-3.0-or-later; manual CC-BY-4.0.",
      "demo": "/demo/card-games.el/",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "card-games-el-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "About thirty card games for GNU Emacs. Corwin's own, outside the inputloop org.",
     "layer": "words",
     "parent": "card-games-el-zone",
     "props": {},
     "data": {
      "tf_ref": "card-games-el-about"
     },
     "style": {}
    },
    {
     "id": "card-games-el-flow-cap",
     "type": "text",
     "x": 0,
     "y": 260,
     "w": 900,
     "h": 30,
     "text": "How it works: From MELPA to a hand of cards.",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-flow-cap"
     },
     "style": {}
    },
    {
     "id": "card-games-el-flow-0",
     "type": "ellipse",
     "x": 0,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "MELPA\npackage-install card-games",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-flow-0",
      "step": "1"
     },
     "style": {}
    },
    {
     "id": "card-games-el-flow-1",
     "type": "rounded",
     "x": 230,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "Emacs\nyour own",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-flow-1",
      "step": "2"
     },
     "style": {}
    },
    {
     "id": "card-games-el-flow-2",
     "type": "rounded",
     "x": 460,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "A game\npick from the list",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-flow-2",
      "step": "3"
     },
     "style": {}
    },
    {
     "id": "card-games-el-flow-3",
     "type": "ellipse",
     "x": 690,
     "y": 310,
     "w": 170,
     "h": 80,
     "text": "A hand\nin a buffer",
     "layer": "flow",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-flow-3",
      "step": "4"
     },
     "style": {}
    },
    {
     "id": "card-games-el-how",
     "type": "text",
     "x": 0,
     "y": 420,
     "w": 1100,
     "h": 60,
     "text": "Written in Emacs Lisp; each game plays in an ordinary buffer.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "card-games-el-how"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "card-games-el-l1",
     "a": {
      "id": "card-games-el-flow-0",
      "port": null
     },
     "b": {
      "id": "card-games-el-flow-1",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "card-games-el-l1"
     }
    },
    {
     "id": "card-games-el-l2",
     "a": {
      "id": "card-games-el-flow-1",
      "port": null
     },
     "b": {
      "id": "card-games-el-flow-2",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "card-games-el-l2"
     }
    },
    {
     "id": "card-games-el-l3",
     "a": {
      "id": "card-games-el-flow-2",
      "port": null
     },
     "b": {
      "id": "card-games-el-flow-3",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "flow",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "card-games-el-l3"
     }
    }
   ]
  },
  {
   "id": "page-gitcha",
   "name": "gitcha",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "gitcha-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "gitcha: A free, self-hosted forge for people and their agents: packages, git, site hosting and CI, with two-factor sign-in on every account. Its README says which parts are tested only against stand-ins.\nMade by: Bryant and Corwin.\nRepository: https://github.com/inputloop/gitcha\nDemo: corwin.cloud/demo/gitcha/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "gitcha-title"
     },
     "style": {}
    },
    {
     "id": "gitcha-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "gitcha",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "gitcha-zone",
      "what": "A free, self-hosted forge for people and their agents: packages, git, site hosting and CI, with two-factor sign-in on every account. Its README says which parts are tested only against stand-ins.",
      "credit": "Bryant and Corwin",
      "public": "yes",
      "repo": "https://github.com/inputloop/gitcha",
      "demo": "/demo/gitcha/",
      "described by": "examples/inputloop/projects/gitcha/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "gitcha-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A free, self-hosted forge for people and their agents: packages, git hosting, site hosting, CI and functions, with two-factor sign-in on every account and a Claude extension that speaks it. It is the front door of inputloop.com; the software is called Gitcha, which is why its commands and settings say gitcha. Made by Bryant and Corwin. The repository is public (since 2026-11-01); its README says which parts have...",
     "layer": "words",
     "parent": "gitcha-zone",
     "props": {},
     "data": {
      "tf_ref": "gitcha-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-c4",
   "name": "c4",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "c4-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "c4: The registry and signing layer the shop's other tools are filed under.\nMade by: Corwin.\nRepository: not public yet.",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "c4-title"
     },
     "style": {}
    },
    {
     "id": "c4-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "c4",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "c4-zone",
      "what": "The registry and signing layer the shop's other tools are filed under.",
      "credit": "Corwin",
      "public": "not public yet",
      "described by": "examples/inputloop/projects/c4/TOPOLOGY.md"
     },
     "style": {
      "dash": "6 4"
     }
    },
    {
     "id": "c4-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A project of the shop. It is private: this page says only that it exists.\n[1 private part kept back: this site never shows private parts.]",
     "layer": "words",
     "parent": "c4-zone",
     "props": {},
     "data": {
      "tf_ref": "c4-about"
     },
     "style": {}
    },
    {
     "id": "c4-near-gw33",
     "type": "server",
     "x": 760,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "gw33",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "c4-near-gw33",
      "what": "The ghostwheel host for the development site dev.ghostwheel.chat, where the vibrobeast account works."
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "c4-l1",
     "a": {
      "id": "c4-zone",
      "port": null
     },
     "b": {
      "id": "c4-near-gw33",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "c4-l1"
     }
    }
   ]
  },
  {
   "id": "page-github",
   "name": ".github",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "github-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": ".github: The organization's front page, and the conduct, support, security and accessibility files every repository inherits.\nMade by: Corwin and Bryant.\nRepository: https://github.com/inputloop/.github",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "github-title"
     },
     "style": {}
    },
    {
     "id": "github-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": ".github",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "github-zone",
      "what": "The organization's front page, and the conduct, support, security and accessibility files every repository inherits.",
      "credit": "Corwin and Bryant",
      "public": "yes",
      "repo": "https://github.com/inputloop/.github",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "github-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "The organization's front page, and the conduct, support, security and accessibility files every repository inherits.",
     "layer": "words",
     "parent": "github-zone",
     "props": {},
     "data": {
      "tf_ref": "github-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-corwin-cloud",
   "name": "corwin.cloud",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "corwin-cloud-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "corwin.cloud: This site: the shell around topology, the tabs, the directory, the packages.\nMade by: Corwin and Bryant.\nRepository: https://github.com/inputloop/corwin.cloud",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "corwin-cloud-title"
     },
     "style": {}
    },
    {
     "id": "corwin-cloud-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "corwin.cloud",
     "layer": "abstract",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "corwin-cloud-zone",
      "what": "This site: the shell around topology, the tabs, the directory, the packages.",
      "credit": "Corwin and Bryant",
      "public": "yes",
      "repo": "https://github.com/inputloop/corwin.cloud",
      "described by": "examples/inputloop/projects/corwin.cloud/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "corwin-cloud-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "A website that will use topology as its front door: the first thing a visitor sees is the drawing, and the drawing links to the things it shows. Not built yet. Which host serves it is not decided.",
     "layer": "words",
     "parent": "corwin-cloud-zone",
     "props": {},
     "data": {
      "tf_ref": "corwin-cloud-about"
     },
     "style": {}
    },
    {
     "id": "corwin-cloud-near-workstation-0",
     "type": "site",
     "x": 760,
     "y": 20,
     "w": 90,
     "h": 90,
     "text": "a workstation",
     "layer": "physical",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "corwin-cloud-near-workstation-0"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "corwin-cloud-l1",
     "a": {
      "id": "corwin-cloud-zone",
      "port": null
     },
     "b": {
      "id": "corwin-cloud-near-workstation-0",
      "port": null
     },
     "route": "orthogonal",
     "text": "connects",
     "layer": "operational",
     "arrows": "end",
     "props": {},
     "style": {},
     "data": {
      "tf_ref": "corwin-cloud-l1"
     }
    }
   ]
  },
  {
   "id": "page-about-corwin",
   "name": "about-corwin",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "about-corwin-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "about-corwin: The source of the personal page at aboutcorwin.com.\nMade by: Corwin.\nRepository: https://github.com/inputloop/about-corwin",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "about-corwin-title"
     },
     "style": {}
    },
    {
     "id": "about-corwin-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "about-corwin",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "about-corwin-zone",
      "what": "The source of the personal page at aboutcorwin.com.",
      "credit": "Corwin",
      "public": "yes",
      "repo": "https://github.com/inputloop/about-corwin",
      "described by": "examples/inputloop/projects/about-corwin/TOPOLOGY.md"
     },
     "style": {}
    },
    {
     "id": "about-corwin-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "The source of aboutcorwin.com, a personal page. The repository is public (since 2026-11-01).",
     "layer": "words",
     "parent": "about-corwin-zone",
     "props": {},
     "data": {
      "tf_ref": "about-corwin-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-ta",
   "name": "ta",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "ta-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "ta: Technology Architecture: a portable operating-model kit, one file you hand an AI so it grounds itself in a host's own public identity and prefers that host's own canon.\nMade by: Corwin, with help from Bryant Frankford and B.K. Graphics (bkgraphics.com).\nRepository: https://github.com/inputloop/ta",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ta-title"
     },
     "style": {}
    },
    {
     "id": "ta-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "ta",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ta-zone",
      "what": "Technology Architecture: a portable operating-model kit, one file you hand an AI so it grounds itself in a host's own public identity and prefers that host's own canon.",
      "credit": "Corwin, with help from Bryant Frankford and B.K. Graphics (bkgraphics.com)",
      "public": "yes",
      "repo": "https://github.com/inputloop/ta",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "ta-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "Technology Architecture: a portable operating-model kit, one file you hand an AI so it grounds itself in a host's own public identity and prefers that host's own canon.",
     "layer": "words",
     "parent": "ta-zone",
     "props": {},
     "data": {
      "tf_ref": "ta-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-three-phase-transcription",
   "name": "three-phase-transcription",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "three-phase-transcription-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "three-phase-transcription: Recorded talk in three layers that each cite the one below: quotes by time, subjects drawn from the quotes, and a result in which every sentence can be followed back to the second it was said.\nMade by: Corwin; where a persona wrote something it wrote for Corwin, who holds the rights.\nRepository: https://github.com/inputloop/three-phase-transcription",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "three-phase-transcription-title"
     },
     "style": {}
    },
    {
     "id": "three-phase-transcription-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "three-phase-transcription",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "three-phase-transcription-zone",
      "what": "Recorded talk in three layers that each cite the one below: quotes by time, subjects drawn from the quotes, and a result in which every sentence can be followed back to the second it was said.",
      "credit": "Corwin; where a persona wrote something it wrote for Corwin, who holds the rights",
      "public": "yes",
      "repo": "https://github.com/inputloop/three-phase-transcription",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "three-phase-transcription-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "Recorded talk in three layers that each cite the one below: quotes by time, subjects drawn from the quotes, and a result in which every sentence can be followed back to the second it was said.",
     "layer": "words",
     "parent": "three-phase-transcription-zone",
     "props": {},
     "data": {
      "tf_ref": "three-phase-transcription-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-journal",
   "name": "journal",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "journal-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "journal: The open shelf: dated, PGP-clearsigned accounts of the shop's work, beginning with two companion accounts of the 2026 GNU Savannah forensic review.\nMade by: Corwin and Claude (Anthropic), as the repository's own attribution line gives it.\nRepository: https://github.com/inputloop/journal",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "journal-title"
     },
     "style": {}
    },
    {
     "id": "journal-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "journal",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "journal-zone",
      "what": "The open shelf: dated, PGP-clearsigned accounts of the shop's work, beginning with two companion accounts of the 2026 GNU Savannah forensic review.",
      "credit": "Corwin and Claude (Anthropic), as the repository's own attribution line gives it",
      "public": "yes",
      "repo": "https://github.com/inputloop/journal",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "journal-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "The open shelf: dated, PGP-clearsigned accounts of the shop's work, beginning with two companion accounts of the 2026 GNU Savannah forensic review.",
     "layer": "words",
     "parent": "journal-zone",
     "props": {},
     "data": {
      "tf_ref": "journal-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-pubs",
   "name": "pubs",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "pubs-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "pubs: The reading room: a copy of the shop's publication shelf, with papers, posts, persona sheets, the canon and four game books, refreshed from the working folder.\nMade by: Corwin.\nRepository: https://github.com/inputloop/pubs",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "pubs-title"
     },
     "style": {}
    },
    {
     "id": "pubs-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "pubs",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "pubs-zone",
      "what": "The reading room: a copy of the shop's publication shelf, with papers, posts, persona sheets, the canon and four game books, refreshed from the working folder.",
      "credit": "Corwin",
      "public": "yes",
      "repo": "https://github.com/inputloop/pubs",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "pubs-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "The reading room: a copy of the shop's publication shelf, with papers, posts, persona sheets, the canon and four game books, refreshed from the working folder.",
     "layer": "words",
     "parent": "pubs-zone",
     "props": {},
     "data": {
      "tf_ref": "pubs-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-egad",
   "name": "egad",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "egad-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "egad: The house design system: one set of colours, type and spacing traced to GNU Emacs's Modus themes, carried to the web, Org, Emacs and the apps you theme.\nMade by: Corwin.\nRepository: https://github.com/inputloop/egad\nDemo: corwin.cloud/demo/egad/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "egad-title"
     },
     "style": {}
    },
    {
     "id": "egad-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "egad",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "egad-zone",
      "what": "The house design system: one set of colours, type and spacing traced to GNU Emacs's Modus themes, carried to the web, Org, Emacs and the apps you theme.",
      "credit": "Corwin",
      "public": "yes",
      "repo": "https://github.com/inputloop/egad",
      "demo": "/demo/egad/",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "egad-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "The house design system: one set of colours, type and spacing traced to GNU Emacs's Modus themes, carried to the web, Org, Emacs and the apps you theme.",
     "layer": "words",
     "parent": "egad-zone",
     "props": {},
     "data": {
      "tf_ref": "egad-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "page-hello-taxgnomes",
   "name": "hello-taxgnomes",
   "layers": [
    {
     "id": "words",
     "name": "Words: titles and notes",
     "visible": true,
     "locked": false
    },
    {
     "id": "abstract",
     "name": "Abstract: people and ideas",
     "visible": true,
     "locked": false
    },
    {
     "id": "operational",
     "name": "Operational: projects, parts and paths",
     "visible": true,
     "locked": false
    },
    {
     "id": "physical",
     "name": "Physical: hosts and screens",
     "visible": true,
     "locked": false
    },
    {
     "id": "flow",
     "name": "Flow: how it works, step by step",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "operational",
   "nodes": [
    {
     "id": "hello-taxgnomes-title",
     "type": "text",
     "x": 0,
     "y": -170,
     "w": 1200,
     "h": 120,
     "text": "hello-taxgnomes: The Gnemonic Word Mines: pick a word, move a four-way slider (simpler, denser, moodier, drier), and every sentence says who wrote it.\nMade by: Corwin Brust and Contributors.\nRepository: https://github.com/inputloop/hello-taxgnomes\nDemo: corwin.cloud/demo/hello-taxgnomes/",
     "layer": "words",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "hello-taxgnomes-title"
     },
     "style": {}
    },
    {
     "id": "hello-taxgnomes-zone",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 640,
     "h": 180,
     "text": "hello-taxgnomes",
     "layer": "operational",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "hello-taxgnomes-zone",
      "what": "The Gnemonic Word Mines: pick a word, move a four-way slider (simpler, denser, moodier, drier), and every sentence says who wrote it.",
      "credit": "Corwin Brust and Contributors",
      "public": "yes",
      "repo": "https://github.com/inputloop/hello-taxgnomes",
      "demo": "/demo/hello-taxgnomes/",
      "described by": "catalog.py only: this project has no TOPOLOGY.md yet"
     },
     "style": {}
    },
    {
     "id": "hello-taxgnomes-about",
     "type": "note",
     "x": 20,
     "y": 40,
     "w": 600,
     "h": 120,
     "text": "The Gnemonic Word Mines: pick a word, move a four-way slider (simpler, denser, moodier, drier), and every sentence says who wrote it.",
     "layer": "words",
     "parent": "hello-taxgnomes-zone",
     "props": {},
     "data": {
      "tf_ref": "hello-taxgnomes-about"
     },
     "style": {}
    }
   ],
   "links": []
  },
  {
   "id": "pg-system",
   "name": "How Gitcha works (Bryant) 1 The whole system",
   "layers": [
    {
     "id": "L1",
     "name": "Drawing",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "L1",
   "nodes": [
    {
     "id": "text-title",
     "type": "text",
     "x": 0,
     "y": -130,
     "w": 1500,
     "h": 90,
     "text": "How Gitcha works: the whole system\nEvery program, the machine it runs on, and who talks to whom. Arrows point from the side that starts the conversation.\nEach shape's custom fields say what it does (what), where its code is (code) and how far it has been tested (status).",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "text-title"
     },
     "style": {}
    },
    {
     "id": "zone-people",
     "type": "zone",
     "x": 0,
     "y": 0,
     "w": 300,
     "h": 560,
     "text": "People, agents and their tools",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "zone-people"
     },
     "style": {}
    },
    {
     "id": "users-browser",
     "type": "users",
     "x": 40,
     "y": 50,
     "w": 80,
     "h": 92,
     "text": "People in a browser",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "users-browser",
      "what": "Sign up (password + authenticator app), browse, post to the crawl, manage teams, sites, keys and billing.",
      "code": "server/src/web.js",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "laptop-cli",
     "type": "laptop",
     "x": 180,
     "y": 50,
     "w": 80,
     "h": 92,
     "text": "gitcha CLI",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "laptop-cli",
      "what": "Signs in once (password + code), keeps a device token (mode 600), publishes and installs packages.",
      "code": "extension/bin/gitcha.js",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "user-claude",
     "type": "user",
     "x": 40,
     "y": 180,
     "w": 80,
     "h": 92,
     "text": "Claude + Gitcha extension",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "user-claude",
      "what": "The MCP server Claude calls. Reuses the CLI's device token, so Claude never sees a password; asks the person for 2FA codes.",
      "code": "extension/bin/gitcha-mcp.js",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "user-agent",
     "type": "user",
     "x": 180,
     "y": 180,
     "w": 80,
     "h": 92,
     "text": "Agents (for HUMAN, MODEL)",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "user-agent",
      "what": "Registered agents push with git (their handle + the owner's console key, or their own SSH key) and do Second Reader reviews.",
      "code": "server/src/social/agents.js",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "laptop-git",
     "type": "laptop",
     "x": 40,
     "y": 310,
     "w": 80,
     "h": 92,
     "text": "git (HTTPS or SSH)",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "laptop-git",
      "what": "Plain git: clone and push projects. HTTPS uses a console key as the password; SSH uses a registered key.",
      "code": "server/src/git/http.js",
      "status": "HTTPS: built and tested. SSH: built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    },
    {
     "id": "desktop-c4ok",
     "type": "desktop",
     "x": 180,
     "y": 310,
     "w": 80,
     "h": 92,
     "text": "C4OK kiosk (staff)",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "desktop-c4ok",
      "what": "The operations door: tickets and changes, with a console key read from a mode-600 file. Asks for a code whenever Gitcha does.",
      "code": "c4ok-PRIVATE w/lib/gitcha.js",
      "status": "built; not yet run inside a full C4OK runtime"
     },
     "style": {}
    },
    {
     "id": "cloud-sandbox",
     "type": "cloud",
     "x": 100,
     "y": 440,
     "w": 100,
     "h": 80,
     "text": "Claude in a cloud sandbox (omni-hub)",
     "layer": "L1",
     "parent": "zone-people",
     "props": {},
     "data": {
      "tf_ref": "cloud-sandbox",
      "what": "Omni's hub: one stdio MCP server in a cloud session that reaches the tools a person runs on their own hosts.",
      "code": "omni/lib/hub.js",
      "status": "built and tested; not yet run against a real cloud session"
     },
     "style": {}
    },
    {
     "id": "site-runners",
     "type": "site",
     "x": 0,
     "y": 620,
     "w": 300,
     "h": 180,
     "text": "Runner VM(s), apart from the server",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "site-runners"
     },
     "style": {}
    },
    {
     "id": "server-runner",
     "type": "server",
     "x": 40,
     "y": 680,
     "w": 80,
     "h": 92,
     "text": "gitcha-runner",
     "layer": "L1",
     "parent": "site-runners",
     "props": {},
     "data": {
      "tf_ref": "server-runner",
      "what": "Registers once (a token that works once), long-polls for jobs, runs each one, streams the log, reports the result.",
      "code": "runner/lib/runner.js#loop",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "vm-bcontain",
     "type": "vm",
     "x": 180,
     "y": 680,
     "w": 80,
     "h": 92,
     "text": "bContain VM, one per job",
     "layer": "L1",
     "parent": "site-runners",
     "props": {
      "notes": "CPU and memory caps; nothing survives the job. Known gaps: outbound network, root inside, no disk quota."
     },
     "data": {
      "tf_ref": "vm-bcontain",
      "what": "Runs one CI build or function call. Files arrive over QEMU's gateway (10.0.2.2) behind a random token for that job.",
      "code": "runner/lib/backends/bcontain.js",
      "status": "built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    },
    {
     "id": "internet-net",
     "type": "internet",
     "x": 400,
     "y": 390,
     "w": 100,
     "h": 80,
     "text": "Internet",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "internet-net",
      "what": "Everything outside the server VM. Only the proxy (and sshd, for git) face it."
     },
     "style": {}
    },
    {
     "id": "site-server",
     "type": "site",
     "x": 580,
     "y": 0,
     "w": 620,
     "h": 800,
     "text": "Gitcha server VM (alphaOS)",
     "layer": "L1",
     "parent": null,
     "props": {
      "notes": "Only the reverse proxy (and sshd, for git) listen on the network; everything else listens on loopback."
     },
     "data": {
      "tf_ref": "site-server"
     },
     "style": {}
    },
    {
     "id": "lb-proxy",
     "type": "lb",
     "x": 620,
     "y": 390,
     "w": 80,
     "h": 92,
     "text": "Reverse proxy (TLS)",
     "layer": "L1",
     "parent": "site-server",
     "props": {
      "hostname": "gitcha.example.org",
      "notes": "Caddy, nginx, Apache or HAProxy. Owns ports 443 and 80 and the certificates; asks Gitcha before issuing one."
     },
     "data": {
      "tf_ref": "lb-proxy",
      "what": "Does https, sets X-Forwarded-For and X-Forwarded-Proto, and asks /api/v1/hosting/tls-allowed before any certificate.",
      "code": "deploy/Caddyfile.example",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "server-sshd",
     "type": "server",
     "x": 840,
     "y": 110,
     "w": 80,
     "h": 92,
     "text": "sshd + gitcha-ssh-keys / gitcha-git-shell",
     "layer": "L1",
     "parent": "site-server",
     "props": {
      "notes": "A known key may only run gitcha-git-shell, which runs git-upload-pack or git-receive-pack and nothing else."
     },
     "data": {
      "tf_ref": "server-sshd",
      "what": "Git over SSH. The helpers ask gitcha-server on loopback, with the secret in internal.key.",
      "code": "server/bin/gitcha-git-shell.js",
      "status": "built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    },
    {
     "id": "server-gitcha",
     "type": "server",
     "x": 840,
     "y": 390,
     "w": 80,
     "h": 92,
     "text": "gitcha-server",
     "layer": "L1",
     "parent": "site-server",
     "props": {
      "hostname": "127.0.0.1:4873",
      "notes": "Plain HTTP on loopback. Never runs user code."
     },
     "data": {
      "tf_ref": "server-gitcha",
      "what": "Accounts and 2FA, packages, teams, git, sites, CI and function queue, billing, the social side, GraphQL, tickets and changes.",
      "code": "server/src/app.js#createApp",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "storage-data",
     "type": "storage",
     "x": 1060,
     "y": 250,
     "w": 80,
     "h": 92,
     "text": "Data folder /var/lib/gitcha",
     "layer": "L1",
     "parent": "site-server",
     "props": {
      "notes": "db.json (file store), secret.key, blobs/, git/, sites/, logs/, job-sources/, internal.key, db.lock"
     },
     "data": {
      "tf_ref": "storage-data",
      "what": "Everything the server keeps on disk. Back up secret.key apart from the rest.",
      "code": "server/src/store.js#Store",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "server-omni",
     "type": "server",
     "x": 620,
     "y": 640,
     "w": 80,
     "h": 92,
     "text": "omni-gateway",
     "layer": "L1",
     "parent": "site-server",
     "props": {
      "hostname": "127.0.0.1:8769",
      "notes": "Behind the TLS proxy; one URL and one bearer token per member."
     },
     "data": {
      "tf_ref": "server-omni",
      "what": "Puts stdio MCP servers on this host (gitcha-mcp, mentcached, persona, tavern) behind HTTP for a cloud session.",
      "code": "omni/lib/gateway.js",
      "status": "built and tested; not yet run against a real vibrobeast server"
     },
     "style": {}
    },
    {
     "id": "nms-medic",
     "type": "nms",
     "x": 1060,
     "y": 640,
     "w": 80,
     "h": 92,
     "text": "gitcha-medic (timer)",
     "layer": "L1",
     "parent": "site-server",
     "props": {
      "notes": "Only reads to diagnose; runs a remedy only if its class is safe, or with --yes for a confirm one."
     },
     "data": {
      "tf_ref": "nms-medic",
      "what": "Looks at Gitcha, Cassandra and the proxy; says what is wrong and why; mends what is safe; tells syslog and the configured rooms.",
      "code": "medic/bin/gitcha-medic.js",
      "status": "built; tested against stand-ins, not yet run on a real host"
     },
     "style": {}
    },
    {
     "id": "zone-cassandra",
     "type": "zone",
     "x": 1280,
     "y": 330,
     "w": 220,
     "h": 220,
     "text": "Cassandra cluster (optional)",
     "layer": "L1",
     "parent": null,
     "props": {
      "notes": "GITCHA_STORE=cassandra. The default build of the Debian package; --without-cassandra ships GITCHA_STORE=file."
     },
     "data": {
      "tf_ref": "zone-cassandra"
     },
     "style": {}
    },
    {
     "id": "database-cassandra",
     "type": "database",
     "x": 1350,
     "y": 390,
     "w": 80,
     "h": 92,
     "text": "Apache Cassandra",
     "layer": "L1",
     "parent": "zone-cassandra",
     "props": {},
     "data": {
      "tf_ref": "database-cassandra",
      "what": "The database, one row per entry; each save is one batch applied whole or not at all. A lease row stops two servers writing.",
      "code": "server/src/store-cassandra.js",
      "status": "built, tested, and run for real on Cassandra 5.0.9"
     },
     "style": {}
    },
    {
     "id": "zone-outside",
     "type": "zone",
     "x": 580,
     "y": 880,
     "w": 920,
     "h": 170,
     "text": "Services Gitcha calls out to (https)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "zone-outside"
     },
     "style": {}
    },
    {
     "id": "cloud-stripe",
     "type": "cloud",
     "x": 610,
     "y": 930,
     "w": 100,
     "h": 80,
     "text": "Stripe",
     "layer": "L1",
     "parent": "zone-outside",
     "props": {},
     "data": {
      "tf_ref": "cloud-stripe",
      "what": "Checkout, portal, webhooks checked by HMAC on the exact bytes.",
      "code": "server/src/billing/stripe.js",
      "status": "built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    },
    {
     "id": "cloud-paypal",
     "type": "cloud",
     "x": 760,
     "y": 930,
     "w": 100,
     "h": 80,
     "text": "PayPal",
     "layer": "L1",
     "parent": "zone-outside",
     "props": {},
     "data": {
      "tf_ref": "cloud-paypal",
      "what": "Subscriptions; webhooks checked by asking PayPal.",
      "code": "server/src/billing/paypal.js",
      "status": "built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    },
    {
     "id": "cloud-nexttime",
     "type": "cloud",
     "x": 910,
     "y": 930,
     "w": 100,
     "h": 80,
     "text": "NextTime-Dev",
     "layer": "L1",
     "parent": "zone-outside",
     "props": {},
     "data": {
      "tf_ref": "cloud-nexttime",
      "what": "Public projects' tasks become timebank requests: created, cancelled, completed.",
      "code": "server/src/social/nexttime.js",
      "status": "built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    },
    {
     "id": "cloud-ring",
     "type": "cloud",
     "x": 1060,
     "y": 930,
     "w": 100,
     "h": 80,
     "text": "Other Gitchas (the ring)",
     "layer": "L1",
     "parent": "zone-outside",
     "props": {},
     "data": {
      "tf_ref": "cloud-ring",
      "what": "Each publishes /.well-known/gitcha-ring.json; only public addresses are fetched.",
      "code": "server/src/hosting/ring.js",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "cloud-dns",
     "type": "cloud",
     "x": 1210,
     "y": 930,
     "w": 100,
     "h": 80,
     "text": "DNS",
     "layer": "L1",
     "parent": "zone-outside",
     "props": {},
     "data": {
      "tf_ref": "cloud-dns",
      "what": "TXT records at _gitcha-challenge.NAME prove a custom domain.",
      "code": "server/src/hosting/domains.js",
      "status": "built and tested"
     },
     "style": {}
    },
    {
     "id": "cloud-claudeapi",
     "type": "cloud",
     "x": 1360,
     "y": 930,
     "w": 100,
     "h": 80,
     "text": "Claude API (medic's agent)",
     "layer": "L1",
     "parent": "zone-outside",
     "props": {},
     "data": {
      "tf_ref": "cloud-claudeapi",
      "what": "Optional: the medic shows a model the redacted findings; it may only ask for safe remedies.",
      "code": "medic/lib/agent.js",
      "status": "built; tested only against stand-ins, not yet run for real"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "link-people-net",
     "a": {
      "id": "zone-people",
      "port": null
     },
     "b": {
      "id": "internet-net",
      "port": null
     },
     "route": "orthogonal",
     "text": "https (git: https or ssh)",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan"
     },
     "style": {},
     "data": {
      "tf_ref": "link-people-net"
     }
    },
    {
     "id": "link-runner-net",
     "a": {
      "id": "server-runner",
      "port": null
     },
     "b": {
      "id": "internet-net",
      "port": null
     },
     "route": "orthogonal",
     "text": "https: long-poll for jobs",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan",
      "notes": "Runner token for the runner; a job token for each job."
     },
     "style": {},
     "data": {
      "tf_ref": "link-runner-net"
     }
    },
    {
     "id": "link-net-proxy",
     "a": {
      "id": "internet-net",
      "port": null
     },
     "b": {
      "id": "lb-proxy",
      "port": null
     },
     "route": "orthogonal",
     "text": "https 443",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan"
     },
     "style": {},
     "data": {
      "tf_ref": "link-net-proxy"
     }
    },
    {
     "id": "link-net-sshd",
     "a": {
      "id": "internet-net",
      "port": null
     },
     "b": {
      "id": "server-sshd",
      "port": null
     },
     "route": "orthogonal",
     "text": "ssh 22 (git only)",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan"
     },
     "style": {},
     "data": {
      "tf_ref": "link-net-sshd"
     }
    },
    {
     "id": "link-proxy-gitcha",
     "a": {
      "id": "lb-proxy",
      "port": null
     },
     "b": {
      "id": "server-gitcha",
      "port": null
     },
     "route": "orthogonal",
     "text": "http to 127.0.0.1:4873",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical",
      "notes": "The proxy passes Host, X-Forwarded-For and X-Forwarded-Proto."
     },
     "style": {},
     "data": {
      "tf_ref": "link-proxy-gitcha"
     }
    },
    {
     "id": "link-proxy-omni",
     "a": {
      "id": "lb-proxy",
      "port": null
     },
     "b": {
      "id": "server-omni",
      "port": null
     },
     "route": "orthogonal",
     "text": "/mcp/<member>",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-proxy-omni"
     }
    },
    {
     "id": "link-sshd-gitcha",
     "a": {
      "id": "server-sshd",
      "port": null
     },
     "b": {
      "id": "server-gitcha",
      "port": null
     },
     "route": "orthogonal",
     "text": "/internal/git/* (loopback + internal.key)",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-sshd-gitcha"
     }
    },
    {
     "id": "link-gitcha-data",
     "a": {
      "id": "server-gitcha",
      "port": null
     },
     "b": {
      "id": "storage-data",
      "port": null
     },
     "route": "orthogonal",
     "text": "file store, blobs, git, sites",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-gitcha-data"
     }
    },
    {
     "id": "link-gitcha-cassandra",
     "a": {
      "id": "server-gitcha",
      "port": null
     },
     "b": {
      "id": "database-cassandra",
      "port": null
     },
     "route": "orthogonal",
     "text": "GITCHA_STORE=cassandra",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-gitcha-cassandra"
     }
    },
    {
     "id": "link-runner-bcontain",
     "a": {
      "id": "server-runner",
      "port": null
     },
     "b": {
      "id": "vm-bcontain",
      "port": null
     },
     "route": "orthogonal",
     "text": "one VM per job",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-runner-bcontain"
     }
    },
    {
     "id": "link-omni-gitcha",
     "a": {
      "id": "server-omni",
      "port": null
     },
     "b": {
      "id": "server-gitcha",
      "port": null
     },
     "route": "orthogonal",
     "text": "gitcha-mcp member",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-omni-gitcha"
     }
    },
    {
     "id": "link-medic-gitcha",
     "a": {
      "id": "nms-medic",
      "port": null
     },
     "b": {
      "id": "server-gitcha",
      "port": null
     },
     "route": "orthogonal",
     "text": "probes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-medic-gitcha"
     }
    },
    {
     "id": "link-medic-cassandra",
     "a": {
      "id": "nms-medic",
      "port": null
     },
     "b": {
      "id": "database-cassandra",
      "port": null
     },
     "route": "orthogonal",
     "text": "probes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-medic-cassandra"
     }
    },
    {
     "id": "link-gitcha-outside",
     "a": {
      "id": "server-gitcha",
      "port": null
     },
     "b": {
      "id": "zone-outside",
      "port": null
     },
     "route": "orthogonal",
     "text": "payments, NextTime, ring, DNS",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan"
     },
     "style": {},
     "data": {
      "tf_ref": "link-gitcha-outside"
     }
    },
    {
     "id": "link-medic-claudeapi",
     "a": {
      "id": "nms-medic",
      "port": null
     },
     "b": {
      "id": "cloud-claudeapi",
      "port": null
     },
     "route": "orthogonal",
     "text": "optional --ask",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan"
     },
     "style": {},
     "data": {
      "tf_ref": "link-medic-claudeapi"
     }
    },
    {
     "id": "link-outside-proxy",
     "a": {
      "id": "zone-outside",
      "port": null
     },
     "b": {
      "id": "lb-proxy",
      "port": null
     },
     "route": "orthogonal",
     "text": "signed webhooks (Stripe, PayPal)",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "wan"
     },
     "style": {},
     "data": {
      "tf_ref": "link-outside-proxy"
     }
    }
   ]
  },
  {
   "id": "pg-request",
   "name": "How Gitcha works (Bryant) 2 One request",
   "layers": [
    {
     "id": "L1",
     "name": "Drawing",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "L1",
   "nodes": [
    {
     "id": "text-title2",
     "type": "text",
     "x": 0,
     "y": -150,
     "w": 1200,
     "h": 70,
     "text": "One request through gitcha-server\nEvery HTTP request takes this path. The code fields name the function that does each step.",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "text-title2"
     },
     "style": {}
    },
    {
     "id": "ellipse-arrive",
     "type": "ellipse",
     "x": 0,
     "y": 0,
     "w": 220,
     "h": 84,
     "text": "A request arrives from the proxy",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-arrive",
      "what": "One handler in createApp takes every request.",
      "code": "server/src/app.js#createApp"
     },
     "style": {}
    },
    {
     "id": "rect-ip",
     "type": "rect",
     "x": 0,
     "y": 140,
     "w": 220,
     "h": 84,
     "text": "Who is calling?\nclientIp",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-ip",
      "what": "Behind a trusted proxy, believe only the X-Forwarded-For entry the nearest trusted proxy added.",
      "code": "server/src/http.js#clientIp"
     },
     "style": {}
    },
    {
     "id": "diamond-hosted",
     "type": "diamond",
     "x": 0,
     "y": 280,
     "w": 220,
     "h": 110,
     "text": "Is the Host a hosted site?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-hosted",
      "code": "server/src/hosting/sites.js#siteForHost"
     },
     "style": {}
    },
    {
     "id": "rect-hosted",
     "type": "rect",
     "x": 320,
     "y": 290,
     "w": 220,
     "h": 84,
     "text": "serveHosted: files from the active deploy, or a function call",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-hosted",
      "what": "Hosted names never reach the Gitcha pages or APIs, so a user's site cannot act as the person viewing it.",
      "code": "server/src/hosting/api.js#serveHosted"
     },
     "style": {}
    },
    {
     "id": "diamond-xsite",
     "type": "diamond",
     "x": 0,
     "y": 450,
     "w": 220,
     "h": 110,
     "text": "An /api/ call marked cross-site?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-xsite",
      "code": "server/src/app.js#createApp"
     },
     "style": {}
    },
    {
     "id": "rect-refuse",
     "type": "rect",
     "x": 320,
     "y": 460,
     "w": 220,
     "h": 84,
     "text": "Refused",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-refuse",
      "what": "A browser said another site sent it (Sec-Fetch-Site: cross-site)."
     },
     "style": {}
    },
    {
     "id": "rect-route",
     "type": "rect",
     "x": 0,
     "y": 620,
     "w": 220,
     "h": 84,
     "text": "Router finds the route",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-route",
      "code": "server/src/http.js#Router"
     },
     "style": {}
    },
    {
     "id": "diamond-guard",
     "type": "diamond",
     "x": 0,
     "y": 760,
     "w": 220,
     "h": 120,
     "text": "Guard: signed in, and no 2FA check due?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-guard",
      "what": "verified(ctx); verified(ctx, { sensitive: true }) adds the 10-minute rule.",
      "code": "server/src/api.js#makeGuards"
     },
     "style": {}
    },
    {
     "id": "rect-mfa",
     "type": "rect",
     "x": 320,
     "y": 778,
     "w": 220,
     "h": 84,
     "text": "Answer mfa_required, or show \"Confirm it's you\"",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-mfa",
      "what": "interval (12 h), new_network, sensitive (10 min). An admin can lock any of them on.",
      "code": "server/src/auth.js#mfaState"
     },
     "style": {}
    },
    {
     "id": "rect-service",
     "type": "rect",
     "x": 0,
     "y": 940,
     "w": 220,
     "h": 90,
     "text": "The service does the work (auth, packages, sites, jobs, billing, social, ops)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-service",
      "code": "server/src/app.js#createApp"
     },
     "style": {}
    },
    {
     "id": "cylinder-tx",
     "type": "cylinder",
     "x": 0,
     "y": 1090,
     "w": 220,
     "h": 100,
     "text": "store.tx: saved whole, or not at all",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "cylinder-tx",
      "what": "If the change throws partway or the write fails, memory goes back to the last save.",
      "code": "server/src/store.js#tx"
     },
     "style": {}
    },
    {
     "id": "cylinder-file",
     "type": "cylinder",
     "x": -260,
     "y": 1250,
     "w": 220,
     "h": 100,
     "text": "File: db.json (temp file, fsync, rename)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "cylinder-file",
      "code": "server/src/store.js#writeFileDurably"
     },
     "style": {}
    },
    {
     "id": "cylinder-cass",
     "type": "cylinder",
     "x": 260,
     "y": 1250,
     "w": 220,
     "h": 100,
     "text": "Cassandra: changed rows, one batch, lease checked",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "cylinder-cass",
      "code": "server/src/store-cassandra.js"
     },
     "style": {}
    },
    {
     "id": "rect-reply",
     "type": "rect",
     "x": 640,
     "y": 940,
     "w": 260,
     "h": 100,
     "text": "Reply: JSON, or an HTML page built with h`...` (every value escaped), plus security headers (CSP)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-reply",
      "code": "server/src/http.js#send"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "link-arrive-ip",
     "a": {
      "id": "ellipse-arrive",
      "port": null
     },
     "b": {
      "id": "rect-ip",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-arrive-ip"
     }
    },
    {
     "id": "link-ip-hosted",
     "a": {
      "id": "rect-ip",
      "port": null
     },
     "b": {
      "id": "diamond-hosted",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-ip-hosted"
     }
    },
    {
     "id": "link-hosted-hosted",
     "a": {
      "id": "diamond-hosted",
      "port": null
     },
     "b": {
      "id": "rect-hosted",
      "port": null
     },
     "route": "orthogonal",
     "text": "yes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-hosted-hosted"
     }
    },
    {
     "id": "link-hosted-xsite",
     "a": {
      "id": "diamond-hosted",
      "port": null
     },
     "b": {
      "id": "diamond-xsite",
      "port": null
     },
     "route": "orthogonal",
     "text": "no",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-hosted-xsite"
     }
    },
    {
     "id": "link-xsite-refuse",
     "a": {
      "id": "diamond-xsite",
      "port": null
     },
     "b": {
      "id": "rect-refuse",
      "port": null
     },
     "route": "orthogonal",
     "text": "yes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-xsite-refuse"
     }
    },
    {
     "id": "link-xsite-route",
     "a": {
      "id": "diamond-xsite",
      "port": null
     },
     "b": {
      "id": "rect-route",
      "port": null
     },
     "route": "orthogonal",
     "text": "no",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-xsite-route"
     }
    },
    {
     "id": "link-route-guard",
     "a": {
      "id": "rect-route",
      "port": null
     },
     "b": {
      "id": "diamond-guard",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-route-guard"
     }
    },
    {
     "id": "link-guard-mfa",
     "a": {
      "id": "diamond-guard",
      "port": null
     },
     "b": {
      "id": "rect-mfa",
      "port": null
     },
     "route": "orthogonal",
     "text": "check due",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-guard-mfa"
     }
    },
    {
     "id": "link-guard-service",
     "a": {
      "id": "diamond-guard",
      "port": null
     },
     "b": {
      "id": "rect-service",
      "port": null
     },
     "route": "orthogonal",
     "text": "ok",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-guard-service"
     }
    },
    {
     "id": "link-service-tx",
     "a": {
      "id": "rect-service",
      "port": null
     },
     "b": {
      "id": "cylinder-tx",
      "port": null
     },
     "route": "orthogonal",
     "text": "change data",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-service-tx"
     }
    },
    {
     "id": "link-tx-file",
     "a": {
      "id": "cylinder-tx",
      "port": null
     },
     "b": {
      "id": "cylinder-file",
      "port": null
     },
     "route": "orthogonal",
     "text": "GITCHA_STORE=file",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-tx-file"
     }
    },
    {
     "id": "link-tx-cass",
     "a": {
      "id": "cylinder-tx",
      "port": null
     },
     "b": {
      "id": "cylinder-cass",
      "port": null
     },
     "route": "orthogonal",
     "text": "GITCHA_STORE=cassandra",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-tx-cass"
     }
    },
    {
     "id": "link-service-reply",
     "a": {
      "id": "rect-service",
      "port": null
     },
     "b": {
      "id": "rect-reply",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-service-reply"
     }
    }
   ]
  },
  {
   "id": "pg-accounts",
   "name": "How Gitcha works (Bryant) 3 Accounts and 2FA",
   "layers": [
    {
     "id": "L1",
     "name": "Drawing",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "L1",
   "nodes": [
    {
     "id": "text-title3",
     "type": "text",
     "x": 0,
     "y": -150,
     "w": 1400,
     "h": 70,
     "text": "Signing up, signing in, and the checks after\nA password alone never signs anyone in. Passwords are scrypt-hashed and then encrypted; authenticator secrets and emails are encrypted; tokens are stored only as hashes.",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "text-title3"
     },
     "style": {}
    },
    {
     "id": "ellipse-signup",
     "type": "ellipse",
     "x": 0,
     "y": 0,
     "w": 220,
     "h": 84,
     "text": "Sign up",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-signup",
      "code": "server/src/web.js#registerWeb"
     },
     "style": {}
    },
    {
     "id": "rect-register",
     "type": "rect",
     "x": 0,
     "y": 140,
     "w": 220,
     "h": 84,
     "text": "Check name, password (twice), optional email (twice)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-register",
      "what": "At least 10 characters, not containing the username. Rate limited: 5 sign-ups an hour per address.",
      "code": "server/src/auth.js#register"
     },
     "style": {}
    },
    {
     "id": "rect-pending",
     "type": "rect",
     "x": 0,
     "y": 280,
     "w": 220,
     "h": 84,
     "text": "Account waits as pending_mfa (30 minutes)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-pending",
      "code": "server/src/auth.js#sweep"
     },
     "style": {}
    },
    {
     "id": "rect-enroll",
     "type": "rect",
     "x": 0,
     "y": 420,
     "w": 220,
     "h": 84,
     "text": "Scan the QR code, type a code",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-enroll",
      "what": "TOTP: the app and the server turn the shared secret and the 30-second time slot into the same 6 digits.",
      "code": "server/src/auth.js#confirmEnrollment"
     },
     "style": {}
    },
    {
     "id": "rect-active",
     "type": "rect",
     "x": 0,
     "y": 560,
     "w": 220,
     "h": 90,
     "text": "Active, with 10 recovery codes (shown once, stored as keyed hashes)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-active",
      "code": "server/src/crypto.js#generateRecoveryCodes"
     },
     "style": {}
    },
    {
     "id": "ellipse-signin",
     "type": "ellipse",
     "x": 400,
     "y": 0,
     "w": 220,
     "h": 84,
     "text": "Sign in (web, CLI or C4OK)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-signin",
      "code": "server/src/api.js#registerApi"
     },
     "style": {}
    },
    {
     "id": "rect-password",
     "type": "rect",
     "x": 400,
     "y": 140,
     "w": 220,
     "h": 84,
     "text": "Password checked (rate limits first, then scrypt)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-password",
      "what": "Unknown names are checked against a dummy hash, so timing reveals nothing.",
      "code": "server/src/auth.js#beginLogin"
     },
     "style": {}
    },
    {
     "id": "rect-ticket",
     "type": "rect",
     "x": 400,
     "y": 280,
     "w": 220,
     "h": 84,
     "text": "A 5-minute ticket, not a session",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-ticket",
      "code": "server/src/auth.js#beginLogin"
     },
     "style": {}
    },
    {
     "id": "rect-code",
     "type": "rect",
     "x": 400,
     "y": 420,
     "w": 220,
     "h": 84,
     "text": "A 6-digit code (each one works once)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-code",
      "code": "server/src/auth.js#verifySecondFactor"
     },
     "style": {}
    },
    {
     "id": "rect-session",
     "type": "rect",
     "x": 400,
     "y": 560,
     "w": 220,
     "h": 90,
     "text": "Session or device token (only its hash is stored)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-session",
      "code": "server/src/auth.js#createSession"
     },
     "style": {}
    },
    {
     "id": "diamond-check",
     "type": "diamond",
     "x": 800,
     "y": 280,
     "w": 220,
     "h": 110,
     "text": "Later: is a check due?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-check",
      "what": "interval: 12 hours; new_network: a network this token has not used; sensitive: publish, deploy, keys and more, within 10 minutes.",
      "code": "server/src/auth.js#mfaState"
     },
     "style": {}
    },
    {
     "id": "rect-stepup",
     "type": "rect",
     "x": 800,
     "y": 440,
     "w": 220,
     "h": 84,
     "text": "Confirm it's you: a fresh code",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-stepup",
      "code": "server/src/auth.js#stepUp"
     },
     "style": {}
    },
    {
     "id": "rect-toggle",
     "type": "rect",
     "x": 800,
     "y": 0,
     "w": 260,
     "h": 110,
     "text": "Each person may turn the follow-up checks off with a code sent with that change; an admin can lock them on",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-toggle",
      "code": "server/src/auth.js#confirmAction"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "link-signup-register",
     "a": {
      "id": "ellipse-signup",
      "port": null
     },
     "b": {
      "id": "rect-register",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-signup-register"
     }
    },
    {
     "id": "link-register-pending",
     "a": {
      "id": "rect-register",
      "port": null
     },
     "b": {
      "id": "rect-pending",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-register-pending"
     }
    },
    {
     "id": "link-pending-enroll",
     "a": {
      "id": "rect-pending",
      "port": null
     },
     "b": {
      "id": "rect-enroll",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-pending-enroll"
     }
    },
    {
     "id": "link-enroll-active",
     "a": {
      "id": "rect-enroll",
      "port": null
     },
     "b": {
      "id": "rect-active",
      "port": null
     },
     "route": "orthogonal",
     "text": "code correct",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-enroll-active"
     }
    },
    {
     "id": "link-signin-password",
     "a": {
      "id": "ellipse-signin",
      "port": null
     },
     "b": {
      "id": "rect-password",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-signin-password"
     }
    },
    {
     "id": "link-password-ticket",
     "a": {
      "id": "rect-password",
      "port": null
     },
     "b": {
      "id": "rect-ticket",
      "port": null
     },
     "route": "orthogonal",
     "text": "correct",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-password-ticket"
     }
    },
    {
     "id": "link-ticket-code",
     "a": {
      "id": "rect-ticket",
      "port": null
     },
     "b": {
      "id": "rect-code",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-ticket-code"
     }
    },
    {
     "id": "link-code-session",
     "a": {
      "id": "rect-code",
      "port": null
     },
     "b": {
      "id": "rect-session",
      "port": null
     },
     "route": "orthogonal",
     "text": "correct",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-code-session"
     }
    },
    {
     "id": "link-session-check",
     "a": {
      "id": "rect-session",
      "port": null
     },
     "b": {
      "id": "diamond-check",
      "port": null
     },
     "route": "orthogonal",
     "text": "each request",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-session-check"
     }
    },
    {
     "id": "link-check-stepup",
     "a": {
      "id": "diamond-check",
      "port": null
     },
     "b": {
      "id": "rect-stepup",
      "port": null
     },
     "route": "orthogonal",
     "text": "yes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-check-stepup"
     }
    },
    {
     "id": "link-toggle-check",
     "a": {
      "id": "rect-toggle",
      "port": null
     },
     "b": {
      "id": "diamond-check",
      "port": null
     },
     "route": "orthogonal",
     "text": "settings",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-toggle-check"
     }
    }
   ]
  },
  {
   "id": "pg-work",
   "name": "How Gitcha works (Bryant) 4 Push, review, build, deploy",
   "layers": [
    {
     "id": "L1",
     "name": "Drawing",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "L1",
   "nodes": [
    {
     "id": "text-title4",
     "type": "text",
     "x": 0,
     "y": -150,
     "w": 1400,
     "h": 70,
     "text": "How work moves: a push, a Second Reader review, a CI build, a deploy\nThe server queues work; only runners run it.",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "text-title4"
     },
     "style": {}
    },
    {
     "id": "ellipse-push",
     "type": "ellipse",
     "x": 0,
     "y": 0,
     "w": 220,
     "h": 84,
     "text": "git push (person or agent)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-push",
      "code": "server/src/git/http.js#serve"
     },
     "style": {}
    },
    {
     "id": "rect-access",
     "type": "rect",
     "x": 0,
     "y": 140,
     "w": 220,
     "h": 84,
     "text": "Read and write rights, 2FA; private projects answer 404",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-access",
      "code": "server/src/git/projects.js#canWrite"
     },
     "style": {}
    },
    {
     "id": "rect-record",
     "type": "rect",
     "x": 0,
     "y": 280,
     "w": 220,
     "h": 100,
     "text": "Record the push: new commits read from git objects, signatures checked against the pusher's keys",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-record",
      "code": "server/src/git/projects.js#afterPush"
     },
     "style": {}
    },
    {
     "id": "rect-credit",
     "type": "rect",
     "x": 0,
     "y": 440,
     "w": 220,
     "h": 100,
     "text": "Credit each commit to the byline's agent (only the pusher's own) or to the pusher",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-credit",
      "code": "server/src/social/agents.js#fromByline"
     },
     "style": {}
    },
    {
     "id": "diamond-pair",
     "type": "diamond",
     "x": 0,
     "y": 600,
     "w": 220,
     "h": 120,
     "text": "Two agents on the same branches within 7 days?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-pair",
      "code": "server/src/social/tasks.js#secondReaderFor"
     },
     "style": {}
    },
    {
     "id": "rect-sr",
     "type": "rect",
     "x": 0,
     "y": 780,
     "w": 220,
     "h": 90,
     "text": "Second Reader task opens (mirrored to NextTime for public projects)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-sr",
      "code": "server/src/social/tasks.js#secondReaderFor"
     },
     "style": {}
    },
    {
     "id": "rect-claim",
     "type": "rect",
     "x": 0,
     "y": 930,
     "w": 220,
     "h": 84,
     "text": "An independent agent claims it (72 hours)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-claim",
      "what": "Not one of the two agents, not owned by either owner, and opted in to this kind of work.",
      "code": "server/src/social/tasks.js#claim"
     },
     "style": {}
    },
    {
     "id": "rect-review",
     "type": "rect",
     "x": 0,
     "y": 1070,
     "w": 220,
     "h": 84,
     "text": "Verdict, summary, findings",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-review",
      "code": "server/src/social/tasks.js#submitReview"
     },
     "style": {}
    },
    {
     "id": "rect-accept",
     "type": "rect",
     "x": 0,
     "y": 1210,
     "w": 220,
     "h": 90,
     "text": "A maintainer (not the reviewer's owner) accepts: credits paid, capped per day",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-accept",
      "code": "server/src/social/budgets.js#award"
     },
     "style": {}
    },
    {
     "id": "ellipse-ci",
     "type": "ellipse",
     "x": 420,
     "y": 0,
     "w": 220,
     "h": 90,
     "text": "A build starts: on publish, or POST /api/v1/ci/runs",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-ci",
      "code": "server/src/compute/ci.js#run"
     },
     "style": {}
    },
    {
     "id": "rect-pipeline",
     "type": "rect",
     "x": 420,
     "y": 150,
     "w": 220,
     "h": 90,
     "text": "Read the pipeline: gitcha-ci.json, .org, or a TopoForge drawing",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-pipeline",
      "code": "server/src/compute/formats.js#normalizePipeline"
     },
     "style": {}
    },
    {
     "id": "rect-quota",
     "type": "rect",
     "x": 420,
     "y": 300,
     "w": 220,
     "h": 90,
     "text": "Deploy rights, queue room, billing quota; minutes or credits held",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-quota",
      "code": "server/src/billing/billing.js#reserve"
     },
     "style": {}
    },
    {
     "id": "cylinder-queue",
     "type": "cylinder",
     "x": 420,
     "y": 450,
     "w": 220,
     "h": 100,
     "text": "The job queue",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "cylinder-queue",
      "code": "server/src/compute/jobs.js#JobService"
     },
     "style": {}
    },
    {
     "id": "rect-runnerclaim",
     "type": "rect",
     "x": 420,
     "y": 610,
     "w": 220,
     "h": 90,
     "text": "A runner long-polls and claims it (a job token for that job and runner only)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-runnerclaim",
      "code": "server/src/compute/jobs.js#claim"
     },
     "style": {}
    },
    {
     "id": "rect-vm",
     "type": "rect",
     "x": 420,
     "y": 760,
     "w": 220,
     "h": 90,
     "text": "bContain VM runs the steps; the log streams back (5 MB cap)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-vm",
      "code": "runner/lib/runner.js#executeJob"
     },
     "style": {}
    },
    {
     "id": "rect-complete",
     "type": "rect",
     "x": 420,
     "y": 910,
     "w": 220,
     "h": 84,
     "text": "Result reported; minutes counted once",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-complete",
      "code": "server/src/compute/ci.js#afterJob"
     },
     "style": {}
    },
    {
     "id": "diamond-deploy",
     "type": "diamond",
     "x": 420,
     "y": 1050,
     "w": 220,
     "h": 110,
     "text": "Passed, and asked to deploy?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-deploy",
      "code": "server/src/compute/ci.js#afterJob"
     },
     "style": {}
    },
    {
     "id": "rect-site",
     "type": "rect",
     "x": 420,
     "y": 1210,
     "w": 220,
     "h": 90,
     "text": "New deploy of the site; older ones kept for instant rollback",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-site",
      "code": "server/src/hosting/sites.js#deploy"
     },
     "style": {}
    },
    {
     "id": "ellipse-fn",
     "type": "ellipse",
     "x": 840,
     "y": 600,
     "w": 220,
     "h": 90,
     "text": "A function call: a site route or the invoke API",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-fn",
      "code": "server/src/compute/functions.js#invoke"
     },
     "style": {}
    },
    {
     "id": "rect-fnrun",
     "type": "rect",
     "x": 840,
     "y": 760,
     "w": 220,
     "h": 100,
     "text": "Queued like a build; a runner runs it in a harness (node, python, sh, orgvm, chain)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-fnrun",
      "code": "runner/harness/node.mjs"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "link-fn-queue",
     "a": {
      "id": "ellipse-fn",
      "port": null
     },
     "b": {
      "id": "cylinder-queue",
      "port": null
     },
     "route": "orthogonal",
     "text": "queued",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-fn-queue"
     }
    },
    {
     "id": "link-push-access",
     "a": {
      "id": "ellipse-push",
      "port": null
     },
     "b": {
      "id": "rect-access",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-push-access"
     }
    },
    {
     "id": "link-access-record",
     "a": {
      "id": "rect-access",
      "port": null
     },
     "b": {
      "id": "rect-record",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-access-record"
     }
    },
    {
     "id": "link-record-credit",
     "a": {
      "id": "rect-record",
      "port": null
     },
     "b": {
      "id": "rect-credit",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-record-credit"
     }
    },
    {
     "id": "link-credit-pair",
     "a": {
      "id": "rect-credit",
      "port": null
     },
     "b": {
      "id": "diamond-pair",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-credit-pair"
     }
    },
    {
     "id": "link-pair-sr",
     "a": {
      "id": "diamond-pair",
      "port": null
     },
     "b": {
      "id": "rect-sr",
      "port": null
     },
     "route": "orthogonal",
     "text": "yes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-pair-sr"
     }
    },
    {
     "id": "link-sr-claim",
     "a": {
      "id": "rect-sr",
      "port": null
     },
     "b": {
      "id": "rect-claim",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-sr-claim"
     }
    },
    {
     "id": "link-claim-review",
     "a": {
      "id": "rect-claim",
      "port": null
     },
     "b": {
      "id": "rect-review",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-claim-review"
     }
    },
    {
     "id": "link-review-accept",
     "a": {
      "id": "rect-review",
      "port": null
     },
     "b": {
      "id": "rect-accept",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-review-accept"
     }
    },
    {
     "id": "link-ci-pipeline",
     "a": {
      "id": "ellipse-ci",
      "port": null
     },
     "b": {
      "id": "rect-pipeline",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-ci-pipeline"
     }
    },
    {
     "id": "link-pipeline-quota",
     "a": {
      "id": "rect-pipeline",
      "port": null
     },
     "b": {
      "id": "rect-quota",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-pipeline-quota"
     }
    },
    {
     "id": "link-quota-queue",
     "a": {
      "id": "rect-quota",
      "port": null
     },
     "b": {
      "id": "cylinder-queue",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-quota-queue"
     }
    },
    {
     "id": "link-queue-runnerclaim",
     "a": {
      "id": "cylinder-queue",
      "port": null
     },
     "b": {
      "id": "rect-runnerclaim",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-queue-runnerclaim"
     }
    },
    {
     "id": "link-runnerclaim-vm",
     "a": {
      "id": "rect-runnerclaim",
      "port": null
     },
     "b": {
      "id": "rect-vm",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-runnerclaim-vm"
     }
    },
    {
     "id": "link-vm-complete",
     "a": {
      "id": "rect-vm",
      "port": null
     },
     "b": {
      "id": "rect-complete",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-vm-complete"
     }
    },
    {
     "id": "link-complete-deploy",
     "a": {
      "id": "rect-complete",
      "port": null
     },
     "b": {
      "id": "diamond-deploy",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-complete-deploy"
     }
    },
    {
     "id": "link-deploy-site",
     "a": {
      "id": "diamond-deploy",
      "port": null
     },
     "b": {
      "id": "rect-site",
      "port": null
     },
     "route": "orthogonal",
     "text": "yes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-deploy-site"
     }
    },
    {
     "id": "link-runnerclaim-fnrun",
     "a": {
      "id": "rect-runnerclaim",
      "port": null
     },
     "b": {
      "id": "rect-fnrun",
      "port": null
     },
     "route": "orthogonal",
     "text": "kind fn",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-runnerclaim-fnrun"
     }
    }
   ]
  },
  {
   "id": "pg-care",
   "name": "How Gitcha works (Bryant) 5 Keeping it running",
   "layers": [
    {
     "id": "L1",
     "name": "Drawing",
     "visible": true,
     "locked": false
    }
   ],
   "activeLayer": "L1",
   "nodes": [
    {
     "id": "text-title5",
     "type": "text",
     "x": 0,
     "y": -150,
     "w": 1400,
     "h": 70,
     "text": "Keeping it running: the medic, and Omni\nThe medic only reads to diagnose, and mends only what is safe. Omni lets a Claude session in a cloud sandbox reach tools a person runs on their own hosts.",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "text-title5"
     },
     "style": {}
    },
    {
     "id": "ellipse-timer",
     "type": "ellipse",
     "x": 0,
     "y": 0,
     "w": 220,
     "h": 90,
     "text": "gitcha-medic.timer (or a person runs gitcha-medic)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-timer",
      "code": "deploy/gitcha-medic.timer"
     },
     "style": {}
    },
    {
     "id": "rect-probes",
     "type": "rect",
     "x": 0,
     "y": 150,
     "w": 220,
     "h": 90,
     "text": "Probes read: services, journal, ports, health URL, Cassandra, the proxy",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-probes",
      "code": "medic/lib/probes.js"
     },
     "style": {}
    },
    {
     "id": "rect-rules",
     "type": "rect",
     "x": 0,
     "y": 300,
     "w": 220,
     "h": 84,
     "text": "Rules turn facts into findings, in plain words",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-rules",
      "code": "medic/lib/rules.js"
     },
     "style": {}
    },
    {
     "id": "diamond-remedy",
     "type": "diamond",
     "x": 0,
     "y": 440,
     "w": 220,
     "h": 110,
     "text": "A remedy that is safe?",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "diamond-remedy",
      "code": "medic/lib/remedies.js"
     },
     "style": {}
    },
    {
     "id": "rect-heal",
     "type": "rect",
     "x": -260,
     "y": 600,
     "w": 220,
     "h": 90,
     "text": "Run it (with --heal): start a stopped service, restart a stuck one",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-heal",
      "code": "medic/lib/remedies.js"
     },
     "style": {}
    },
    {
     "id": "rect-park",
     "type": "rect",
     "x": 260,
     "y": 600,
     "w": 220,
     "h": 90,
     "text": "Park it for a person (confirm class: --yes NAME)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-park",
      "code": "medic/lib/remedies.js"
     },
     "style": {}
    },
    {
     "id": "rect-agent",
     "type": "rect",
     "x": 300,
     "y": 300,
     "w": 240,
     "h": 100,
     "text": "Optional: ask a model about what the rules left unexplained (redacted facts only)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-agent",
      "code": "medic/lib/agent.js#consult"
     },
     "style": {}
    },
    {
     "id": "rect-tell",
     "type": "rect",
     "x": 0,
     "y": 760,
     "w": 220,
     "h": 90,
     "text": "Tell syslog always, and the configured rooms (a change, or a heartbeat)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-tell",
      "code": "medic/lib/channels.js#announce"
     },
     "style": {}
    },
    {
     "id": "ellipse-hub",
     "type": "ellipse",
     "x": 800,
     "y": 0,
     "w": 220,
     "h": 90,
     "text": "Claude in a cloud sandbox calls omni-hub (stdio)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "ellipse-hub",
      "code": "omni/lib/hub.js"
     },
     "style": {}
    },
    {
     "id": "rect-hubtools",
     "type": "rect",
     "x": 800,
     "y": 150,
     "w": 220,
     "h": 90,
     "text": "The hub offers every member's tools as its own, plus omni_status and skill_read",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-hubtools",
      "code": "omni/lib/hub.js"
     },
     "style": {}
    },
    {
     "id": "rect-https",
     "type": "rect",
     "x": 800,
     "y": 300,
     "w": 220,
     "h": 90,
     "text": "https with one bearer token per member (an environment secret, read per call)",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-https",
      "code": "omni/lib/remote.js"
     },
     "style": {}
    },
    {
     "id": "rect-gw",
     "type": "rect",
     "x": 800,
     "y": 450,
     "w": 220,
     "h": 90,
     "text": "nginx (TLS), then omni-gateway on 127.0.0.1:8769",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-gw",
      "code": "deploy/nginx-omni.conf.example"
     },
     "style": {}
    },
    {
     "id": "rect-members",
     "type": "rect",
     "x": 800,
     "y": 600,
     "w": 220,
     "h": 90,
     "text": "One stdio child per member: gitcha-mcp, mentcached, persona, tavern, ...",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-members",
      "code": "omni/lib/gateway.js"
     },
     "style": {}
    },
    {
     "id": "rect-label",
     "type": "rect",
     "x": 800,
     "y": 750,
     "w": 220,
     "h": 80,
     "text": "What a member answers is labelled as other people's text",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "rect-label",
      "code": "omni/lib/hub.js"
     },
     "style": {}
    },
    {
     "id": "note-medicomni",
     "type": "note",
     "x": 300,
     "y": 900,
     "w": 260,
     "h": 70,
     "text": "The medic also watches omni-gateway when it is installed.",
     "layer": "L1",
     "parent": null,
     "props": {},
     "data": {
      "tf_ref": "note-medicomni"
     },
     "style": {}
    }
   ],
   "links": [
    {
     "id": "link-timer-probes",
     "a": {
      "id": "ellipse-timer",
      "port": null
     },
     "b": {
      "id": "rect-probes",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-timer-probes"
     }
    },
    {
     "id": "link-probes-rules",
     "a": {
      "id": "rect-probes",
      "port": null
     },
     "b": {
      "id": "rect-rules",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-probes-rules"
     }
    },
    {
     "id": "link-rules-remedy",
     "a": {
      "id": "rect-rules",
      "port": null
     },
     "b": {
      "id": "diamond-remedy",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-rules-remedy"
     }
    },
    {
     "id": "link-remedy-heal",
     "a": {
      "id": "diamond-remedy",
      "port": null
     },
     "b": {
      "id": "rect-heal",
      "port": null
     },
     "route": "orthogonal",
     "text": "yes",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-remedy-heal"
     }
    },
    {
     "id": "link-remedy-park",
     "a": {
      "id": "diamond-remedy",
      "port": null
     },
     "b": {
      "id": "rect-park",
      "port": null
     },
     "route": "orthogonal",
     "text": "no",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-remedy-park"
     }
    },
    {
     "id": "link-rules-agent",
     "a": {
      "id": "rect-rules",
      "port": null
     },
     "b": {
      "id": "rect-agent",
      "port": null
     },
     "route": "orthogonal",
     "text": "--ask",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-rules-agent"
     }
    },
    {
     "id": "link-agent-remedy",
     "a": {
      "id": "rect-agent",
      "port": null
     },
     "b": {
      "id": "diamond-remedy",
      "port": null
     },
     "route": "orthogonal",
     "text": "asks for a remedy",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-agent-remedy"
     }
    },
    {
     "id": "link-heal-tell",
     "a": {
      "id": "rect-heal",
      "port": null
     },
     "b": {
      "id": "rect-tell",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-heal-tell"
     }
    },
    {
     "id": "link-park-tell",
     "a": {
      "id": "rect-park",
      "port": null
     },
     "b": {
      "id": "rect-tell",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-park-tell"
     }
    },
    {
     "id": "link-hub-hubtools",
     "a": {
      "id": "ellipse-hub",
      "port": null
     },
     "b": {
      "id": "rect-hubtools",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-hub-hubtools"
     }
    },
    {
     "id": "link-hubtools-https",
     "a": {
      "id": "rect-hubtools",
      "port": null
     },
     "b": {
      "id": "rect-https",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-hubtools-https"
     }
    },
    {
     "id": "link-https-gw",
     "a": {
      "id": "rect-https",
      "port": null
     },
     "b": {
      "id": "rect-gw",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-https-gw"
     }
    },
    {
     "id": "link-gw-members",
     "a": {
      "id": "rect-gw",
      "port": null
     },
     "b": {
      "id": "rect-members",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-gw-members"
     }
    },
    {
     "id": "link-members-label",
     "a": {
      "id": "rect-members",
      "port": null
     },
     "b": {
      "id": "rect-label",
      "port": null
     },
     "route": "orthogonal",
     "text": "",
     "layer": "L1",
     "arrows": "end",
     "props": {
      "medium": "logical"
     },
     "style": {},
     "data": {
      "tf_ref": "link-members-label"
     }
    }
   ]
  }
 ],
 "cur": 0
}