cyberia 3.2.12 → 3.2.70

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 (194) hide show
  1. package/.env.example +127 -68
  2. package/.github/workflows/cyberia-client.cd.yml +40 -0
  3. package/.github/workflows/cyberia-server.cd.yml +40 -0
  4. package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
  5. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
  6. package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
  7. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
  8. package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
  9. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
  10. package/.github/workflows/engine-cyberia.cd.yml +34 -24
  11. package/.github/workflows/engine-cyberia.ci.yml +27 -5
  12. package/.github/workflows/ghpkg.ci.yml +89 -1
  13. package/.github/workflows/gitlab.ci.yml +1 -1
  14. package/.github/workflows/hardhat.ci.yml +1 -1
  15. package/.github/workflows/npmpkg.ci.yml +19 -12
  16. package/.github/workflows/publish.ci.yml +2 -2
  17. package/.github/workflows/publish.cyberia.ci.yml +5 -16
  18. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
  19. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  20. package/CHANGELOG.md +451 -1
  21. package/CLI-HELP.md +1040 -1130
  22. package/Dockerfile +141 -43
  23. package/Dockerfile.dev +143 -0
  24. package/Dockerfile.test +165 -0
  25. package/README.md +1 -1
  26. package/baremetal/commission-workflows.json +1 -0
  27. package/bin/build.js +128 -136
  28. package/bin/build.template.js +25 -179
  29. package/bin/cyberia.js +1089 -188
  30. package/bin/deploy.js +6 -3
  31. package/bin/index.js +1089 -188
  32. package/bump.config.js +1 -0
  33. package/compose.env +131 -0
  34. package/conf.js +19 -1
  35. package/deployment.yaml +74 -2
  36. package/docker-compose.yml +316 -0
  37. package/hardhat/hardhat.config.js +2 -2
  38. package/hardhat/package-lock.json +620 -2041
  39. package/hardhat/package.json +7 -5
  40. package/hardhat/scripts/deployObjectLayerToken.js +18 -18
  41. package/hardhat/test/ObjectLayerToken.js +378 -274
  42. package/ipfs/configure-ipfs.sh +13 -0
  43. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  44. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  45. package/manifests/deployment/dd-cyberia-development/deployment.yaml +74 -2
  46. package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
  47. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
  48. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  49. package/mongodb/entrypoint.sh +76 -0
  50. package/nginx.conf +87 -0
  51. package/package.json +34 -22
  52. package/pv-pvc.yaml +32 -0
  53. package/scripts/disk-clean.sh +85 -60
  54. package/scripts/kubeadm-node-setup.sh +317 -0
  55. package/scripts/link-local-underpost-cli.sh +6 -0
  56. package/scripts/rocky-kickstart.sh +877 -185
  57. package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
  58. package/scripts/test-monitor.sh +248 -0
  59. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
  60. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
  61. package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
  62. package/src/api/cyberia-action/cyberia-action.model.js +21 -29
  63. package/src/api/cyberia-action/cyberia-action.router.js +42 -7
  64. package/src/api/cyberia-action/cyberia-action.service.js +20 -4
  65. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
  66. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
  67. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
  68. package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
  69. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
  70. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
  71. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
  72. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
  73. package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
  74. package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
  75. package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
  76. package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
  77. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
  78. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
  79. package/src/api/cyberia-map/cyberia-map.router.js +21 -6
  80. package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
  81. package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
  82. package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
  83. package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
  84. package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
  85. package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
  86. package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
  87. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -122
  88. package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
  89. package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
  90. package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
  91. package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
  92. package/src/api/ipfs/ipfs.service.js +28 -15
  93. package/src/api/object-layer/object-layer.router.js +25 -15
  94. package/src/api/object-layer/object-layer.service.js +15 -20
  95. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
  96. package/src/cli/baremetal.js +717 -16
  97. package/src/cli/cluster.js +80 -5
  98. package/src/cli/deploy.js +326 -292
  99. package/src/cli/docker-compose.js +648 -0
  100. package/src/cli/env.js +12 -6
  101. package/src/cli/fs.js +75 -30
  102. package/src/cli/image.js +187 -55
  103. package/src/cli/index.js +157 -6
  104. package/src/cli/kickstart.js +142 -20
  105. package/src/cli/monitor.js +387 -6
  106. package/src/cli/release.js +72 -15
  107. package/src/cli/repository.js +339 -40
  108. package/src/cli/run.js +678 -186
  109. package/src/cli/secrets.js +82 -48
  110. package/src/cli/ssh.js +234 -0
  111. package/src/cli/static.js +2 -2
  112. package/src/client/components/core/PanelForm.js +44 -44
  113. package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
  114. package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
  115. package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
  116. package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
  117. package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
  118. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
  119. package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
  120. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
  121. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
  122. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
  123. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +92 -20
  124. package/src/client/public/cyberia-docs/ARCHITECTURE.md +272 -50
  125. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
  126. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
  127. package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
  128. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
  129. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +28 -12
  130. package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
  131. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +27 -5
  132. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  133. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  134. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
  135. package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
  136. package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
  137. package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
  138. package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
  139. package/src/client/services/object-layer/object-layer.management.js +6 -6
  140. package/src/{server → client-builder}/client-build-docs.js +15 -5
  141. package/src/{server → client-builder}/client-build-live.js +3 -3
  142. package/src/{server → client-builder}/client-build.js +25 -22
  143. package/src/{server → client-builder}/client-dev-server.js +3 -3
  144. package/src/{server → client-builder}/client-icons.js +2 -2
  145. package/src/{server → client-builder}/ssr.js +5 -5
  146. package/src/client.build.js +1 -3
  147. package/src/client.dev.js +1 -1
  148. package/src/db/mongo/MongoBootstrap.js +12 -12
  149. package/src/db/mongo/MongooseDB.js +2 -1
  150. package/src/grpc/cyberia/grpc-server.js +255 -70
  151. package/src/index.js +12 -1
  152. package/src/mailer/EmailRender.js +1 -1
  153. package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
  154. package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
  155. package/src/projects/cyberia/catalog-cyberia.js +81 -0
  156. package/src/projects/cyberia/gemini-client.js +175 -0
  157. package/src/projects/cyberia/generate-saga.js +2107 -0
  158. package/src/{server → projects/cyberia}/ipfs-client.js +6 -4
  159. package/src/{server → projects/cyberia}/object-layer.js +12 -108
  160. package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
  161. package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
  162. package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
  163. package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
  164. package/src/{server → projects/cyberia}/shape-generator.js +2 -2
  165. package/src/projects/underpost/catalog-underpost.js +60 -0
  166. package/src/runtime/cyberia-client/Dockerfile +31 -83
  167. package/src/runtime/cyberia-client/Dockerfile.dev +23 -30
  168. package/src/runtime/cyberia-server/Dockerfile +23 -42
  169. package/src/runtime/cyberia-server/Dockerfile.dev +20 -35
  170. package/src/runtime/engine-cyberia/Dockerfile +143 -0
  171. package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
  172. package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
  173. package/src/runtime/engine-cyberia/compose.env +131 -0
  174. package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
  175. package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
  176. package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
  177. package/src/runtime/engine-cyberia/nginx.conf +87 -0
  178. package/src/runtime/express/Express.js +2 -2
  179. package/src/runtime/nginx/Nginx.js +250 -0
  180. package/src/runtime/wp/Dockerfile +3 -3
  181. package/src/server/catalog.js +72 -0
  182. package/src/server/conf.js +415 -60
  183. package/src/server/runtime-status.js +252 -0
  184. package/src/server/start.js +42 -11
  185. package/src/server.js +6 -2
  186. package/test/cyberia-instance-conf-defaults.test.js +140 -0
  187. package/test/deploy-monitor.test.js +267 -0
  188. package/test/shape-generator.test.js +7 -1
  189. package/typedoc.dd-cyberia.json +3 -1
  190. package/typedoc.json +3 -1
  191. package/manifests/deployment/dd-test-development/deployment.yaml +0 -256
  192. package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
  193. /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
  194. /package/src/{server → client-builder}/client-formatted.js +0 -0
@@ -235,16 +235,22 @@ All requests are CORS-simple GETs (no preflight) and cacheable. None require cre
235
235
  ```bash
236
236
  cd cyberia-client
237
237
 
238
- # Development build
239
- make -f Web.mk clean && make -f Web.mk web
238
+ # Development build (defaults: BUILD_MODE=DEBUG, localhost URLs)
239
+ make -f Web.mk clean && make -f Web.mk all
240
240
 
241
- # Release build
242
- make -f Web.mk clean && make -f Web.mk web BUILD_MODE=RELEASE
241
+ # Release build — pass the production URLs explicitly (see note below)
242
+ make -f Web.mk clean && make -f Web.mk all BUILD_MODE=RELEASE \
243
+ WS_URL=wss://server.cyberiaonline.com/ws \
244
+ API_BASE=https://www.cyberiaonline.com
243
245
 
244
- # Build + serve on dev port :8082
245
- make -f Web.mk serve-development
246
+ # Build + serve locally on dev port :8082 (DEBUG, localhost)
247
+ ./dev-server.sh # or: ./dev-server.sh <port>
246
248
  ```
247
249
 
250
+ `WS_URL` and `API_BASE` are passed straight through to the compiler — see
251
+ [Compile-time configuration](#compile-time-configuration). When omitted they
252
+ default to `localhost`, so **production build pipelines must pass real URLs**.
253
+
248
254
  The build is part of the Underpost Platform static + PWA pipeline; production deploys go through `underpost client` and `underpost deploy`.
249
255
 
250
256
  ### Output
@@ -261,22 +267,33 @@ bin/
261
267
 
262
268
  ## Compile-time configuration
263
269
 
264
- `src/config.h`:
265
-
266
- | Constant | Default | Description |
267
- | --------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
268
- | `WS_URL` | `wss://server.cyberiaonline.com/ws` | WebSocket endpoint of cyberia-server |
269
- | `API_BASE_URL` | `https://www.cyberiaonline.com` | engine-cyberia REST base URL |
270
- | `CYBERIA_CLIENT_HINTS_CODE` | `cyberia-main` | Lookup key for the optional client-hints fetch (presentation override key — never an instance/server identifier) |
271
- | `HTTP_TIMEOUT_SECONDS` | `10` | HTTP request timeout |
272
- | `MAX_TEXTURE_CACHE_SIZE` | `512` | Atlas texture LRU cap |
273
- | `MAX_LAYER_CACHE_SIZE` | `256` | ObjectLayer metadata LRU cap |
274
- | `MAX_ATLAS_CACHE_SIZE` | `256` | Atlas metadata LRU cap |
275
- | `DEFAULT_FRAME_DURATION_MS` | `100` | Default animation frame duration |
276
- | `ENABLE_DEV_UI` | `false` | Force dev overlay regardless of presentation hints |
277
- | `APP_VERSION` | `"1.0.0"` | Application version string |
278
-
279
- For local development, point `WS_URL` and `API_BASE_URL` at `localhost` before rebuilding.
270
+ ### Server URLs (build arguments)
271
+
272
+ `WS_URL` and `API_BASE` are `make` arguments baked into the WASM as
273
+ `-DWS_URL_OVERRIDE` / `-DAPI_BASE_URL_OVERRIDE`. There is **no** RELEASE/DEBUG URL
274
+ switch both default to `localhost` regardless of `BUILD_MODE`:
275
+
276
+ | make argument | Default | Baked macro |
277
+ | ------------- | ------------------------ | ----------------------- |
278
+ | `WS_URL` | `ws://localhost:8081/ws` | `WS_URL_OVERRIDE` |
279
+ | `API_BASE` | `http://localhost:4005` | `API_BASE_URL_OVERRIDE` |
280
+
281
+ > **Production pipelines must pass the URLs.** `BUILD_MODE=RELEASE` alone still
282
+ > yields `localhost`. Any release build the Docker image build, CI, manual
283
+ > release — must pass `WS_URL=wss://… API_BASE=https://…` as make arguments
284
+ > (or Docker build-args wired through to `make`), or the client ships pointing
285
+ > at localhost. If unset, `src/config.h` falls back to bare `"ws://"` /
286
+ > `"https://"` stubs.
287
+
288
+ ### Constants (`src/config.h`)
289
+
290
+ | Constant | Default | Description |
291
+ | --------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------- |
292
+ | `CYBERIA_CLIENT_HINTS_CODE` | `cyberia-main` | Lookup key for the optional client-hints fetch (presentation override key — never an instance/server identifier) |
293
+ | `MAX_TEXTURE_CACHE_SIZE` | `512` | Atlas texture LRU cap |
294
+ | `MAX_LAYER_CACHE_SIZE` | `256` | ObjectLayer metadata LRU cap |
295
+ | `MAX_ATLAS_CACHE_SIZE` | `256` | Atlas metadata LRU cap |
296
+ | `DEFAULT_FRAME_DURATION_MS` | `100` | Default animation frame duration |
280
297
 
281
298
  ---
282
299
 
@@ -0,0 +1,88 @@
1
+ # **Cyberia Lore — The Frontier of Hyperspace**
2
+
3
+ ### **Setting: Year 2520+**
4
+
5
+ Earth is a ruined monument to humanity’s first age. Civilization now spans broken continents, orbital fortresses, lunar citadels, asteroid enclaves, and distant colonies. From the ashes of collapse emerged **Cyberia**: a living frontier where the physical world and **hyperspace** (persistent, immersive Instances) have fused into one unstable civilization.
6
+
7
+ The boundary between reality and hyperspace is porous. Relics, wars, identities, and events bleed between layers, creating opportunity, conflict, and myth.
8
+
9
+ ---
10
+
11
+ ### **The Historical Fracture**
12
+
13
+ The **Pacific Cataclysm** (2045) shattered the old world through nuclear disaster, environmental collapse, plagues, and uncontrolled experiments. As Earth became hostile, humanity expanded into space while building advanced **Instances** — enduring digital worlds that evolved from tools into homes, markets, religions, and battlefields.
14
+
15
+ Two new groups emerged:
16
+
17
+ - **Mutagens**: Radiation- and bio-altered humans, resilient but often marginalized.
18
+ - **Synthetics**: Artificial beings who gained autonomy and identity.
19
+
20
+ These groups, along with baseline humans, now navigate the layered reality of Cyberia.
21
+
22
+ ---
23
+
24
+ ### **The Dual Nature of Cyberia**
25
+
26
+ Cyberia exists on two overlapping layers:
27
+
28
+ - **Physical Layer**: Harsh, resource-driven reality of fleets, colonies, infrastructure, territory, and direct force. Slow, unforgiving, dominated by logistics and raw power.
29
+ - **Hyperspace Layer**: Vast network of persistent Instances — living archives, simulated empires, memory-cities, and evolving digital ecosystems where time, geography, and identity are fluid.
30
+
31
+ The two layers constantly influence each other through neural links, relics, breaches, and megastructures. What happens in one can reshape the other.
32
+
33
+ ---
34
+
35
+ ### **The Three Great Confederations**
36
+
37
+ #### **Zenith Empire (Red)**
38
+
39
+ Militaristic, hierarchical, expansionist. Masters of the **physical layer** — fleets, fortresses, industry, and territorial control.
40
+ **Core belief**: Civilization requires imposed order through strength.
41
+ **Strength**: Force, discipline, infrastructure dominance.
42
+ **Weakness**: Rigidity, anti-Mutagen prejudice, ecological destruction, over-reliance on Nova’s systems.
43
+ **Internal split**: Iron Legion (conquest) vs. Aether Council (technocratic stability).
44
+ **Relations**: Views Atlas as chaotic and degenerate; depends on Nova for intelligence and administration while resenting the dependency.
45
+
46
+ #### **Atlas Confederation (Yellow)**
47
+
48
+ Decentralized web of frontier settlements, Mutagen clans, Synthetics, traders, scavengers, hackers, and nomads. Excels in the unstable spaces **between** layers.
49
+ **Core strength**: Adaptation, asymmetric warfare, improvisation, infiltration, and survival in chaos.
50
+ **Weakness**: Fragmentation and internal division.
51
+ **Internal split**: Harmony Enclaves (diplomacy & coexistence) vs. Void Nomads (radical independence).
52
+ **Relations**: Opposes Zenith’s rigidity and environmental harm; undermines Nova through hacking and sabotage. Thrives on black-market networks and flexible alliances.
53
+
54
+ #### **Nova Republic (Blue)**
55
+
56
+ Technocratic, intelligence-driven civilization that dominates **hyperspace**. Obsessed with optimization, prediction, cognitive architecture, and transcendence.
57
+ **Core strength**: Information control, surveillance, predictive systems, and deep integration into digital realms.
58
+ **Weakness**: Emotional detachment, underestimation of irrational human elements (faith, loyalty, culture).
59
+ **Internal split**: Singularity Directive (machine supremacy) vs. Transcendent Conclave (hybrid flesh-machine future).
60
+ **Relations**: Provides critical systems to Zenith; faces constant subversion from Atlas hackers. Sees itself as the guiding intelligence of civilization.
61
+
62
+ ---
63
+
64
+ ### **The Balance of Power**
65
+
66
+ The three confederations exist in tense, interdependent equilibrium:
67
+
68
+ - Zenith controls physical force and territory.
69
+ - Nova controls hyperspace information and architecture.
70
+ - Atlas thrives in the gaps, through mobility, sabotage, and adaptation.
71
+
72
+ They compete publicly, collaborate secretly, and betray each other routinely. None can fully dominate Cyberia — the frontier is too unstable, layered, and resistant to total control.
73
+
74
+ ---
75
+
76
+ ### **Your Role as Player**
77
+
78
+ You are not a destined hero. You are a survivor who has lived long enough to matter.
79
+
80
+ You can be:
81
+
82
+ - A soldier, scout, smuggler, hacker, relic hunter, Instance diver, mercenary, diplomat, or rogue Synthetic/Mutagen.
83
+ - Aligned with one faction, multiple, or none.
84
+ - A wanderer who moves between physical ruins and hyperspace realms.
85
+
86
+ Your power comes from what you **carry, recover, build, protect, steal, or remember**. In Cyberia, the true battle is over meaning, memory, and access across layers of reality. Your choices can shift alliances, awaken relics, destabilize systems, or forge new legends.
87
+
88
+ **Welcome to Cyberia** — the Hyper-Reality Frontier. A broken future where physical and digital realities collide, ancient scars shape new empires, and your story is still unwritten.
@@ -0,0 +1,394 @@
1
+ # Cyberia Saga — Top-Down Procedural Generation guided by LLMs
2
+
3
+ > Semantic Reverse-Engineering of a complete saga ecosystem from a single theme.
4
+
5
+ ## Concept
6
+
7
+ Classic procedural content generation is **bottom-up**: you draw maps, place
8
+ cells and entities, then layer text and logic on top of that spatial substrate.
9
+ Cyberia inverts the pipeline.
10
+
11
+ **Top-Down PCG (Semantic Reverse-Engineering)** uses an LLM to infer the entire
12
+ **semantic ontology and narrative backbone first**, from one high-level natural
13
+ language theme. We generate the lightweight, easy-to-align textual layer —
14
+ slugs, titles, descriptions, dialogues, objective requirements, item stats —
15
+ _before_ a single coordinate or texture exists. Spatial layout and graphic
16
+ synthesis become a separate, later stage that fills in a backbone that is
17
+ already internally consistent.
18
+
19
+ This is a specialized form of AI-Generated Content (AIGC). The win is
20
+ **consistency**: by resolving names, references, and progression as pure text,
21
+ the world is logically coherent before we pay the cost of spatial and visual
22
+ synthesis.
23
+
24
+ ## Architectural boundary (hard rule)
25
+
26
+ This stage owns **text and logical metadata only**.
27
+
28
+ | Owned here (textual) | Out of scope (forced to `null` / default) |
29
+ | -------------------------------- | --------------------------------------------- |
30
+ | codes, titles, descriptions | `sourceMapCode`, `sourceCellX`, `sourceCellY` |
31
+ | dialogue text, speakers, mood | `initCellX`, `initCellY`, map grid sizes |
32
+ | quest steps, objectives, rewards | asset CIDs, textures, `render` frames |
33
+ | item stats balance, item types | atlas / sprite-sheet generation |
34
+
35
+ The generator normalizes every spatial and render field to `null` regardless of
36
+ what the model returns. Downstream spatial + graphic synthesis is responsible
37
+ for binding quests/actions to cells and producing object-layer renders.
38
+
39
+ ## Pipeline
40
+
41
+ The ecosystem is generated in **six bounded, sequential stages** rather than a
42
+ single monolithic request. Each stage emits one layer and feeds canonical
43
+ (slugified) references into the next, so no single model call has to produce the
44
+ whole cross-referenced graph — this keeps each Gemini request small enough to
45
+ complete well within the request timeout.
46
+
47
+ ```
48
+ theme (from --prompt, OR auto-synthesized from CYBERIA-LORE.md)
49
+
50
+
51
+ Google Gemini (Generative Language API /v1beta/models/{model}:generateContent)
52
+ │ stage 1 foundation → saga identity + objectLayers (item ids)
53
+ │ stage 2 maps → maps (narrative zones / places)
54
+ │ stage 3 quests → quests (consume item ids + map zones)
55
+ │ stage 4 dialogues → dialogues (consume quest codes)
56
+ │ stage 5 actions → actions (consume quest + dialogue + item codes)
57
+ │ stage 6 skills → skills (bind trigger items → logic-event handlers)
58
+
59
+ normalizeSagaPayload() ── enforces text-only boundary, slugifies, resolves refs,
60
+ │ derives the CyberiaInstance shell from saga + map codes
61
+
62
+ persistSagaPayload() ── idempotent upserts into MongoDB
63
+ ```
64
+
65
+ The **instance** is not a model call: it is derived deterministically from the
66
+ saga's own metadata (`code`, `name`, `description`) plus the map codes the saga
67
+ already defined, so the saga is immediately playable as a `CyberiaInstance`.
68
+ Skill `logicEventId`s are restricted to the handlers the simulation actually
69
+ implements (`projectile`, `coin_drop_or_transaction`, `doppelganger`); unknown
70
+ ones are dropped during normalization.
71
+
72
+ ### Theme source: prompt vs. lore-grounded auto-generation
73
+
74
+ - **`--prompt` given** → the theme is used verbatim with no lore grounding
75
+ (current behavior).
76
+ - **`--prompt` omitted** → a distinct theme is auto-synthesized from the Cyberia
77
+ base lore (`src/client/public/cyberia-docs/CYBERIA-LORE.md`, override with
78
+ `--lore-path`). The whole lore document is read and passed to Gemini, and every
79
+ stage is grounded in it so the saga reads as a chapter of the canon. Variety is
80
+ driven by a random world-first **subject**, an explicit **narrative tone**, a
81
+ **faction role** (background vs. driver), a random entropy token, and a
82
+ configurable sampling **temperature** (`--temperature`, default `1.3`) — so
83
+ repeated runs surface very different, grounded Cyberia stories.
84
+
85
+ #### Subject — the world comes first
86
+
87
+ The primary diversity lever. Each auto-theme picks one **world-first subject** at
88
+ random so the output spreads across lived Cyberia reality rather than collapsing
89
+ into faction politics: daily life, frontier survival, ecology, salvage, trade,
90
+ exploration, mutagen clans, synthetics, Instance life, anomalies, small-community
91
+ crises, infrastructure, relic hunting, personal/family stories, culture, and
92
+ grounded jobs that go sideways. (Random, not customizable.)
93
+
94
+ #### Faction role — background by default, driver on request
95
+
96
+ The confederations are large powers that are **always present but rarely the
97
+ topic**. By default they stay **background only** — borders, trade influence,
98
+ security presence, taxes, historical scars — never the subject.
99
+
100
+ To make a faction the **central driver** of the saga, name one or more with
101
+ `--faction-context` (comma-separated). Valid keys:
102
+
103
+ | Key | Confederation |
104
+ | --------- | ---------------------------------------------- |
105
+ | `zenith` | the Zenith Empire (Red) |
106
+ | `nova` | the Nova Republic (Blue) |
107
+ | `atlas` | the Atlas Confederation (Yellow) |
108
+ | `neutral` | unaligned independent enclaves / neutral parties |
109
+
110
+ ```bash
111
+ # Nova + Zenith are the driving pressure of the saga
112
+ node bin/cyberia.js generate-saga --faction-context 'nova,zenith'
113
+ ```
114
+
115
+ Unknown keys warn and are skipped; if none resolve (or the flag is unset) the
116
+ saga falls back to background factions. Even when driven, the saga is still told
117
+ through the subject's people and places, not abstract galaxy-wide politics.
118
+
119
+ #### Character culture & naming
120
+
121
+ To stop the model collapsing into cliché sci-fi names (John, Nova, X-99…) and to
122
+ make Cyberia feel descended from Earth's many diasporas, a **naming &
123
+ character-culture** block is injected into every generation stage that names
124
+ people or places. It uses `CHARACTER_NAMES_POOL` as a **statistical/stylistic
125
+ prior — inspiration only, never a whitelist**: the model is told to invent fresh
126
+ names with similar linguistic/demographic characteristics (evolving, hybridizing,
127
+ or blending them with professions, slang, and technical terms), reusing an exact
128
+ sample only rarely.
129
+
130
+ Two optional flags steer it; **both default to random** when unset:
131
+
132
+ - **`--character-context <keys>`** — comma-separated pools to draw inspiration
133
+ from (unset → a random non-empty subset, so runs vary their cultural mix).
134
+ Unknown keys warn and are skipped. Valid keys:
135
+
136
+ | Key | Flavor |
137
+ | --- | ------ |
138
+ | `low_level_synthetics` | serialized / technical utility-frame designations |
139
+ | `high_fidelity_synthetics` | refined, near-human synthetic names |
140
+ | `global_latin_diaspora` | Latin / Iberian-descended |
141
+ | `east_asian_pacific_diaspora` | East-Asian / Pacific-descended |
142
+ | `middle_eastern_turkish_diaspora` | Middle-Eastern / Turkish-descended |
143
+ | `sub_saharan_african_diaspora` | Sub-Saharan African-descended |
144
+ | `classic_western_scifi` | gritty Anglo operators / enforcers |
145
+ | `mutagen_clans` | organic, mutation-flavored clan names |
146
+
147
+ - **`--cultural-exposure <mode>`** — how mixed the population is (unset → random):
148
+
149
+ | Mode | Effect |
150
+ | ---- | ------ |
151
+ | `cosmopolitan` | heavy mixing, hybrid surnames, intermarriage — maximizes variety |
152
+ | `local` | isolated clans/enclaves, strong local naming traditions, internally consistent |
153
+
154
+ ```bash
155
+ # Latin + East-Asian diaspora names in a highly mixed, cosmopolitan population
156
+ node bin/cyberia.js generate-saga \
157
+ --character-context 'global_latin_diaspora,east_asian_pacific_diaspora' \
158
+ --cultural-exposure cosmopolitan
159
+
160
+ # An isolated mutagen-clan enclave with consistent internal naming
161
+ node bin/cyberia.js generate-saga --character-context 'mutagen_clans' --cultural-exposure local
162
+ ```
163
+
164
+ #### Narrative tone
165
+
166
+ The theme commits to one of four broad narrative types — chosen **uniformly
167
+ (~25% each)** unless forced with `--tone`:
168
+
169
+ | Tone | Register |
170
+ | ----------- | -------------------------------------------------------------- |
171
+ | `adventure` | noir high-risk missions: covert ops, sabotage, combat, rogue AI, mystery |
172
+ | `politics` | power & ideology at any scale — councils, clans, enclaves, revolutions (confederation geopolitics only sometimes) |
173
+ | `tragic` | heartbreaking, intimate: family, bonds, loss, grief |
174
+ | `comedy` | everyday absurdity and silliness, played for humor |
175
+
176
+ #### Spatial context (physical vs. hyperspace)
177
+
178
+ Cyberia has two equally important layers — the **Physical Layer** (fleets,
179
+ colonies, logistics, force) and the **Hyperspace Layer** (persistent Instances,
180
+ memory-cities, digital ecosystems). Because the lore is titled *The Frontier of
181
+ Hyperspace*, an unconstrained model drifts to hyperspace-only premises. So the
182
+ auto-theme picks a spatial context **explicitly and uniformly (~33.3% each)**:
183
+
184
+ | Context | Premise lives in… |
185
+ | ------------ | -------------------------------------------------------------- |
186
+ | `physical` | the material frontier only — no hyperspace |
187
+ | `mixed` | the porous interplay where events bleed between both layers |
188
+ | `hyperspace` | inside the Instances only — not the physical frontier |
189
+
190
+ Force one with `--space-context <physical\|mixed\|hyperspace>`; an invalid value
191
+ warns and falls back to random. This applies to auto-generation only (with
192
+ `--prompt`, you control the setting yourself).
193
+
194
+ > Transient `generateContent` failures (timeouts, 5xx, malformed JSON) are
195
+ > retried automatically with backoff (`maxRetries`, default 2) per stage.
196
+
197
+ Source:
198
+
199
+ - `src/projects/cyberia/gemini-client.js` — thin Gemini `generateContent` JSON client.
200
+ - `src/projects/cyberia/generate-saga.js` — system prompt, normalization, persistence.
201
+ - `bin/cyberia.js` — `generate-saga` CLI command wiring.
202
+
203
+ ## Usage
204
+
205
+ ### Lore-based auto-generation (no `--prompt`)
206
+
207
+ With no `--prompt`, the theme is auto-synthesized from the Cyberia base lore.
208
+ Omitting the steering flags lets the generator pick a random faction, tone and
209
+ spatial context each run, so the same command keeps producing different sagas:
210
+
211
+ ```bash
212
+ # Fully random, lore-grounded saga (faction + tone + spatial context all random)
213
+ node bin/cyberia.js generate-saga
214
+
215
+ # Force a heartbreaking, character-driven story grounded in physical reality
216
+ node bin/cyberia.js generate-saga --tone tragic --space-context physical
217
+
218
+ # A political saga set inside hyperspace Instances
219
+ node bin/cyberia.js generate-saga --tone politics --space-context hyperspace
220
+
221
+ # A light comedy that spans both layers, cranked up for more divergence
222
+ node bin/cyberia.js generate-saga --tone comedy --space-context mixed --temperature 1.7
223
+
224
+ # A noir adventure, kept tightly on-theme with a low temperature
225
+ node bin/cyberia.js generate-saga --tone adventure --temperature 0.6
226
+
227
+ # Steer only the spatial layer; let subject + tone stay random
228
+ node bin/cyberia.js generate-saga --space-context physical
229
+
230
+ # Make Nova + Zenith the driving pressure (factions as the main theme)
231
+ node bin/cyberia.js generate-saga --faction-context 'nova,zenith'
232
+
233
+ # Steer character names toward specific diasporas and a cosmopolitan, mixed population
234
+ node bin/cyberia.js generate-saga \
235
+ --character-context 'global_latin_diaspora,sub_saharan_african_diaspora' --cultural-exposure cosmopolitan
236
+
237
+ # Dry run (no DB writes) and capture the payload to inspect it
238
+ node bin/cyberia.js generate-saga --tone tragic --dry-run --out ./engine-private/cyberia-sagas/preview.json
239
+ ```
240
+
241
+ Each run logs the chosen facets, e.g.
242
+ `Theme: subject="..." | context=physical | tone=tragic | factions=background`,
243
+ then `Auto-generated theme: "..."`.
244
+
245
+ ### Generate from an explicit theme
246
+
247
+ ```bash
248
+ node bin/cyberia.js generate-saga \
249
+ --prompt "A rebel hacker base hidden in the sewers of Santiago"
250
+ ```
251
+
252
+ Either way, when `--out` is omitted the payload is written to
253
+ `./engine-private/cyberia-sagas/<saga-code>.json`.
254
+
255
+ Import a previously generated payload (no model call):
256
+
257
+ ```bash
258
+ node bin/cyberia.js generate-saga --import ./engine-private/cyberia-sagas/<saga-code>.json
259
+ ```
260
+
261
+ `--import` reads the same JSON shape `--out` writes and loads it through the
262
+ **same** normalize → persist path as generation. Re-running is safe: documents
263
+ are upserted by `code` (dialogues by `code` + `order`), so existing entries are
264
+ overwritten and codes are never duplicated.
265
+
266
+ Options:
267
+
268
+ | Flag | Description |
269
+ | -------------------------- | ---------------------------------------------------- |
270
+ | `--prompt <theme>` | Theme seed. Omit to auto-generate from the lore. |
271
+ | `--import <file>` | Load a generated payload file into the DB. |
272
+ | `--lore-path <path>` | Override the base-lore doc (auto-generate mode). |
273
+ | `--space-context <ctx>` | Force `physical` \| `mixed` \| `hyperspace` (else random). |
274
+ | `--tone <tone>` | Force `adventure` \| `politics` \| `tragic` \| `comedy` (else random). |
275
+ | `--faction-context <keys>` | Comma-separated `zenith` \| `nova` \| `atlas` \| `neutral` that DRIVE the theme (else background). |
276
+ | `--character-context <keys>` | Comma-separated `CHARACTER_NAMES_POOL` keys for name inspiration (else random subset). |
277
+ | `--cultural-exposure <mode>` | `cosmopolitan` \| `local` naming-diversity mode (else random). |
278
+ | `--temperature <value>` | Sampling temperature for every model call (default `1.3`). |
279
+ | `--model <id>` | Gemini model id (default `gemma-4-26b-a4b-it`). |
280
+ | `--timeout <ms>` | Per-request timeout in ms (default `300000`). |
281
+ | `--thinking-level <level>` | `low` \| `medium` \| `high` (default `high`). |
282
+ | `--out <file>` | Payload dump path (default `./engine-private/cyberia-sagas/<saga-code>.json`). |
283
+ | `--dry-run` | Normalize only; no database writes. |
284
+ | `--env-path <path>` | Env file to load (`GEMINI_API_KEY`, deploy vars). |
285
+ | `--mongo-host <host>` | Mongo host override. |
286
+ | `--dev` | Force the development environment. |
287
+
288
+ `GEMINI_API_KEY` must be available in the environment or the `--env-path` file.
289
+
290
+ ## Output schema
291
+
292
+ A single JSON object with these interrelated sections:
293
+
294
+ - **saga** — `code`, `name`, `description`, `mapCodes[]`, `itemIds[]`, `questCodes[]`.
295
+ - **instance** — the playable `CyberiaInstance` shell derived from the saga
296
+ metadata: `code`, `name`, `description`, `tags[]`, `cyberiaMapCodes[]` (the
297
+ saga's map codes), `itemIds[]` (`{ id, defaultPlayerInventory }`),
298
+ `topologyMode`. Spatial topology (`portals`) and the tuning ref (`conf`) are
299
+ left empty here and bound by downstream spatial synthesis.
300
+ - **maps[]** — narrative zones the quest chain visits: `code`, `name`,
301
+ `description` (text only — grid/cells/entities stay at schema defaults). Their
302
+ codes populate `saga.mapCodes` and `instance.cyberiaMapCodes`.
303
+ - **quests[]** — textual objectives, titles, linear `steps[]` with
304
+ `collect | talk | kill` objectives and `rewards[]`. Spatial fields `null`.
305
+ - **dialogues[]** — narrative nodes: `code`, `order`, `speaker`, `text`, `mood`.
306
+ - **actions[]** — interaction nodes: `dialogCode`, `questDialogueCodes[]`,
307
+ `shopItems[]` pricing, and `craftRecipes[]`. Spatial fields `null`.
308
+ - **skills[]** — `triggerItemId` → logic skills: `logicEventIds[]` (derived) and
309
+ `skills[]` (`logicEventId`, `name`, `description`, `summonedEntityItemId`). The
310
+ trigger and every non-placeholder summoned id resolve to an
311
+ `objectLayers[].item.id`.
312
+ - **objectLayers[]** — item data with balanced `stats`, an `item`
313
+ (`id`, `type`, `description`, `activable`), and `render: null`.
314
+
315
+ Object-layer item shape:
316
+
317
+ ```json
318
+ {
319
+ "stats": { "effect": 4, "resistance": 4, "agility": 3, "range": 10, "intelligence": 5, "utility": 10 },
320
+ "item": { "id": "generated-id", "type": "skin", "description": "Lore-matching description", "activable": true },
321
+ "render": null
322
+ }
323
+ ```
324
+
325
+ ## Referential integrity
326
+
327
+ The system prompt requires the model to keep cross-references resolvable, and
328
+ normalization slugifies every code/id so they line up:
329
+
330
+ - quest objective/reward `itemId`, shop `itemId`, and craft `itemId` resolve to
331
+ an `objectLayers[].item.id`;
332
+ - action `questDialogueCodes[].questCode` resolves to a `quests[].code`;
333
+ - action `dialogCode` / `questDialogueCodes[].dialogCode` resolve to a
334
+ `dialogues[].code`;
335
+ - skill `triggerItemId` resolves to an `objectLayers[].item.id` (skills whose
336
+ trigger is unknown are dropped), and each non-placeholder `summonedEntityItemId`
337
+ resolves to one too — a deterministic repair (`ensureSkillLinkage`) appends a
338
+ minimal `skill`-type item for any summoned id the model omitted;
339
+ - `saga.questCodes` / `saga.itemIds` are reconciled to include every generated
340
+ quest and item (including appended skill items), and `instance.cyberiaMapCodes`
341
+ / `instance.itemIds` mirror the saga's.
342
+
343
+ ### `talk` objectives (guaranteed fulfillable)
344
+
345
+ A `talk` objective completes **only** when the player views the `dialogCode` an
346
+ action maps for that quest, on the NPC bot whose skin matches the objective's
347
+ `itemId` (the bot skin derives from the action's `default-<skin>` greeting). A
348
+ missing link means the objective can never be completed.
349
+
350
+ So a talk objective requires four aligned pieces: a `skin` item for the NPC, the
351
+ `talk` objective referencing that skin id, a talk dialogue group, and an action
352
+ with `dialogCode: default-<skin>` plus a `questDialogueCodes` entry
353
+ `{ questCode, dialogCode }`. The prompts ask the model to produce all four (the
354
+ quests/dialogues/actions stages are told the `talkTargets`), and a deterministic
355
+ **repair pass** (`ensureTalkLinkage`) then guarantees it: for every talk
356
+ objective it creates any missing skin item, talk dialogue
357
+ (`quest-talk-<questCode>`), or NPC action mapping, and rewrites a
358
+ `questDialogueCodes` entry that points at a non-existent dialogue. Reruns where
359
+ the model already produced valid links are a no-op.
360
+
361
+ ## Persistence
362
+
363
+ Documents are written sequentially with idempotent upserts (rerunnable):
364
+
365
+ | Section | Model | Upsert key |
366
+ | ------------- | ---------------------- | ---------------- |
367
+ | saga | `CyberiaSagaModel` | `code` |
368
+ | instance | `CyberiaInstanceModel` | `code` |
369
+ | maps | `CyberiaMapModel` | `code` |
370
+ | quests | `CyberiaQuestModel` | `code` |
371
+ | dialogues | `CyberiaDialogueModel` | `code` + `order` |
372
+ | actions | `CyberiaActionModel` | `code` |
373
+ | skills | `CyberiaSkillModel` | `triggerItemId` |
374
+ | object layers | `ObjectLayerModel` | `data.item.id` |
375
+
376
+ The **instance** refreshes its textual/logical fields every run but PRESERVES
377
+ `portals` and `conf` (set only on insert), so downstream spatial topology is
378
+ never clobbered — the same preservation contract `ObjectLayer` render/ledger
379
+ uses. **Skills** are upserted by `triggerItemId` into the `CyberiaSkill`
380
+ collection — the authoritative own-model source the gRPC server reads.
381
+
382
+ Both `generate` and `import` go through the same persistence path. Object-layer
383
+ items are written as real `ObjectLayer` documents so they are immediately
384
+ editable in `src/client/components/cyberia/ObjectLayerEngineViewer.js`:
385
+
386
+ - `data.render` is empty (`cid: null`, `metadataCid: null`) and the top-level
387
+ `cid` is `null` — there is no atlas yet;
388
+ - `data.ledger.type` is `OFF_CHAIN` (no on-chain token minted);
389
+ - `sha256` is computed over `data` with the canonical `computeSha256` helper.
390
+
391
+ The render and ledger are the **graphic/economic synthesis** stage and are set
392
+ later from the viewer. Re-running generate/import refreshes the textual
393
+ stats/item fields but **preserves any render or ledger already set** — it never
394
+ clobbers a populated render back to `null`. `saga.itemIds` records every item id.
@@ -12,7 +12,7 @@
12
12
 
13
13
  `cyberia-server` is the authoritative simulation runtime for the Cyberia MMO extension on Underpost Platform. It owns world state, advances a fixed-rate tick, drains typed input commands from connected clients, and dispatches AOI-filtered snapshots on a separately-paced replication tick.
14
14
 
15
- It is **not** the content authority. World content is loaded from `engine-cyberia` over gRPC. It is **not** the render-policy authority. Presentation is owned by `cyberia-client`.
15
+ It is **not** the content authority. World content is loaded once at boot from `engine-cyberia` over gRPC. It is **not** the render-policy authority. Presentation is owned by `cyberia-client`.
16
16
 
17
17
  ---
18
18
 
@@ -20,23 +20,32 @@ It is **not** the content authority. World content is loaded from `engine-cyberi
20
20
 
21
21
  Three independent processes, non-overlapping roles. The ecosystem is playable only when all three are running and healthy at the same time.
22
22
 
23
- ```text
24
- engine-cyberia (Node.js) cyberia-server (Go) cyberia-client (C/WASM)
25
- content authority authoritative simulation presentation runtime
26
- gRPC + REST tick + AOI + snapshots render + prediction
27
-
28
- └── gRPC GetFullInstance ──► └── WebSocket binary ──►
29
- world config snapshots -> / input <-
23
+ ```
24
+ engine-cyberia (Node.js) cyberia-server (Go) cyberia-client (C/WASM)
25
+ ───────────────────────── ────────────────── ──────────────────────
26
+ content authority authoritative simulation presentation runtime
27
+ persisted maps + rules tick + AOI + snapshots render + prediction
28
+
29
+ │ gRPC GetFullInstance │ WebSocket binary
30
+ │────────────────────────────────────► │ AOI snapshots + init
31
+ │ (world configuration: │ ▲
32
+ │ AOI radius, economy, │ │ typed input commands
33
+ │ skill, equipment, │ │
34
+ │ entity gameplay defaults) │ │
35
+ │ │ │
36
+ ▼ ▼ ▼
37
+ (boot + hot reload) tick loop + replication
30
38
  ```
31
39
 
32
40
  - Each service is supervised independently and owns its own monitor and reconnector.
33
- - `cyberia-server` loads world configuration from `engine-cyberia` and does not fabricate a world.
41
+ - `cyberia-server` dials `engine-cyberia` gRPC at boot and exits on dial failure rather than fabricate a world.
42
+ - On reconnect, world configuration is reloaded via `GetFullInstance(instanceCode)`.
34
43
  - If any one of the three services is unhealthy, the game moves to standby until all three recover.
35
44
 
36
45
  The server speaks two protocols:
37
46
 
38
- - **gRPC inbound** to consume world configuration from `engine-cyberia`
39
- - **WebSocket binary** to deliver snapshots and accept typed input commands
47
+ - **gRPC, inbound, at boot and hot reload:** consumes world configuration from `engine-cyberia`.
48
+ - **WebSocket binary, ongoing:** delivers AOI snapshots to clients, accepts typed input commands.
40
49
 
41
50
  There is no per-tick traffic between `cyberia-server` and `engine-cyberia`, and no presentation authority in the Go runtime.
42
51
 
@@ -257,7 +266,8 @@ All content data (ObjectLayer metadata, asset blobs, optional client hints) is s
257
266
  | --------------------------------- | ----------------- | -------------------------------------------------- |
258
267
  | `ENGINE_GRPC_ADDRESS` | `localhost:50051` | engine-cyberia gRPC address (**required**) |
259
268
  | `INSTANCE_CODE` | `default` | Instance code to load on startup |
260
- | `ENGINE_API_BASE_URL` | _(empty)_ | engine-cyberia REST base URL; forwarded to clients |
269
+ | `ENGINE_API_BASE_URL` | _(empty)_ | Internal engine-cyberia origin (server→engine only) |
270
+ | `ENGINE_PUBLIC_URL` | _(empty)_ | Client-visible Content Authority origin (forwarded to clients) |
261
271
  | `ENGINE_GRPC_RELOAD_INTERVAL_SEC` | _(disabled)_ | ObjectLayer hot-reload polling interval |
262
272
  | `SERVER_PORT` | `8081` | WebSocket + HTTP listen port |
263
273
  | `STATIC_DIR` | `./public` | Directory for static WASM client files |
@@ -276,3 +286,9 @@ go run main.go
276
286
  go build -o cyberia-server .
277
287
  ./cyberia-server
278
288
  ```
289
+
290
+ ### Test logx
291
+
292
+ ```bash
293
+ go test ./logx/ -run TestResolveLevel
294
+ ```
@@ -224,7 +224,7 @@ cyberia chain register / mint / transfer / burn / pause / unpause
224
224
 
225
225
  # Named workflows
226
226
  cyberia run-workflow import-default-items
227
- cyberia run-workflow seed-skill-config --instance-code default
227
+ cyberia run-workflow seed-skills
228
228
  cyberia run-workflow seed-dialogues
229
229
  cyberia run-workflow build-manifest
230
230
  cyberia run-workflow build-server-dashboard