gini-toolkit 6.0.1.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. gini/__init__.py +12 -0
  2. gini/__main__.py +107 -0
  3. gini/_version.py +24 -0
  4. gini/agent/__init__.py +17 -0
  5. gini/agent/agent_gamemaster.py +140 -0
  6. gini/agent/api.py +291 -0
  7. gini/agent/ask.py +123 -0
  8. gini/agent/authoring.py +72 -0
  9. gini/agent/blackboard.py +114 -0
  10. gini/agent/contracts.py +142 -0
  11. gini/agent/domains.py +91 -0
  12. gini/agent/embed.py +123 -0
  13. gini/agent/gamemaster.py +256 -0
  14. gini/agent/kb.py +148 -0
  15. gini/agent/lesson_resolver.py +261 -0
  16. gini/agent/llm/__init__.py +5 -0
  17. gini/agent/llm/backend.py +43 -0
  18. gini/agent/llm/fake.py +25 -0
  19. gini/agent/llm/ollama.py +206 -0
  20. gini/agent/loop.py +258 -0
  21. gini/agent/mcp_server.py +86 -0
  22. gini/agent/meaning.py +225 -0
  23. gini/agent/mission.py +210 -0
  24. gini/agent/mission_controller.py +208 -0
  25. gini/agent/narration.py +116 -0
  26. gini/agent/notifier.py +86 -0
  27. gini/agent/personas.py +79 -0
  28. gini/agent/reasoning.py +172 -0
  29. gini/agent/recall.py +248 -0
  30. gini/agent/session.py +79 -0
  31. gini/agent/teaching_center.py +482 -0
  32. gini/agent/tools/__init__.py +3 -0
  33. gini/agent/tools/registry.py +193 -0
  34. gini/agent/twin/__init__.py +28 -0
  35. gini/agent/twin/authoring.py +71 -0
  36. gini/agent/twin/contracts.py +54 -0
  37. gini/agent/twin/dialectic.py +189 -0
  38. gini/agent/twin/harness.py +93 -0
  39. gini/agent/twin/justify.py +156 -0
  40. gini/agent/twin/learner.py +64 -0
  41. gini/agent/twin/mission.py +60 -0
  42. gini/agent/twin/os_coach.py +79 -0
  43. gini/agent/twin/salience.py +30 -0
  44. gini/agent/understand.py +250 -0
  45. gini/agent/verifiers.py +106 -0
  46. gini/agent/wizard.py +178 -0
  47. gini/agent/xv6_pack.py +74 -0
  48. gini/app/__init__.py +3 -0
  49. gini/app/context.py +368 -0
  50. gini/app/paths.py +121 -0
  51. gini/data/README.md +21 -0
  52. gini/domain/__init__.py +9 -0
  53. gini/domain/assembly.py +209 -0
  54. gini/domain/authoring.py +353 -0
  55. gini/domain/blueprints.py +5 -0
  56. gini/domain/capabilities.py +177 -0
  57. gini/domain/catalog.py +85 -0
  58. gini/domain/certify.py +201 -0
  59. gini/domain/compose.py +413 -0
  60. gini/domain/composition.py +88 -0
  61. gini/domain/concepts.py +383 -0
  62. gini/domain/connection_rules.py +269 -0
  63. gini/domain/constraints.py +153 -0
  64. gini/domain/content.py +59 -0
  65. gini/domain/cpu_journey.py +89 -0
  66. gini/domain/devices.py +747 -0
  67. gini/domain/diagnose.py +201 -0
  68. gini/domain/element_guide.py +327 -0
  69. gini/domain/explain.py +90 -0
  70. gini/domain/fingerprint.py +201 -0
  71. gini/domain/firewall.py +34 -0
  72. gini/domain/flowlog.py +61 -0
  73. gini/domain/flowtable.py +179 -0
  74. gini/domain/fragment_yaml.py +230 -0
  75. gini/domain/fragments.py +169 -0
  76. gini/domain/games/__init__.py +2 -0
  77. gini/domain/games/paging_games.py +119 -0
  78. gini/domain/games/policy_game.py +86 -0
  79. gini/domain/games/process_game.py +48 -0
  80. gini/domain/games/thrash_game.py +75 -0
  81. gini/domain/games/translate_game.py +60 -0
  82. gini/domain/games/trap_game.py +86 -0
  83. gini/domain/grader.py +155 -0
  84. gini/domain/grouping.py +67 -0
  85. gini/domain/legality.py +103 -0
  86. gini/domain/lesson.py +241 -0
  87. gini/domain/lexicon.py +150 -0
  88. gini/domain/machine_state.py +410 -0
  89. gini/domain/missions/networking/basic-lan.yaml +32 -0
  90. gini/domain/missions/networking/cache-in-front.yaml +23 -0
  91. gini/domain/missions/networking/decouple-with-queue.yaml +31 -0
  92. gini/domain/missions/networking/drive-load.yaml +20 -0
  93. gini/domain/missions/networking/fix-the-address.yaml +75 -0
  94. gini/domain/missions/networking/fix-the-lan.yaml +43 -0
  95. gini/domain/missions/networking/inspect-flows.yaml +16 -0
  96. gini/domain/missions/networking/k8s-autoscale.yaml +27 -0
  97. gini/domain/missions/networking/least-privilege.yaml +21 -0
  98. gini/domain/missions/networking/load-balanced-web.yaml +29 -0
  99. gini/domain/missions/networking/observe-it.yaml +24 -0
  100. gini/domain/missions/networking/put-in-vpc.yaml +30 -0
  101. gini/domain/missions/networking/reachability-boundary.yaml +56 -0
  102. gini/domain/missions/networking/sdn-reactive.yaml +35 -0
  103. gini/domain/missions/networking/send-request.yaml +19 -0
  104. gini/domain/missions/networking/serverless-api.yaml +25 -0
  105. gini/domain/missions/networking/service-chain.yaml +33 -0
  106. gini/domain/missions/os/lottery-fix.yaml +19 -0
  107. gini/domain/missions/os/priority-fix.yaml +24 -0
  108. gini/domain/missions.py +111 -0
  109. gini/domain/modulechain.py +36 -0
  110. gini/domain/objectives.py +488 -0
  111. gini/domain/os_zoo.py +79 -0
  112. gini/domain/paging_sim.py +141 -0
  113. gini/domain/pricing.py +199 -0
  114. gini/domain/probes.py +226 -0
  115. gini/domain/profile.py +142 -0
  116. gini/domain/recipes.py +738 -0
  117. gini/domain/riders.py +309 -0
  118. gini/domain/router_modules.py +224 -0
  119. gini/domain/routetable.py +67 -0
  120. gini/domain/scoring.py +76 -0
  121. gini/domain/staging.py +122 -0
  122. gini/domain/syscall_builder.py +144 -0
  123. gini/domain/topic_cloud.py +62 -0
  124. gini/domain/topology.py +213 -0
  125. gini/domain/vocabulary.py +51 -0
  126. gini/domain/xv6.py +808 -0
  127. gini/domain/xv6_fs.py +250 -0
  128. gini/domain/xv6_runner.py +113 -0
  129. gini/domain/xv6_vm.py +385 -0
  130. gini/gloader.py +17 -0
  131. gini/runtime/__init__.py +18 -0
  132. gini/runtime/cloudfabric_agent.py +370 -0
  133. gini/runtime/console.py +68 -0
  134. gini/runtime/control.py +70 -0
  135. gini/runtime/frame.py +138 -0
  136. gini/runtime/gbridge.py +638 -0
  137. gini/runtime/grouter.py +223 -0
  138. gini/runtime/hostsim.py +90 -0
  139. gini/runtime/shuttle.py +348 -0
  140. gini/runtime/switch.py +109 -0
  141. gini/runtime/transport.py +77 -0
  142. gini/runtime/xv6_bridge.py +312 -0
  143. gini/server/__init__.py +22 -0
  144. gini/server/__main__.py +74 -0
  145. gini/server/app.py +140 -0
  146. gini/server/auth.py +82 -0
  147. gini/server/policy.py +57 -0
  148. gini/server/session.py +23 -0
  149. gini/services/__init__.py +15 -0
  150. gini/services/boardflash.py +248 -0
  151. gini/services/boardsetup.py +374 -0
  152. gini/services/cloud_catalog.py +143 -0
  153. gini/services/compiler.py +1858 -0
  154. gini/services/discovery.py +324 -0
  155. gini/services/gloader.py +183 -0
  156. gini/services/orchestrator.py +1460 -0
  157. gini/services/persistence.py +28 -0
  158. gini/services/probe_runner.py +149 -0
  159. gini/services/project.py +217 -0
  160. gini/services/remote.py +93 -0
  161. gini/services/rider_runner.py +96 -0
  162. gini/services/rider_session.py +171 -0
  163. gini/services/shadow_store.py +52 -0
  164. gini/services/terminal.py +45 -0
  165. gini/setup/__init__.py +17 -0
  166. gini/setup/cli.py +109 -0
  167. gini/setup/images.py +33 -0
  168. gini/setup/marker.py +43 -0
  169. gini/setup/runtime.py +69 -0
  170. gini/ui/__init__.py +3 -0
  171. gini/ui/assets/app_icon.icns +0 -0
  172. gini/ui/assets/app_icon.ico +0 -0
  173. gini/ui/assets/app_icon.png +0 -0
  174. gini/ui/assets/app_icon_1024.png +0 -0
  175. gini/ui/assets/cue/_w.txt +1 -0
  176. gini/ui/assets/cue/ai.png +0 -0
  177. gini/ui/assets/cue/canvas.png +0 -0
  178. gini/ui/assets/cue/cloud.png +0 -0
  179. gini/ui/assets/cue/cost.png +0 -0
  180. gini/ui/assets/cue/dark/ai.png +0 -0
  181. gini/ui/assets/cue/dark/canvas.png +0 -0
  182. gini/ui/assets/cue/dark/cloud.png +0 -0
  183. gini/ui/assets/cue/dark/cost.png +0 -0
  184. gini/ui/assets/cue/dark/metrics.png +0 -0
  185. gini/ui/assets/cue/dark/router.png +0 -0
  186. gini/ui/assets/cue/dark/run.png +0 -0
  187. gini/ui/assets/cue/dark/serverless.png +0 -0
  188. gini/ui/assets/cue/dark/settings.png +0 -0
  189. gini/ui/assets/cue/dark/welcome.png +0 -0
  190. gini/ui/assets/cue/dark/wizard.png +0 -0
  191. gini/ui/assets/cue/ginibrand/ai.png +0 -0
  192. gini/ui/assets/cue/ginibrand/canvas.png +0 -0
  193. gini/ui/assets/cue/ginibrand/cloud.png +0 -0
  194. gini/ui/assets/cue/ginibrand/cost.png +0 -0
  195. gini/ui/assets/cue/ginibrand/metrics.png +0 -0
  196. gini/ui/assets/cue/ginibrand/router.png +0 -0
  197. gini/ui/assets/cue/ginibrand/run.png +0 -0
  198. gini/ui/assets/cue/ginibrand/serverless.png +0 -0
  199. gini/ui/assets/cue/ginibrand/settings.png +0 -0
  200. gini/ui/assets/cue/ginibrand/welcome.png +0 -0
  201. gini/ui/assets/cue/ginibrand/wizard.png +0 -0
  202. gini/ui/assets/cue/highcontrast/ai.png +0 -0
  203. gini/ui/assets/cue/highcontrast/canvas.png +0 -0
  204. gini/ui/assets/cue/highcontrast/cloud.png +0 -0
  205. gini/ui/assets/cue/highcontrast/cost.png +0 -0
  206. gini/ui/assets/cue/highcontrast/metrics.png +0 -0
  207. gini/ui/assets/cue/highcontrast/router.png +0 -0
  208. gini/ui/assets/cue/highcontrast/run.png +0 -0
  209. gini/ui/assets/cue/highcontrast/serverless.png +0 -0
  210. gini/ui/assets/cue/highcontrast/settings.png +0 -0
  211. gini/ui/assets/cue/highcontrast/welcome.png +0 -0
  212. gini/ui/assets/cue/highcontrast/wizard.png +0 -0
  213. gini/ui/assets/cue/light/ai.png +0 -0
  214. gini/ui/assets/cue/light/canvas.png +0 -0
  215. gini/ui/assets/cue/light/cloud.png +0 -0
  216. gini/ui/assets/cue/light/cost.png +0 -0
  217. gini/ui/assets/cue/light/metrics.png +0 -0
  218. gini/ui/assets/cue/light/router.png +0 -0
  219. gini/ui/assets/cue/light/run.png +0 -0
  220. gini/ui/assets/cue/light/serverless.png +0 -0
  221. gini/ui/assets/cue/light/settings.png +0 -0
  222. gini/ui/assets/cue/light/welcome.png +0 -0
  223. gini/ui/assets/cue/light/wizard.png +0 -0
  224. gini/ui/assets/cue/metrics.png +0 -0
  225. gini/ui/assets/cue/router.png +0 -0
  226. gini/ui/assets/cue/run.png +0 -0
  227. gini/ui/assets/cue/serverless.png +0 -0
  228. gini/ui/assets/cue/settings.png +0 -0
  229. gini/ui/assets/cue/welcome.png +0 -0
  230. gini/ui/assets/cue/wizard.png +0 -0
  231. gini/ui/assistant.py +2111 -0
  232. gini/ui/author_dialog.py +184 -0
  233. gini/ui/board_dialog.py +247 -0
  234. gini/ui/branding.py +21 -0
  235. gini/ui/canvas.py +2007 -0
  236. gini/ui/chat_panel.py +7 -0
  237. gini/ui/cpu_journey.py +212 -0
  238. gini/ui/cpu_lab.py +306 -0
  239. gini/ui/cue_cards.py +214 -0
  240. gini/ui/dashboard.py +222 -0
  241. gini/ui/diagnose_game.py +336 -0
  242. gini/ui/fingerprint_lab.py +219 -0
  243. gini/ui/flash_dialog.py +244 -0
  244. gini/ui/flow_layout.py +63 -0
  245. gini/ui/fragment_manager.py +1415 -0
  246. gini/ui/game_catalog.py +184 -0
  247. gini/ui/game_renderers.py +340 -0
  248. gini/ui/games_lab.py +90 -0
  249. gini/ui/inspector.py +1055 -0
  250. gini/ui/live_metrics.py +130 -0
  251. gini/ui/machine_lab.py +1412 -0
  252. gini/ui/main_window.py +3153 -0
  253. gini/ui/memory_lab.py +371 -0
  254. gini/ui/mission_panel.py +302 -0
  255. gini/ui/mode_indicator.py +227 -0
  256. gini/ui/palette.py +112 -0
  257. gini/ui/peripherals.py +218 -0
  258. gini/ui/process_tree.py +130 -0
  259. gini/ui/reset_dialog.py +179 -0
  260. gini/ui/router_lab.py +776 -0
  261. gini/ui/run_button.py +183 -0
  262. gini/ui/settings_dialog.py +234 -0
  263. gini/ui/signin_dialog.py +111 -0
  264. gini/ui/storage_lab.py +219 -0
  265. gini/ui/syscall_builder.py +235 -0
  266. gini/ui/syscall_lab.py +152 -0
  267. gini/ui/theme/__init__.py +5 -0
  268. gini/ui/theme/icons.py +145 -0
  269. gini/ui/theme/manager.py +291 -0
  270. gini/ui/theme/tokens.py +194 -0
  271. gini/ui/trap_lab.py +270 -0
  272. gini/ui/worker_host.py +102 -0
  273. gini/ui/zoo_lab.py +112 -0
  274. gini_toolkit-6.0.1.dev0.dist-info/METADATA +77 -0
  275. gini_toolkit-6.0.1.dev0.dist-info/RECORD +278 -0
  276. gini_toolkit-6.0.1.dev0.dist-info/WHEEL +5 -0
  277. gini_toolkit-6.0.1.dev0.dist-info/entry_points.txt +3 -0
  278. gini_toolkit-6.0.1.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,383 @@
1
+ """Concept notes — tier-3 GINI knowledge (the *how it actually works* layer).
2
+
3
+ The element catalog says *what exists* and the connection grammar says *how things
4
+ wire*; this module says *how each subsystem behaves in GINI* — the depth that lets the
5
+ Ask GINI agent answer probing questions ("why does my private DB stay reachable from the
6
+ web tier?") instead of falling back on generic training knowledge.
7
+
8
+ Pure data, Qt/compiler-free, so every layer (retrieval, tests) can share it. Each note is
9
+ compact (a paragraph or two) and student-facing. Notes are keyed to the elements and the
10
+ search terms they cover, so retrieval can pull the right ones for a question.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass, field
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class Concept:
19
+ """One subsystem explainer, with the elements and search terms it covers."""
20
+ key: str # stable slug
21
+ title: str # human title
22
+ elements: tuple[str, ...] # related device type_keys
23
+ keywords: tuple[str, ...] = field(default_factory=tuple) # retrieval synonyms
24
+ body: str = "" # the teaching text (authoritative)
25
+
26
+
27
+ CONCEPTS: tuple[Concept, ...] = (
28
+ Concept(
29
+ "networking-basics", "LANs, switching & routing",
30
+ ("host", "switch", "hub", "router", "firewall", "wap", "gini32"),
31
+ ("lan", "subnet", "gateway", "layer 2", "layer 3", "l2", "l3", "ethernet",
32
+ "collision", "broadcast", "arp", "mac", "ip", "route", "routing"),
33
+ "GINI's networking plane is a real user-space fabric, not a simulation: links are "
34
+ "Ethernet-in-UDP, each Router runs a real C gRouter, and Switches are multiplexed "
35
+ "into one `fabric` container. A Host is an end machine. A Switch (Layer 2) learns "
36
+ "MAC addresses and forwards within one subnet/broadcast domain; a Hub (Layer 1) is a "
37
+ "dumb repeater that floods every port — useful to *see* collisions and why switches "
38
+ "replaced hubs. A Router (Layer 3) forwards between subnets: each host needs the "
39
+ "router as its gateway to reach other subnets or the Internet. A Firewall filters "
40
+ "between trust zones. Addressing is automatic unless Manual Addressing is on.",
41
+ ),
42
+ Concept(
43
+ "sdn", "Software-defined networking (OpenFlow)",
44
+ ("ovs", "controller"),
45
+ ("sdn", "openflow", "flow", "flow table", "pox", "control plane", "data plane",
46
+ "software defined", "dashboard", "visualize"),
47
+ "SDN splits the control plane (decisions) from the data plane (forwarding). In GINI "
48
+ "the OpenVSwitch element is the C gRouter run in `--openflow` mode, and the Controller "
49
+ "is a POX controller speaking OpenFlow 1.0. An OVS has no built-in logic — it MUST "
50
+ "connect to a Controller, which installs flow rules reactively the first time a flow "
51
+ "appears (so the first packet triggers a rule, then the rest follow it). Wire hosts to "
52
+ "the OVS and the OVS to a Controller; build multi-switch fabrics by linking OVS to OVS. "
53
+ "To SEE what the controller installed, double-click the OVS: its Router Lab opens in "
54
+ "SDN dashboard mode showing the live OpenFlow flow table — each flow's match, action, "
55
+ "and packet/byte counters, refreshed while the lab runs. (There is no separate 'SDN "
56
+ "dashboard' element; that visualization lives on the OVS itself.)",
57
+ ),
58
+ Concept(
59
+ "nfv", "Network Function Virtualization (NFV)",
60
+ ("firewall", "cloud", "router", "vnf"),
61
+ ("nfv", "network function virtualization", "vnf", "virtual network function",
62
+ "middlebox", "network function", "appliance", "service function", "virtualize"),
63
+ "NFV runs network functions (firewall, NAT, IDS/DPI, cache, load balancer, WAN "
64
+ "optimizer) as SOFTWARE on ordinary compute instead of dedicated hardware boxes — so "
65
+ "you can deploy, move, and scale a function like any other workload. GINI realizes NFV "
66
+ "in a few ways: (1) INSIDE a router — the gRouter has an inline data-plane pipeline "
67
+ "(open the Router Lab and add modules: ACL/Firewall, NAT, rate-limit, tap/mirror, a "
68
+ "custom Lua module), each a function processing packets as they pass through; (2) as "
69
+ "STANDALONE in-path elements on the fabric — the Firewall element filters, and the "
70
+ "Internet element is a real NAT gateway; (3) the VNF element — a real container you "
71
+ "wire INLINE (pick its function in Kind: firewall/block/IDS/cache/shaper) that "
72
+ "IP-forwards between its two interfaces and applies the function (firewall and block "
73
+ "are real iptables today; IDS/cache/shaper forward-only for now). Because a VNF "
74
+ "is just a workload, the cost meter prices it, observability traces it, and Kubernetes "
75
+ "can autoscale it — that's the whole point of virtualizing the function.",
76
+ ),
77
+ Concept(
78
+ "sfc", "Service Function Chaining (SFC)",
79
+ ("firewall", "cloud", "router", "ovs", "controller"),
80
+ ("sfc", "service function chain", "service chain", "chaining", "steering",
81
+ "classifier", "traffic steering", "function chain", "chain", "service chaining"),
82
+ "SFC steers traffic through an ORDERED sequence of network functions before it reaches "
83
+ "its destination — e.g. firewall -> IDS -> NAT. A classifier decides WHICH traffic "
84
+ "enters WHICH chain (say, only web traffic through a WAF); the steering mechanism walks "
85
+ "each packet through the functions in order. GINI builds chains two ways: (1) INSIDE "
86
+ "one router — the Router Lab's ordered inline-module pipeline IS a service chain; add, "
87
+ "reorder, and step-trace a packet through firewall -> NAT -> rate-limit and watch each "
88
+ "function's verdict; (2) ACROSS the fabric — wire functions in series (host -> firewall "
89
+ "-> Internet/NAT) so the drawn path is the chain, or, for selective steering, let the "
90
+ "SDN controller install OpenFlow rules that push chosen flows through VNFs in order (the "
91
+ "steering rules then show up in the OVS flow-table dashboard). The router pipeline vs. a "
92
+ "steered container chain is the classic 'function in the box' vs. 'function as a "
93
+ "service' contrast.",
94
+ ),
95
+ Concept(
96
+ "cloud-compute", "Cloud compute: instances & containers",
97
+ ("instance", "container", "web_app", "host"),
98
+ ("vm", "virtual machine", "container", "docker", "compute", "workload", "runtime",
99
+ "image", "service discovery"),
100
+ "The cloud plane is plain Docker containers on a shared `gini` network, reachable by "
101
+ "name (real service discovery — no manual IPs). An Instance models a VM-style workload; "
102
+ "a Container models a single containerised process (both take an image/command). A Web "
103
+ "App is a ready-made HTTP service you can put behind a load balancer or proxy and back "
104
+ "with a datastore. Unlike the networking plane, these don't need routers/switches — they "
105
+ "find each other by service name on the cloud bridge.",
106
+ ),
107
+ Concept(
108
+ "serverless", "Serverless (functions & API gateway)",
109
+ ("function", "api_gateway", "object_store", "queue", "stream", "messaging"),
110
+ ("serverless", "faas", "lambda", "function", "api gateway", "cold start", "invoke",
111
+ "event driven", "stateless", "handler", "trigger"),
112
+ "Serverless = run code without managing servers; you pay per invocation and it scales "
113
+ "to zero. A Function is stateless: it holds no data between calls, so it reads/writes "
114
+ "state from a datastore or object storage. In GINI all functions run in one shared "
115
+ "`faas` runtime container; each handles requests via `handle(event, context)` and "
116
+ "returns `{statusCode, body}`. The API Gateway (a Traefik path-router) is the front "
117
+ "door — it maps a URL path to a function. Functions can also be *triggered by events*: "
118
+ "a Queue message, a Stream record, or a pub/sub message invokes the function "
119
+ "(event-driven). The first hit after idle pays a small cold-start delay; the cost meter "
120
+ "counts invocations, not idle time.",
121
+ ),
122
+ Concept(
123
+ "messaging-queue", "Queues, streams & pub/sub",
124
+ ("queue", "stream", "messaging", "function"),
125
+ ("queue", "message queue", "stream", "streaming", "pub/sub", "pubsub", "publish",
126
+ "subscribe", "async", "asynchronous", "decouple", "kafka", "rabbitmq", "nats",
127
+ "producer", "consumer", "event"),
128
+ "These decouple services so a producer doesn't wait on a consumer. A Queue (work "
129
+ "queue, RabbitMQ-style) delivers each message to one consumer — good for tasks/jobs. "
130
+ "A Stream (event log, Kafka/Redpanda-style) keeps an ordered, replayable log many "
131
+ "consumers can read independently — good for event sourcing/analytics. Messaging "
132
+ "(pub/sub, NATS-style) fans a message out to all subscribers. In GINI any of the three "
133
+ "can *trigger a Function* (event-driven serverless): drop the queue/stream/messaging "
134
+ "element next to a function and it subscribes. A producing workload (web app, "
135
+ "instance) connects to the queue to publish.",
136
+ ),
137
+ Concept(
138
+ "datastores", "Datastores: SQL, NoSQL, cache, object, block",
139
+ ("database", "nosql", "cache", "object_store", "block_volume"),
140
+ ("database", "sql", "postgres", "nosql", "mongo", "cache", "redis", "object storage",
141
+ "s3", "bucket", "block volume", "disk", "persistence", "state", "store"),
142
+ "Pick the store to fit the data. A Database is relational (Postgres) for structured, "
143
+ "queryable state. NoSQL (Mongo) is document/flexible-schema for denormalised data. A "
144
+ "Cache (Redis) is in-memory, fast, and ephemeral — put it in front of a database to "
145
+ "cut load. Object Storage (MinIO/S3-style) holds files/blobs and function code, "
146
+ "durable and cheap. A Block Volume is a persistent disk attached to one Instance (like "
147
+ "an EBS volume). Apps, functions, and pods all connect to these for their state; "
148
+ "functions are stateless so they lean on them heavily.",
149
+ ),
150
+ Concept(
151
+ "load-balancing", "Load balancing, proxies & load testing",
152
+ ("load_balancer", "proxy", "load_generator", "web_app"),
153
+ ("load balancer", "load balancing", "reverse proxy", "proxy", "nginx", "traefik",
154
+ "round robin", "least conn", "tls", "load test", "fortio", "throughput", "traffic"),
155
+ "A Load Balancer (nginx) spreads incoming traffic across several backend replicas — "
156
+ "its Scheme (round-robin / least-conn / ip-hash) is a property, and it builds its "
157
+ "backend list from the links you draw. A Reverse Proxy (Traefik) fronts a service for "
158
+ "path routing and TLS termination; you can chain a load balancer in front of a proxy. "
159
+ "A Load Generator (Fortio) fires HTTP load at a backend, a gateway, or a function so "
160
+ "you can watch throughput, latency, cost, and autoscaling react — drive it from "
161
+ "gBuilder with a live rate throttle.",
162
+ ),
163
+ Concept(
164
+ "kubernetes", "Kubernetes: clusters, pods & autoscaling",
165
+ ("k8s_cluster", "pod", "instance_group", "registry"),
166
+ ("kubernetes", "k8s", "k3s", "cluster", "pod", "deployment", "hpa", "autoscale",
167
+ "autoscaling", "replicas", "registry", "orchestration"),
168
+ "GINI runs REAL Kubernetes (a k3s container), not a mock. A K8s Cluster is the k3s "
169
+ "node; a Pod compiles to a Deployment (its replicas run your image); a Pod Autoscaler "
170
+ "(HPA — the `instance_group` element) scales a Pod's replicas on CPU. A Pod MUST live "
171
+ "in a Cluster. A private Registry serves images to the cluster. GINI generates the "
172
+ "Deployment/Service/HPA manifests and applies them with kubectl, and you can watch "
173
+ "replicas scale live. (The single-node `k8s_node` element is hidden — use Cluster + "
174
+ "Pod + Autoscaler.)",
175
+ ),
176
+ Concept(
177
+ "vpc-networking", "VPCs, subnets & public/private",
178
+ ("vpc", "cloud_subnet", "region", "gateway"),
179
+ ("vpc", "subnet", "public", "private", "isolation", "cidr", "network", "egress",
180
+ "region", "availability zone", "az", "cloud network"),
181
+ "A VPC is a real isolated Docker network with its own CIDR — services inside reach each "
182
+ "other by name, but nothing outside reaches in. Drop workloads inside the VPC box (or a "
183
+ "Subnet box) on the canvas and containment sets membership. A Subnet's Tier is the "
184
+ "teaching knob: a PUBLIC subnet's members also join a per-VPC egress bridge, so they "
185
+ "get real Internet and their consoles open from the host; a PRIVATE subnet's members "
186
+ "stay on the internal VPC fabric ONLY — no Internet, not reachable from the host, but "
187
+ "STILL reachable by other members of the same VPC (which is why a public web tier can "
188
+ "talk to a private database while the outside world can't). A Region/AZ is a label only "
189
+ "— there's no real geography on one host. A Gateway gives a VPC outbound Internet.",
190
+ ),
191
+ Concept(
192
+ "security-groups", "Security groups (stateful firewall)",
193
+ ("security_group",),
194
+ ("security group", "firewall", "default deny", "least privilege", "ingress", "iptables",
195
+ "allow", "port", "stateful", "acl"),
196
+ "A Security Group is a stateful, DEFAULT-DENY firewall you attach to the workloads or "
197
+ "datastores it protects. List inbound rules in its Ingress field, one per line: "
198
+ "`<port> from <source>`, where source is a CIDR, `anywhere`, or ANOTHER security "
199
+ "group's name. Only the listed ports open (outbound is allowed, and replies to allowed "
200
+ "traffic flow back because it's stateful). Referencing another SG by name is what makes "
201
+ "least privilege work — the classic web->app->db chain: web open to the world on 80, "
202
+ "app reachable only from web, db only from app. Under the hood each protected member "
203
+ "gets a per-member iptables sidecar sharing its network namespace, and the telemetry "
204
+ "agent is always allowed so the dashboard keeps working.",
205
+ ),
206
+ Concept(
207
+ "observability", "Observability: metrics, dashboards & tracing",
208
+ ("metrics", "dashboard", "tracing"),
209
+ ("observability", "metrics", "prometheus", "dashboard", "grafana", "tracing", "jaeger",
210
+ "monitoring", "scrape", "telemetry", "kpi", "latency"),
211
+ "Metrics (Prometheus) scrapes numeric time-series from your targets — apps, proxies, "
212
+ "load balancers, functions, the API gateway. A Dashboard (Grafana) visualises them and "
213
+ "MUST connect to a metrics source. Tracing (Jaeger) collects distributed traces across "
214
+ "services to show a request's path and where time goes. GINI also runs a `cloudfabric` "
215
+ "agent that polls each service's native metrics and feeds the gBuilder cost/Live "
216
+ "panels, so you get per-element CPU/throughput/latency without wiring everything by "
217
+ "hand.",
218
+ ),
219
+ Concept(
220
+ "cost-model", "The GINI $ cost meter",
221
+ ("region",),
222
+ ("cost", "price", "pricing", "bill", "gini dollars", "money", "budget", "meter",
223
+ "cheap", "expensive", "spend"),
224
+ "GINI shows a live 'cloud bill' so students feel the price of their design. The meter "
225
+ "sums a per-element rate x its instance size (S/M/L/XL multiply cost 1/2/4/8, matching "
226
+ "the CPU cap) x time it runs, plus usage where it applies (e.g. serverless counts "
227
+ "invocations, not idle). Rates live in `pricing.py` and are editable in Settings, so "
228
+ "you can model different providers. The dashboard breaks the bill down by category so "
229
+ "students can see what's driving cost and compare designs (e.g. always-on VMs vs. "
230
+ "scale-to-zero functions).",
231
+ ),
232
+ Concept(
233
+ "kata-isolation", "VM vs container isolation (Kata)",
234
+ ("kinstance",),
235
+ ("kata", "microvm", "vm", "isolation", "secure", "hypervisor", "sandbox",
236
+ "vm vs container"),
237
+ "The Kata Instance is a deliberately RESTRICTED element for one experiment: comparing "
238
+ "a VM-isolated workload against a plain container. It runs as a Kata microVM (real "
239
+ "hardware-virtualisation isolation, stronger than a container's shared-kernel "
240
+ "isolation) via a brokered GINI server, and it pays a longer startup than a container "
241
+ "— which is the whole teaching point (isolation vs. startup/overhead trade-off). It "
242
+ "wires only to a load source, a backend datastore/object store, and metrics — never to "
243
+ "Kubernetes, VPCs, or the networking plane, because it's meant to stay flat for the "
244
+ "comparison.",
245
+ ),
246
+ Concept(
247
+ "internet-nat", "The Internet / NAT gateway",
248
+ ("cloud",),
249
+ ("internet", "nat", "egress", "public", "outbound", "gateway", "wan", "masquerade"),
250
+ "The Internet element is a real on-fabric NAT gateway, not just a cloud icon. In "
251
+ "faithful mode it forces egress to flow through the topology you actually drew: a "
252
+ "host's default route goes out via the router(s) and firewall you placed, then "
253
+ "MASQUERADEs to the outside — so if you forgot a route or a firewall blocks it, traffic "
254
+ "really fails, which is the lesson. Wire a router or firewall to the Internet element "
255
+ "to give a LAN outbound connectivity.",
256
+ ),
257
+ Concept(
258
+ "os-scheduling", "Processes & CPU scheduling (xv6)",
259
+ ("xv6",),
260
+ ("xv6", "kernel", "operating system", "os", "process", "scheduler", "scheduling",
261
+ "context switch", "swtch", "time slice", "timeslice", "quantum", "preemption",
262
+ "preemptive", "round robin", "trap", "timer interrupt", "proc", "pcb", "runnable",
263
+ "machine lab"),
264
+ "The xv6 Machine runs a real teaching kernel (MIT 6.1810's xv6) on QEMU-RISC-V — an "
265
+ "actual OS, not a container. The Machine Lab reads its live state through QEMU's GDB "
266
+ "stub (no kernel patch needed to observe): the process table (xv6's `proc[]`: each "
267
+ "entry has a pid, a state — UNUSED/USED/SLEEPING/RUNNABLE/RUNNING/ZOMBIE — a name and a "
268
+ "parent), the CPU registers (pc, sp, ra, satp, …), and the kernel stack (unwound with "
269
+ "`bt`). A context switch happens in `swtch()`: xv6's per-CPU scheduler loop picks a "
270
+ "RUNNABLE process, switches to it, and a timer interrupt later traps back so the "
271
+ "scheduler can pick again — that is preemptive round-robin. GINI makes this visible: "
272
+ "the four panels (machine/process/memory/stack) update on each switch, a Gantt strip "
273
+ "shows which pid held the CPU over time, and you can slow the time-slice (the timer "
274
+ "reload) to ~1 s or Step one switch at a time to actually watch control move between "
275
+ "processes. The mirror of the Router Lab: there you step a packet through a pipeline; "
276
+ "here you step the CPU through context switches.",
277
+ ),
278
+ Concept(
279
+ "os-processes", "Processes, fork/exec & system calls (xv6)",
280
+ ("xv6",),
281
+ ("xv6", "process", "fork", "exec", "wait", "exit", "zombie", "pid", "system call",
282
+ "syscall", "trap", "user mode", "kernel mode", "proc", "parent", "child"),
283
+ "On the xv6 Machine a process is one entry in the kernel's `proc[]` table: a pid, a "
284
+ "state, a name, a parent, its own page table and a kernel stack. `fork()` makes a child "
285
+ "by copying the parent; `exec()` replaces a process's memory image with a program; "
286
+ "`wait()` lets a parent collect a finished child; `exit()` ends a process, which then "
287
+ "sits as a ZOMBIE until its parent wait()s for it — a zombie that lingers means the "
288
+ "parent never did, and you can see exactly that in the process table. A system call is "
289
+ "how a user program asks the kernel to act: it traps from user mode into the kernel, "
290
+ "runs the handler, and returns — visible in the CPU registers and kernel stack in the "
291
+ "Machine Lab. GINI runs a REAL xv6, so these are the actual kernel mechanics, not a "
292
+ "simulation.",
293
+ ),
294
+ Concept(
295
+ "os-memory", "Virtual memory & paging (xv6)",
296
+ ("xv6",),
297
+ ("xv6", "memory", "virtual memory", "paging", "page table", "pte", "satp", "sv39",
298
+ "page fault", "cow", "copy on write", "lazy allocation", "demand paging", "trampoline",
299
+ "trapframe", "allocator", "kalloc"),
300
+ "xv6 on RISC-V uses Sv39 three-level page tables: `satp` points at the root page table, "
301
+ "and a virtual address is split into three 9-bit indices plus a 12-bit offset. Each leaf "
302
+ "PTE maps a virtual page to a physical page with permission bits V/R/W/X/U. A process's "
303
+ "address space runs low-to-high — text, data, heap (grown by `sbrk`), a guard page, the "
304
+ "user stack — with the trapframe and the shared trampoline mapped at the very top. The "
305
+ "Memory face in the Machine Lab shows these as the address-space map, the leaf mappings "
306
+ "(VA→PA with perms), and the physical page allocator (free vs used). A page fault traps "
307
+ "into the kernel; demand/lazy allocation handles it by allocating a physical page and "
308
+ "adding a mapping — you can watch the stack grow that way. Copy-on-write fork and "
309
+ "lazy allocation are labs that add exactly this behaviour.",
310
+ ),
311
+ Concept(
312
+ "os-filesystem", "File system, buffer cache & the log (xv6)",
313
+ ("xv6",),
314
+ ("xv6", "file system", "filesystem", "inode", "dinode", "directory", "dirent", "block",
315
+ "superblock", "bitmap", "buffer cache", "bcache", "log", "journal", "journaling",
316
+ "crash", "transaction", "write ahead log", "commit"),
317
+ "xv6's disk is a fixed sequence of regions: boot | super | log | inodes | bitmap | data. "
318
+ "A file is an inode (`dinode`) holding its type, size, link count and up to 12 direct + "
319
+ "1 indirect block pointers; a directory is just a file whose data is an array of "
320
+ "`dirent {inum, name}`, which is how paths resolve. Recently-used blocks live in the "
321
+ "buffer cache (bcache) so repeated reads hit memory instead of disk. Crucially, writes "
322
+ "go through a write-ahead LOG: a system call's block writes are first recorded in the "
323
+ "log, the log is committed, and only then installed at their home locations — so a crash "
324
+ "either loses the whole transaction or applies all of it, never a half-write. The "
325
+ "Storage face shows the layout, inodes, directory tree, buffer cache, and the log "
326
+ "transaction as it fills and commits.",
327
+ ),
328
+ Concept(
329
+ "os-zoo", "The OS Zoo (historical OSes under emulation)",
330
+ ("freedos", "kolibri", "menuet", "msdos", "mac7", "win31", "oszoo_byo"),
331
+ ("os zoo", "freedos", "dos", "ms-dos", "msdos", "kolibri", "kolibrios", "menuet",
332
+ "menuetos", "assembly",
333
+ "windows", "win95",
334
+ "windows 95", "mac", "system 7", "classic", "historical", "emulator", "emulation",
335
+ "qemu", "vnc", "novnc", "basilisk", "dosbox", "vintage", "retro", "boot"),
336
+ "The OS Zoo is a play-with-real-OSes section (separate from the xv6 workbench, which is "
337
+ "about OS internals). Each Zoo element is a genuine historical operating system running "
338
+ "under emulation inside a Docker container — QEMU for x86 guests (FreeDOS, KolibriOS, "
339
+ "MenuetOS), Basilisk II for a 68k classic Mac — with its screen embedded in gBuilder over "
340
+ "noVNC (a VNC framebuffer served as a web page, shown in a QWebEngineView). Double-click an "
341
+ "element to open the Zoo Lab and use the OS live: mouse, keyboard, boot and all. GINI ships "
342
+ "only freely-redistributable OSes (FreeDOS, KolibriOS and MenuetOS boot out of the box; "
343
+ "KolibriOS and MenuetOS — tiny assembly OSes on a single floppy — are the fast ones). "
344
+ "Proprietary OSes (Windows 95, Mac "
345
+ "System 7) use the 'Classic OS (your image)' "
346
+ "element: GINI provides the emulator and points to where the image legally lives, and you "
347
+ "supply a disk image (and a Mac ROM for 68k) you own — GINI hosts nothing copyrighted. "
348
+ "Boots are ephemeral by default (a clean image each Run); turn on Persist to keep changes "
349
+ "in a qcow2 overlay. v1 is display-only; fabric networking (wiring a Zoo OS into the GINI "
350
+ "network like any machine) is v2.",
351
+ ),
352
+ )
353
+
354
+
355
+ # -- lookups ---------------------------------------------------------------- #
356
+ _BY_KEY: dict[str, Concept] = {c.key: c for c in CONCEPTS}
357
+ _BY_ELEMENT: dict[str, list[Concept]] = {}
358
+ for _c in CONCEPTS:
359
+ for _e in _c.elements:
360
+ _BY_ELEMENT.setdefault(_e, []).append(_c)
361
+
362
+
363
+ def by_key(key: str) -> Concept | None:
364
+ return _BY_KEY.get(key)
365
+
366
+
367
+ def for_element(type_key: str) -> list[Concept]:
368
+ """Concept notes that discuss this element type."""
369
+ return list(_BY_ELEMENT.get(type_key, []))
370
+
371
+
372
+ def search(terms) -> list[Concept]:
373
+ """Concepts whose title/keywords/elements match any of the given lowercase terms,
374
+ ranked by number of matches. `terms` is any iterable of strings."""
375
+ want = [t.strip().lower() for t in terms if t and t.strip()]
376
+ scored: list[tuple[int, Concept]] = []
377
+ for c in CONCEPTS:
378
+ hay = " ".join((c.key, c.title, " ".join(c.keywords), " ".join(c.elements))).lower()
379
+ score = sum(1 for t in want if t in hay)
380
+ if score:
381
+ scored.append((score, c))
382
+ scored.sort(key=lambda s: -s[0])
383
+ return [c for _, c in scored]