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,21 @@
1
+ id: least-privilege
2
+ layer: core
3
+ teaches: security-groups
4
+ spirit: a workload is protected by a default-deny security group that opens only the ports it needs —
5
+ least privilege by design.
6
+ summary: Protect a workload with a security group.
7
+ parent: security
8
+ provides:
9
+ - micro-segmentation
10
+ - web-endpoint
11
+ objectives:
12
+ - id: has-sg
13
+ say: Place a security group
14
+ check: exists(security_group)
15
+ - id: has-workload
16
+ say: Place a workload to protect
17
+ check: exists(web_app) or exists(database) or exists(instance)
18
+ misconceptions:
19
+ - Leaving a datastore open to the world instead of scoping ingress.
20
+ peers:
21
+ - service-chain
@@ -0,0 +1,29 @@
1
+ id: load-balanced-web
2
+ layer: core
3
+ teaches: load-balancing
4
+ spirit: incoming traffic is spread across several backend replicas via a load balancer.
5
+ summary: Front two or more web replicas with a load balancer.
6
+ parent: cloud/scaling
7
+ provides:
8
+ - load-distributor
9
+ - public-entrypoint
10
+ - web-endpoint
11
+ - metrics-source
12
+ objectives:
13
+ - id: has-lb
14
+ say: Place a load balancer
15
+ check: exists(load_balancer)
16
+ - id: enough-backs
17
+ say: Place at least two web backends
18
+ check: count(web_app) >= 2
19
+ - id: fans-out
20
+ say: Wire the load balancer to the backends
21
+ check: path(load_balancer, web_app)
22
+ - id: live-balances
23
+ say: 'Live: the load balancer really has 2+ live backends behind it (Run to check)'
24
+ kind: behavioral
25
+ probe: balances(load_balancer, >= 2)
26
+ misconceptions:
27
+ - A single backend behind the LB (nothing to balance).
28
+ peers:
29
+ - k8s-autoscale
@@ -0,0 +1,24 @@
1
+ id: observe-it
2
+ layer: observe
3
+ teaches: observability
4
+ spirit: a dashboard is fed by a metrics source so the system can be watched — the dashboard must connect
5
+ to a metrics collector.
6
+ summary: Wire a dashboard to a metrics source.
7
+ parent: observability
8
+ provides:
9
+ - visualizer
10
+ - dashboard-view
11
+ - metrics-collector
12
+ - metrics-source
13
+ objectives:
14
+ - id: has-metrics
15
+ say: Place a metrics source
16
+ check: exists(metrics)
17
+ - id: has-dash
18
+ say: Place a dashboard
19
+ check: exists(dashboard)
20
+ - id: wired
21
+ say: Wire the dashboard to the metrics source
22
+ check: path(dashboard, metrics)
23
+ misconceptions:
24
+ - A dashboard with no metrics source has nothing to show.
@@ -0,0 +1,30 @@
1
+ id: put-in-vpc
2
+ layer: core
3
+ teaches: vpc-networking
4
+ spirit: the workloads sit inside the VPC boundary — however the student draws the grouping.
5
+ summary: Place a web app and a database inside a VPC.
6
+ parent: cloud/vpc
7
+ provides:
8
+ - vpc-boundary
9
+ - web-endpoint
10
+ - relational-store
11
+ objectives:
12
+ - id: has-vpc
13
+ say: Place a VPC on the canvas
14
+ check: exists(vpc)
15
+ - id: has-web
16
+ say: Place a web app
17
+ check: exists(web_app)
18
+ - id: has-db
19
+ say: Place a managed database
20
+ check: exists(database)
21
+ - id: web-in
22
+ say: The web app sits inside the VPC
23
+ check: contains_type(vpc, web_app)
24
+ - id: db-in
25
+ say: The database sits inside the VPC
26
+ check: contains_type(vpc, database)
27
+ misconceptions:
28
+ - Leaving a workload outside the box but wired in — containment is membership.
29
+ peers:
30
+ - reachability-boundary
@@ -0,0 +1,56 @@
1
+ id: reachability-boundary
2
+ layer: core
3
+ teaches: vpc-networking
4
+ spirit: a database is reachable by the web tier but NOT from the Internet — by a private subnet, a security
5
+ group, or firewall placement; the mechanism is free.
6
+ summary: Keep a database reachable from the web tier but hidden from the Internet.
7
+ parent: cloud/vpc
8
+ provides:
9
+ - vpc-boundary
10
+ - web-endpoint
11
+ - relational-store
12
+ # Objectives form a PROGRESSIVE LADDER (the engine orders them by tier automatically):
13
+ # L1 placement -> L2 connection -> L3 containment -> L4 live.
14
+ # Independent tasks come first, so a beginner always has an obvious next move. Later tasks can be
15
+ # satisfied incidentally by earlier ones (dropping the web app INSIDE the VPC ticks both L1 and L3).
16
+ objectives:
17
+ # --- L1: place the elements -------------------------------------------------
18
+ - id: has-vpc
19
+ say: Place a VPC on the canvas
20
+ check: exists(vpc)
21
+ - id: has-web
22
+ say: Place a web app
23
+ check: exists(web_app)
24
+ - id: has-db
25
+ say: Place a managed database
26
+ check: exists(database)
27
+ # --- L2: make the connection ------------------------------------------------
28
+ - id: web-to-db
29
+ say: Connect the web app to the database
30
+ check: path(web_app, database)
31
+ # --- L3: group them correctly (containment) ---------------------------------
32
+ - id: web-in-vpc
33
+ say: The web app sits inside the VPC
34
+ check: contains_type(vpc, web_app)
35
+ - id: db-in-vpc
36
+ say: The database sits inside the VPC
37
+ check: contains_type(vpc, database)
38
+ - id: shielded
39
+ say: The database is not reachable from the Internet
40
+ # guard the negative with existence, or it passes VACUOUSLY on an empty board
41
+ check: exists(database) and not path(cloud, database)
42
+ level: 3 # isolation is an ADVANCED idea, even though its predicate reads as a connection
43
+ # --- L4: prove it live (needs the running system) ----------------------------
44
+ - id: live-reach
45
+ say: 'Live: the web app actually reaches the database (Run to check)'
46
+ kind: behavioral
47
+ probe: reach(web_app -> database) == ok
48
+ - id: live-shield
49
+ say: 'Live: the database refuses the Internet (Run to check)'
50
+ kind: behavioral
51
+ probe: reach(cloud -> database) == fail
52
+ misconceptions:
53
+ - Thinking 'private' means unreachable by everything (still reachable inside).
54
+ - Exposing the DB to the Internet to 'make it work'.
55
+ peers:
56
+ - put-in-vpc
@@ -0,0 +1,35 @@
1
+ id: sdn-reactive
2
+ layer: core
3
+ teaches: sdn
4
+ spirit: an OpenFlow switch has no built-in logic — it MUST be wired to a controller for traffic to flow;
5
+ hosts connect to the switch.
6
+ summary: Wire an OpenVSwitch to a controller, with hosts on the switch.
7
+ parent: sdn
8
+ provides:
9
+ - sdn-fabric
10
+ - flow-control
11
+ - service-endpoint
12
+ objectives:
13
+ - id: has-ovs
14
+ say: Place an OpenVSwitch
15
+ check: exists(ovs)
16
+ - id: has-ctrl
17
+ say: Place a controller
18
+ check: exists(controller)
19
+ - id: has-hosts
20
+ say: Place at least two hosts
21
+ check: count(host) >= 2
22
+ - id: ovs-controlled
23
+ say: Wire the switch to the controller
24
+ check: link(ovs, controller)
25
+ - id: hosts-on-ovs
26
+ say: Wire the hosts to the switch
27
+ check: link(host, ovs)
28
+ - id: live-flows
29
+ say: 'Live: the controller actually installed flows on the switch (Run to check)'
30
+ kind: behavioral
31
+ probe: flow_installed(ovs, any)
32
+ misconceptions:
33
+ - Expecting an OVS to forward with no controller attached (it can't).
34
+ peers:
35
+ - basic-lan
@@ -0,0 +1,19 @@
1
+ id: send-request
2
+ layer: exercise
3
+ teaches: request-path
4
+ spirit: the student sends a single request through the system and follows its path end to end — proving
5
+ the wiring actually carries traffic.
6
+ summary: Send a request through the system and trace its path.
7
+ parent: traffic
8
+ step: Add a host, send a request toward the service, and follow the path it takes.
9
+ provides:
10
+ - request-client
11
+ requires:
12
+ - traffic-sink
13
+ objectives:
14
+ - id: has-caller
15
+ say: There is a client to send a request
16
+ check: exists(host)
17
+ misconceptions:
18
+ - Assuming a drawn link works without ever sending traffic over it.
19
+ catalog: false
@@ -0,0 +1,25 @@
1
+ id: serverless-api
2
+ layer: core
3
+ teaches: serverless
4
+ spirit: an API gateway fronts a function so an HTTP request to the gateway invokes it — the gateway must
5
+ route to the function.
6
+ summary: Put a function behind an API gateway.
7
+ parent: serverless
8
+ provides:
9
+ - serverless-fn
10
+ - api-endpoint
11
+ - public-entrypoint
12
+ objectives:
13
+ - id: has-fn
14
+ say: Place a function
15
+ check: exists(function)
16
+ - id: has-gw
17
+ say: Place an API gateway
18
+ check: exists(api_gateway)
19
+ - id: routed
20
+ say: Route the gateway to the function
21
+ check: path(api_gateway, function)
22
+ misconceptions:
23
+ - A function with no gateway — nothing can invoke it over HTTP.
24
+ peers:
25
+ - decouple-with-queue
@@ -0,0 +1,33 @@
1
+ id: service-chain
2
+ layer: core
3
+ teaches: sfc
4
+ spirit: traffic passes THROUGH a network function on its way out — a firewall sits in the path between
5
+ the host and the Internet, by any valid wiring.
6
+ summary: Steer a host's traffic through a firewall to the Internet.
7
+ parent: nfv/sfc
8
+ provides:
9
+ - inline-nf
10
+ - perimeter-filter
11
+ - internet-egress
12
+ - service-endpoint
13
+ objectives:
14
+ - id: has-fw
15
+ say: Place a firewall
16
+ check: exists(firewall)
17
+ - id: has-host
18
+ say: Place a host
19
+ check: exists(host)
20
+ - id: has-net
21
+ say: Place an Internet element
22
+ check: exists(cloud)
23
+ - id: host-reaches-fw
24
+ say: The host's traffic reaches the firewall
25
+ check: path(host, firewall)
26
+ - id: through-fw
27
+ say: Traffic to the Internet passes through the firewall (no bypass)
28
+ check: through(firewall, host, cloud)
29
+ level: 3
30
+ misconceptions:
31
+ - Wiring the host straight to the Internet, bypassing the function.
32
+ peers:
33
+ - least-privilege
@@ -0,0 +1,19 @@
1
+ id: xv6-lottery-fix
2
+ layer: core
3
+ teaches: os-scheduling
4
+ spirit: the shipped lottery scheduler ignores tickets (it picks uniformly at random); a correct
5
+ shadow makes each process's CPU share track its ticket count.
6
+ summary: Make lottery scheduling give CPU share proportional to tickets.
7
+ parent: os-scheduling
8
+ objectives:
9
+ - id: shadow-wired
10
+ say: Your lottery_sched shadow is running
11
+ kind: behavioral
12
+ probe: measure(shadow_active, lottery_sched) == 1
13
+ - id: share-tracks-tickets
14
+ say: CPU share matches the ticket ratio
15
+ kind: behavioral
16
+ probe: measure(share_ratio, tickets) <= 0.15
17
+ misconceptions:
18
+ - A lottery that ignores tickets is just randomized round-robin.
19
+ - Equal ticket counts should give equal share; unequal counts should not.
@@ -0,0 +1,24 @@
1
+ id: xv6-priority-fix
2
+ layer: core
3
+ teaches: os-scheduling
4
+ spirit: the shipped priority scheduler starves low-priority processes (it favours the lowest pid on
5
+ ties and ages too weakly); a correct shadow keeps the highest-priority process served AND ages
6
+ waiting processes so none starves forever.
7
+ summary: Fix the priority scheduler so high priority wins without starving anyone.
8
+ parent: os-scheduling
9
+ objectives:
10
+ - id: shadow-wired
11
+ say: Your prio_sched shadow is running
12
+ kind: behavioral
13
+ probe: measure(shadow_active, prio_sched) == 1
14
+ - id: high-priority-served
15
+ say: The highest-priority process gets its share of the CPU
16
+ kind: behavioral
17
+ probe: measure(cpu_share, highest_priority) >= 0.40
18
+ - id: no-starvation
19
+ say: No runnable process is starved
20
+ kind: behavioral
21
+ probe: measure(max_wait_slices, any) <= 8
22
+ misconceptions:
23
+ - Strict priority with no aging starves low-priority processes forever.
24
+ - Breaking ties by pid makes the lowest pid look higher-priority than it is.
@@ -0,0 +1,111 @@
1
+ """Missions — the Wizard's objective layer.
2
+
3
+ The Wizard is "X-ray with a goal." A student states an objective in plain words ("a
4
+ multi-LAN IP network"); that becomes a **Mission**: the set of element types relevant to
5
+ the goal, plus a suggested first element. The canvas then filters X-ray to on-goal
6
+ neighbours, dims off-goal palette items, and flags off-goal drops.
7
+
8
+ This module is the deterministic, offline base (keyword → relevant types). When a local
9
+ LLM is connected the assistant refines the type set and restates the goal, but everything
10
+ works without a model — just more coarsely.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import re
15
+ from dataclasses import dataclass
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class Mission:
20
+ goal: str
21
+ types: frozenset[str] # element type_keys that belong to this objective
22
+ first: str | None = None # a good first element to drop
23
+
24
+ def allows(self, type_key: str) -> bool:
25
+ return type_key in self.types
26
+
27
+
28
+ # category -> (relevant element type_keys, anchor/first element, trigger keywords)
29
+ _CATS: tuple[tuple[str, set[str], str, tuple[str, ...]], ...] = (
30
+ ("net", {"router", "switch", "hub", "host", "firewall", "wap", "cloud"}, "router",
31
+ ("lan", "subnet", "router", "switch", "ethernet", "ip network", "ping", "route",
32
+ "vlan", "gateway", "multi-lan", "broadcast", "network topology", "campus")),
33
+ ("sdn", {"ovs", "controller", "host", "switch", "cloud"}, "controller",
34
+ ("sdn", "openflow", "flow table", "ovs", "openvswitch", "software-defined",
35
+ "software defined", "controller")),
36
+ ("k8s", {"k8s_cluster", "pod", "instance_group", "registry", "load_balancer",
37
+ "metrics", "dashboard", "load_generator"}, "k8s_cluster",
38
+ ("kubernetes", "k8s", "pod", "orchestrat", "deployment", "autoscal", "hpa",
39
+ "replica", "k3s")),
40
+ ("cloudnet", {"vpc", "cloud_subnet", "security_group", "gateway", "region",
41
+ "instance", "web_app", "container"}, "vpc",
42
+ ("vpc", "security group", "isolat", "private network", "availability zone",
43
+ "subnet within", "tenancy")),
44
+ ("web", {"web_app", "instance", "container", "load_balancer", "proxy", "gateway",
45
+ "database", "nosql", "cache", "metrics", "dashboard", "load_generator",
46
+ "registry"}, "web_app",
47
+ ("web", "http", "microservice", "api", "backend", "frontend", "website", "web app",
48
+ "web service", "three-tier", "3-tier", "scal", "load balanc")),
49
+ ("serverless", {"function", "api_gateway", "queue", "object_store", "database"},
50
+ "function",
51
+ ("serverless", "function", "faas", "lambda", "event-driven", "pub/sub", "messaging",
52
+ "queue")),
53
+ ("data", {"database", "nosql", "cache", "object_store", "block_volume"}, "database",
54
+ ("database", "sql", "postgres", "mongo", "cache", "redis", "object storage",
55
+ "bucket", "datastore", "persist")),
56
+ ("obs", {"metrics", "dashboard", "tracing"}, "metrics",
57
+ ("monitor", "observab", "metric", "dashboard", "prometheus", "grafana", "trace",
58
+ "telemetry")),
59
+ )
60
+
61
+
62
+ def _palette_keys() -> set[str]:
63
+ from .devices import by_category
64
+ return {d.key for items in by_category().values() for d in items}
65
+
66
+
67
+ # first-element preference when several categories match (most "anchoring" element wins)
68
+ _ANCHOR_PRIORITY = ("k8s_cluster", "controller", "router", "vpc", "web_app", "function",
69
+ "database", "metrics", "instance")
70
+
71
+
72
+ def _hit(keyword: str, goal: str) -> bool:
73
+ """Match a keyword on a word boundary (a leading \\b, prefixes allowed) so 'wan' no
74
+ longer matches 'want' and 'route' still matches 'router'."""
75
+ return re.search(r"\b" + re.escape(keyword), goal) is not None
76
+
77
+
78
+ def keyword_mission(goal: str) -> Mission:
79
+ """Deterministic objective → relevant element types (the offline base the LLM refines).
80
+ Unrecognized goals impose no constraint (every palette element is on-goal)."""
81
+ g = (goal or "").lower()
82
+ types: set[str] = set()
83
+ first: str | None = None
84
+ for _key, tset, anchor, kws in _CATS:
85
+ if any(_hit(kw, g) for kw in kws):
86
+ types |= tset
87
+ if first is None:
88
+ first = anchor
89
+ if not types: # nothing recognized -> don't constrain
90
+ return Mission(goal.strip(), frozenset(_palette_keys()), None)
91
+ return Mission(goal.strip(), frozenset(types), anchor_for(types) or first)
92
+
93
+
94
+ def anchor_for(types) -> str | None:
95
+ """The best 'first element to drop' for a set of relevant types."""
96
+ for k in _ANCHOR_PRIORITY:
97
+ if k in types:
98
+ return k
99
+ return None
100
+
101
+
102
+ def refine_types(raw: str) -> set[str]:
103
+ """Pull element type_keys from free LLM text (a lenient membership scan over the
104
+ registry's keys and labels) — used to refine a mission when a model is connected."""
105
+ from .devices import REGISTRY
106
+ low = (raw or "").lower()
107
+ hits: set[str] = set()
108
+ for key, dt in REGISTRY.items():
109
+ if key in low or f" {dt.label.lower()}" in f" {low}":
110
+ hits.add(key)
111
+ return hits
@@ -0,0 +1,36 @@
1
+ """Parse the gRouter's LIVE inline module chain — the deployed Service Function Chain.
2
+
3
+ The gRouter's data-plane pipeline is editable/inspectable over the rctl socket via `gpipe`
4
+ (`element_query(router, "gpipe list")`). Its `list` output (gr_control.c) looks like:
5
+
6
+ base: parse -> [0:acl] -> [1:nat] -> route -> rewrite
7
+
8
+ i.e. the fixed base (parse … route → rewrite) with the ordered inline NF modules in the
9
+ middle. This module turns that into the ordered functions actually running in the router,
10
+ so the Router Lab can show the *deployed* chain next to the *edited* one. Pure/text-only.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import re
15
+ from dataclasses import dataclass
16
+
17
+ _MOD_RE = re.compile(r"\[(\d+):([^\]]+)\]")
18
+
19
+
20
+ @dataclass
21
+ class DeployedModule:
22
+ index: int
23
+ type: str # gRouter module type: acl | nat | counter | block | lua | filter | …
24
+
25
+
26
+ def parse_chain(text: str) -> list[DeployedModule]:
27
+ """The ordered inline NF modules currently deployed in the router (from `gpipe list`)."""
28
+ return [DeployedModule(int(i), t.strip()) for i, t in _MOD_RE.findall(text or "")]
29
+
30
+
31
+ def chain_summary(text: str) -> str:
32
+ """A compact one-line view of the deployed chain, e.g. 'parse → acl → nat → route → rewrite'."""
33
+ mods = parse_chain(text)
34
+ if not mods:
35
+ return "parse → route → rewrite (no service functions deployed)"
36
+ return "parse → " + " → ".join(m.type for m in mods) + " → route → rewrite"