@underpostnet/cyberia 3.3.76 → 3.4.0

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 (132) hide show
  1. package/.env.example +1 -0
  2. package/.github/workflows/coverall.cyberia.ci.yml +17 -3
  3. package/.github/workflows/cyberia-client.cd.yml +2 -1
  4. package/.github/workflows/cyberia-server.cd.yml +2 -1
  5. package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
  6. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
  7. package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
  8. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
  9. package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
  10. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
  11. package/.github/workflows/engine-cyberia.ci.yml +7 -1
  12. package/.github/workflows/ghpkg.ci.yml +19 -2
  13. package/.github/workflows/gitlab.ci.yml +8 -6
  14. package/.github/workflows/hardhat.ci.yml +4 -1
  15. package/.github/workflows/npmpkg.ci.yml +9 -5
  16. package/.github/workflows/publish.ci.yml +21 -12
  17. package/.github/workflows/publish.cyberia.ci.yml +25 -14
  18. package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -2
  19. package/CHANGELOG.md +110 -262
  20. package/CLI-HELP.md +29 -2
  21. package/Dockerfile +1 -1
  22. package/Dockerfile.dev +1 -1
  23. package/Dockerfile.test +1 -1
  24. package/README.md +1 -1
  25. package/bin/build.js +67 -14
  26. package/bin/cyberia.js +4 -4
  27. package/bin/index.js +4 -4
  28. package/compose.env +1 -0
  29. package/conf.js +1 -1043
  30. package/deploy/cyberia-client/deploy.sh +13 -11
  31. package/deploy/cyberia-server/deploy.sh +13 -11
  32. package/deploy/dd-cyberia/sync-deploy.sh +7 -28
  33. package/deploy/lib/config.sh +1 -1
  34. package/deploy/lib/github-actions-logging.sh +3 -2
  35. package/deploy/lib/host.sh +1 -1
  36. package/deploy/pwa-microservices-template/deploy.sh +7 -1
  37. package/deployment.yaml +1 -1
  38. package/docker-compose.yml +7 -7
  39. package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
  40. package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
  41. package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
  42. package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
  43. package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
  44. package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
  45. package/docs/coverage/cyberia/base.css +362 -0
  46. package/docs/coverage/cyberia/block-navigation.js +82 -0
  47. package/docs/coverage/cyberia/favicon.png +0 -0
  48. package/docs/coverage/cyberia/index.html +161 -0
  49. package/docs/coverage/cyberia/prettify.css +101 -0
  50. package/docs/coverage/cyberia/prettify.js +937 -0
  51. package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
  52. package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
  53. package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
  54. package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
  55. package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
  56. package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
  57. package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
  58. package/docs/coverage/cyberia/sorter.js +205 -0
  59. package/hardhat/package-lock.json +6 -6
  60. package/hardhat/package.json +2 -2
  61. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  62. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
  63. package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
  64. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  65. package/package.json +19 -17
  66. package/scripts/test-monitor.sh +1 -1
  67. package/src/api/file/file.router.js +7 -1
  68. package/src/cli/baremetal.js +1 -1
  69. package/src/cli/cluster.js +73 -18
  70. package/src/cli/deploy.js +211 -30
  71. package/src/cli/docker-compose.js +1 -1
  72. package/src/cli/index.js +36 -0
  73. package/src/cli/kubectl.js +4 -3
  74. package/src/cli/release.js +5 -1
  75. package/src/cli/repository.js +14 -11
  76. package/src/cli/run.js +10 -3
  77. package/src/cli/secrets.js +393 -200
  78. package/src/cli/socketsecurity.js +260 -0
  79. package/src/client/components/core/Badge.js +0 -1
  80. package/src/client/components/core/CssCore.js +11 -1
  81. package/src/client/components/core/Modal.js +189 -66
  82. package/src/client/components/core/SearchBox.js +0 -1
  83. package/src/client/components/core/ToolTip.js +15 -0
  84. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
  85. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
  86. package/src/client/components/default/AppShellDefault.js +1 -4
  87. package/src/client/components/itemledger/AppShellItemledger.js +0 -2
  88. package/src/client/components/underpost/AppShellUnderpost.js +12 -0
  89. package/src/client/components/underpost/CssUnderpost.js +15 -2
  90. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +8 -0
  91. package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
  92. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +54 -56
  93. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  94. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +30 -29
  95. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  96. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +54 -25
  97. package/src/client/ssr/head/Css.js +12 -10
  98. package/src/client/ssr/views/Cyberia404.js +9 -13
  99. package/src/client/ssr/views/NotFound.js +75 -0
  100. package/src/client-builder/client-bundle.js +2 -2
  101. package/src/index.js +13 -1
  102. package/src/runtime/engine-cyberia/Dockerfile +1 -1
  103. package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
  104. package/src/runtime/engine-cyberia/Dockerfile.test +1 -1
  105. package/src/runtime/engine-cyberia/compose.env +1 -0
  106. package/src/runtime/engine-cyberia/docker-compose.yml +7 -7
  107. package/src/server/build/catalog.js +1 -0
  108. package/src/server/build/execution.js +19 -5
  109. package/src/server/build/package.js +166 -3
  110. package/src/server/build/testing.js +2 -1
  111. package/src/server/network/underpost-gateway.js +113 -14
  112. package/src/server/runtime/conf.js +57 -7
  113. package/src/server/runtime/config-scope.js +2 -0
  114. package/src/server/runtime/process.js +4 -3
  115. package/src/server/runtime/start.js +11 -2
  116. package/src/server/security/socketsecurity.js +1735 -0
  117. package/test/integration/infra/1-security/config-scope.test.js +3 -0
  118. package/test/integration/infra/1-security/socketsecurity.test.js +1041 -0
  119. package/test/integration/infra/1-security/sops-secret-store.test.js +201 -71
  120. package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
  121. package/test/unit/build-template.test.js +24 -0
  122. package/test/unit/catalog.test.js +13 -2
  123. package/test/unit/conf-resolution.test.js +41 -7
  124. package/test/unit/cyberia/instance-data.test.js +314 -0
  125. package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
  126. package/test/unit/deploy-log-table.test.js +38 -0
  127. package/test/unit/execution-profiles.test.js +1 -0
  128. package/test/unit/gateway-pod-fetch.test.js +95 -0
  129. package/test/unit/letsencrypt-issuer.test.js +101 -0
  130. package/test/unit/package.test.js +118 -0
  131. package/test/unit/release-bump.test.js +3 -1
  132. package/test/unit/start-options.test.js +12 -0
@@ -13,50 +13,48 @@
13
13
  Cyberia Online uses the **Fountain & Sink** economy — the industry standard for sustainable in-game economies, pioneered by _Ultima Online_ and refined in _EVE Online_ and _World of Warcraft_.
14
14
 
15
15
  ```
16
- ┌─────────────────────────────┐
17
- FOUNTAINS
18
- (inject coins into economy)
19
-
20
- botSpawnCoins ──► Bot
21
- playerSpawnCoins Player
22
- └──────────────┬──────────────┘
23
- new coins
16
+ ┌──────────────────────┐
17
+ FOUNTAINS
18
+ Supply Creation
19
+
20
+ botSpawnCoins │
21
+ playerSpawnCoins
22
+ └──────────┬───────────┘
23
+
24
24
 
25
- ┌──────────────────────────────┐
26
- CIRCULATING SUPPLY
27
- (player & bot wallets)
28
- └──────┬───────────────────────┘
29
-
30
- ┌────────────┴────────────┐
31
- KILL LOOT zero-sum drop race
32
- │ victim stake → grid drop │
33
- contributors race to collect│
34
- (players + bots, any victim)
35
-
36
- PvE: coinKillPercentVsBot
37
- PvP: coinKillPercentVsPlayer
38
- floor: coinKillMinAmount
39
- └────────────┬────────────┘
40
-
41
- ┌──────▼──────────────────────┐
42
- SINKS
43
- (destroy coins — alpha=0) │
44
- │ │
45
- respawnCostPercent │
46
- │ portalFee
47
- │ craftingFeePercent │
48
- └─────────────────────────────┘
25
+ ┌──────────────────────┐
26
+ CIRCULATING SUPPLY
27
+ Active Coin Supply
28
+ └──────────┬───────────┘
29
+
30
+ ┌────────────┴────────────┐
31
+
32
+ ▼ ▼
33
+ ┌─────────────────────┐ ┌─────────────────────┐
34
+ TRANSFERS & LOOT │ SINKS │
35
+ │ │
36
+ Supply moves ↔ │ Supply destroyed ↓│
37
+ │ │
38
+ • PvP / PvE loot │ • respawn cost │
39
+ │ • kill rewards │ │ • portal fee │
40
+ • minimum loot │ │ • crafting fee │
41
+ └─────────────────────┘ └─────────────────────┘
42
+
43
+
44
+ Circulating Supply ↓
45
+
46
+ └──────────────► Wallet Redistribution
49
47
  ```
50
48
 
51
49
  ### Core Rules
52
50
 
53
- | Rule | Description |
54
- | ----------------------------- | ----------------------------------------------------------------------------------------------- |
55
- | **Bots are infinite mint** | Every bot respawn resets wallet to `botSpawnCoins`. Supply bounded by player kill rate. |
56
- | **Players are zero-sum** | A player kill scatters the victim's coins as a grid drop but creates no new ones — redistribution only. A player who dies to bots still drops coins (per `coinKillPercentVsPlayer`); the contributing bots may reclaim them. |
57
- | **Loot is a contributor race**| Every kill scatters the victim's configured `dropItemIds` (plus coins where the entity carries them); only damage contributors may collect, and the first eligible collider wins. Damage amount is irrelevant — only contribution counts. The contributor set is tracked uniformly for **any** victim (player, bot, resource) and includes both players and the bots that dealt damage: a contributing player collects on collision, a contributing bot has the loot transferred to it on collision. A death with no contributor drops nothing. |
58
- | **Kill floor** | `coinKillMinAmount` guarantees every successful kill pays out even against a near-empty wallet. |
59
- | **Sinks scale with activity** | Fees burn a fraction — more activity = more burn, preventing indefinite inflation. |
51
+ | Rule | Description |
52
+ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | **Bots are infinite mint** | Every bot respawn resets wallet to `botSpawnCoins`. Supply bounded by player kill rate. |
54
+ | **Players are zero-sum** | A player kill scatters the victim's coins as a grid drop but creates no new ones — redistribution only. A player who dies to bots still drops coins (per `coinKillPercentVsPlayer`); the contributing bots may reclaim them. |
55
+ | **Loot is a contributor race** | Every kill scatters the victim's configured `dropItemIds` (plus coins where the entity carries them); only damage contributors may collect, and the first eligible collider wins. Damage amount is irrelevant — only contribution counts. The contributor set is tracked uniformly for **any** victim (player, bot, resource) and includes both players and the bots that dealt damage: a contributing player collects on collision, a contributing bot has the loot transferred to it on collision. A death with no contributor drops nothing. |
56
+ | **Kill floor** | `coinKillMinAmount` guarantees every successful kill pays out even against a near-empty wallet. |
57
+ | **Sinks scale with activity** | Fees burn a fraction — more activity = more burn, preventing indefinite inflation. |
60
58
 
61
59
  ---
62
60
 
@@ -123,15 +121,15 @@ All economy logic is encapsulated in a single file enforcing the single-source p
123
121
 
124
122
  ### Method Reference
125
123
 
126
- | Method | Caller | Description |
127
- | ------------------------------------- | ---------------------------------- | ---------------------------------------------------- |
128
- | `FountainInitPlayer(player)` | `handlers.go` on WebSocket connect | Credits `playerSpawnCoins` to new player |
129
- | `FountainInitBot(bot)` | `collision.go` on bot respawn | Resets bot wallet to `botSpawnCoins` (infinite mint) |
130
- | `botCoinDropAmount(bot)` | `collision.go handleBotDeath` | Coin stake a dead bot scatters as a grid drop |
131
- | `pvpCoinDropAmount(player)` | `collision.go handlePlayerDeath` | Coin stake a dead player scatters as a grid drop |
132
- | `SinkRespawnCost(player)` | `collision.go handlePlayerDeath` | Burns `respawnCostPercent`% of player coins |
133
- | `SinkPortalFee(player)` | Portal handler | Burns flat `portalFee` coins on portal use |
134
- | `SinkCraftingFee(player, item)` | Craft handler | Burns `craftingFeePercent`% on crafting |
124
+ | Method | Caller | Description |
125
+ | ------------------------------- | ---------------------------------- | ---------------------------------------------------- |
126
+ | `FountainInitPlayer(player)` | `handlers.go` on WebSocket connect | Credits `playerSpawnCoins` to new player |
127
+ | `FountainInitBot(bot)` | `collision.go` on bot respawn | Resets bot wallet to `botSpawnCoins` (infinite mint) |
128
+ | `botCoinDropAmount(bot)` | `collision.go handleBotDeath` | Coin stake a dead bot scatters as a grid drop |
129
+ | `pvpCoinDropAmount(player)` | `collision.go handlePlayerDeath` | Coin stake a dead player scatters as a grid drop |
130
+ | `SinkRespawnCost(player)` | `collision.go handlePlayerDeath` | Burns `respawnCostPercent`% of player coins |
131
+ | `SinkPortalFee(player)` | Portal handler | Burns flat `portalFee` coins on portal use |
132
+ | `SinkCraftingFee(player, item)` | Craft handler | Burns `craftingFeePercent`% on crafting |
135
133
 
136
134
  ### Kill Loot Logic
137
135
 
@@ -222,14 +220,14 @@ Offset Size Field
222
220
  15 str itemId bytes
223
221
  ```
224
222
 
225
- | `fct_type` | Constant | Color | Display | Status |
226
- | ---------- | ------------------- | ------ | -------------- | ------ |
227
- | `0x00` | `FCTTypeDamage` | Red | `-N` HP lost | active |
228
- | `0x01` | `FCTTypeRegen` | Green | `+N` HP gained | active |
229
- | `0x02` | `FCTTypeCoinGain` | Yellow | `+N` coins | retired |
230
- | `0x03` | `FCTTypeCoinLoss` | Yellow | `-N` coins | retired |
231
- | `0x04` | `FCTTypeItemGain` | Cyan | `+N ItemID` | retired |
232
- | `0x05` | `FCTTypeItemLoss` | Purple | `-N ItemID` | retired |
223
+ | `fct_type` | Constant | Color | Display | Status |
224
+ | ---------- | ----------------- | ------ | -------------- | ------- |
225
+ | `0x00` | `FCTTypeDamage` | Red | `-N` HP lost | active |
226
+ | `0x01` | `FCTTypeRegen` | Green | `+N` HP gained | active |
227
+ | `0x02` | `FCTTypeCoinGain` | Yellow | `+N` coins | retired |
228
+ | `0x03` | `FCTTypeCoinLoss` | Yellow | `-N` coins | retired |
229
+ | `0x04` | `FCTTypeItemGain` | Cyan | `+N ItemID` | retired |
230
+ | `0x05` | `FCTTypeItemLoss` | Purple | `-N ItemID` | retired |
233
231
 
234
232
  One uniform visibility rule: **every combat FCT (damage and regen, on any
235
233
  entity — player, bot, or resource) is broadcast with its exact amount to every
@@ -1,6 +1,6 @@
1
1
  # Cyberia Online — Development Roadmap
2
2
 
3
- **Current version:** 3.3.76 | **Target milestone:** Open Alpha
3
+ **Current version:** 3.4.0 | **Target milestone:** Open Alpha
4
4
 
5
5
  ---
6
6
 
@@ -23,35 +23,36 @@ The platform is the operational backbone. It should stay the source of truth for
23
23
 
24
24
  `underpost` is the shared control surface for platform operations. Top-level commands (`underpost <cmd> --help` for options):
25
25
 
26
- | Group | Command | Purpose |
27
- | ------------------ | ----------- | ------------------------------------------------------------- |
28
- | **Project / repo** | `new` | Initialize a new project, service, or configuration |
29
- | | `clone` | Clone a GitHub repository into the current directory |
30
- | | `pull` | Pull latest changes from a repository |
31
- | | `cmt` | Manage commits (commit types and options) |
32
- | | `push` | Push committed changes to a remote repository |
33
- | | `install` | Import Underpost npm dependencies by copying |
34
- | | `root` | Print the npm installation root path |
35
- | **Build / config** | `client` | Build client assets / single replicas; sync environment ports |
36
- | | `static` | Static build of pages, bundles, and documentation |
37
- | | `env` | Set env vars and config for a deploy ID |
38
- | | `config` | Manage Underpost configuration via operators |
39
- | | `metadata` | Import/export cluster metadata |
40
- | **Deploy / infra** | `cluster` | Manage Kubernetes clusters (defaults to Kind init) |
41
- | | `deploy` | Manage deployments (defaults to development pods) |
42
- | | `image` | Build, save, and load Docker images into clusters |
43
- | | `secret` | Manage secrets across platforms |
44
- | | `lxd` | Manage LXD VMs as K3s nodes (control plane / workers) |
45
- | | `baremetal` | Bare-metal provisioning workflows |
46
- | | `ip` | Show current public machine IP addresses |
47
- | **Data / ops** | `db` | Database backup / restore and related operations |
48
- | | `cron` | Run cron jobs or generate/apply K8s CronJob manifests |
49
- | | `fs` | File storage (defaults to upload) |
50
- | | `monitor` | Health-server monitoring for deployments |
51
- | | `ssh` | Manage SSH credentials and sessions for cluster nodes |
52
- | | `run` | Run scripts via named runners |
53
- | | `test` | Run the test suites |
54
- | | `release` | Release orchestrator for building and shipping CLI versions |
26
+ | Group | Command | Purpose |
27
+ | ------------------ | ---------------- | ------------------------------------------------------------- |
28
+ | **Project / repo** | `new` | Initialize a new project, service, or configuration |
29
+ | | `clone` | Clone a GitHub repository into the current directory |
30
+ | | `pull` | Pull latest changes from a repository |
31
+ | | `cmt` | Manage commits (commit types and options) |
32
+ | | `push` | Push committed changes to a remote repository |
33
+ | | `install` | Import Underpost npm dependencies by copying |
34
+ | | `root` | Print the npm installation root path |
35
+ | **Build / config** | `client` | Build client assets / single replicas; sync environment ports |
36
+ | | `static` | Static build of pages, bundles, and documentation |
37
+ | | `env` | Set env vars and config for a deploy ID |
38
+ | | `config` | Manage Underpost configuration via operators |
39
+ | | `metadata` | Import/export cluster metadata |
40
+ | **Deploy / infra** | `cluster` | Manage Kubernetes clusters (defaults to Kind init) |
41
+ | | `deploy` | Manage deployments (defaults to development pods) |
42
+ | | `image` | Build, save, and load Docker images into clusters |
43
+ | | `secret` | Manage secrets across platforms |
44
+ | | `lxd` | Manage LXD VMs as K3s nodes (control plane / workers) |
45
+ | | `baremetal` | Bare-metal provisioning workflows |
46
+ | | `ip` | Show current public machine IP addresses |
47
+ | **Data / ops** | `db` | Database backup / restore and related operations |
48
+ | | `cron` | Run cron jobs or generate/apply K8s CronJob manifests |
49
+ | | `fs` | File storage (defaults to upload) |
50
+ | | `monitor` | Health-server monitoring for deployments |
51
+ | | `ssh` | Manage SSH credentials and sessions for cluster nodes |
52
+ | | `run` | Run scripts via named runners |
53
+ | | `test` | Run the test suites |
54
+ | | `socketsecurity` | Dependency and source code security audit, Socket patches |
55
+ | | `release` | Release orchestrator for building and shipping CLI versions |
55
56
 
56
57
  Cyberia-specific operations belong in `cyberia`, not in parallel platform commands. Use the base CLI for infrastructure and delivery, then layer Cyberia workflows on top.
57
58
 
@@ -18,7 +18,7 @@ _Stackable Rendering Layers as a Unified Tokenized Reality_
18
18
 
19
19
  ---
20
20
 
21
- **Version:** 3.3.76 | **Status:** Draft | **Authors:** Underpost Engineering
21
+ **Version:** 3.4.0 | **Status:** Draft | **Authors:** Underpost Engineering
22
22
 
23
23
  ---
24
24
 
@@ -1,3 +1,6 @@
1
+ // Skeleton of the Underpost app shell as it first paints: `barMode: 'top-bottom-bar'` with a left
2
+ // `slide-menu` (closed). Keep the boxes on the 50px grid the live bars use so nothing jumps when
3
+ // the shell replaces this.
1
4
  SSRComponent = ({ backgroundImage }) => html`
2
5
  ${backgroundImage
3
6
  ? html`<style>
@@ -7,38 +10,56 @@ SSRComponent = ({ backgroundImage }) => html`
7
10
  </style>`
8
11
  : ''}
9
12
  <style>
10
- .ssr-top-bar {
13
+ body {
14
+ background: #222121;
15
+ }
16
+ .ssr-top-bar,
17
+ .ssr-bottom-bar {
11
18
  background: #121212;
12
- height: 100px;
19
+ width: 100%;
20
+ height: 50px;
21
+ left: 0px;
22
+ }
23
+ .ssr-top-bar {
24
+ top: 0px;
25
+ }
26
+ .ssr-bottom-bar {
27
+ bottom: 0px;
13
28
  }
14
29
  .ssr-btn {
30
+ top: 5px;
15
31
  height: 40px;
16
32
  width: 40px;
17
33
  }
18
- .ssr-btn-0 {
19
- top: 55px;
34
+ /* Top bar: app icon, then the search box beside it, profile on the right. */
35
+ .ssr-btn-app-icon {
36
+ left: 5px;
37
+ }
38
+ .ssr-btn-profile {
39
+ right: 5px;
40
+ border-radius: 50%;
41
+ }
42
+ .ssr-top-bar .ssr-search-box {
43
+ left: 55px;
44
+ }
45
+ /* Bottom bar: the menu hamburger on the left; left/right/home/theme/lang on the right. */
46
+ .ssr-btn-menu {
47
+ left: 5px;
48
+ }
49
+ .ssr-btn-nav-0 {
20
50
  right: 5px;
21
51
  }
22
- .ssr-btn-1 {
23
- top: 55px;
52
+ .ssr-btn-nav-1 {
24
53
  right: 55px;
25
54
  }
26
- .ssr-btn-2 {
27
- top: 55px;
55
+ .ssr-btn-nav-2 {
28
56
  right: 105px;
29
57
  }
30
- .ssr-btn-3 {
31
- top: 55px;
32
- left: 5px;
33
- }
34
- .ssr-btn-4 {
35
- top: 5px;
36
- left: 5px;
58
+ .ssr-btn-nav-3 {
59
+ right: 155px;
37
60
  }
38
- .ssr-btn-5 {
39
- top: 5px;
40
- right: 5px;
41
- border-radius: 50%;
61
+ .ssr-btn-nav-4 {
62
+ right: 205px;
42
63
  }
43
64
  .ssr-loader {
44
65
  width: auto;
@@ -72,19 +93,27 @@ SSRComponent = ({ backgroundImage }) => html`
72
93
  </style>
73
94
  <div class="ssr-background-image"></div>
74
95
  <div class="ssr-abs ssr-background" style="opacity: 1">
75
- <div class="ssr-in ssr-top-bar">
76
- ${new Array(6)
77
- .fill()
96
+ <div class="ssr-abs ssr-top-bar">
97
+ ${['app-icon', 'profile']
78
98
  .map(
79
- (v, i) =>
80
- html`<div class="ssr-abs ssr-btn ssr-btn-${i}">
99
+ (id) =>
100
+ html`<div class="ssr-abs ssr-btn ssr-btn-${id}">
81
101
  <div class="ssr-shimmer-dark"></div>
82
102
  </div>`,
83
103
  )
84
104
  .join('')}
85
-
86
105
  <div class="ssr-abs ssr-search-box"><div class="ssr-shimmer-dark-search-box"></div></div>
87
106
  </div>
107
+ <div class="ssr-abs ssr-bottom-bar">
108
+ ${['menu', 'nav-0', 'nav-1', 'nav-2', 'nav-3', 'nav-4']
109
+ .map(
110
+ (id) =>
111
+ html`<div class="ssr-abs ssr-btn ssr-btn-${id}">
112
+ <div class="ssr-shimmer-dark"></div>
113
+ </div>`,
114
+ )
115
+ .join('')}
116
+ </div>
88
117
  <div class="ssr-abs ssr-center ssr-loader"><div class="loader"></div></div>
89
118
  </div>
90
119
  `;
@@ -146,15 +146,17 @@ SSRComponent = () => html`
146
146
  background-position: 80px 0;
147
147
  }
148
148
  }
149
+ /* Dark bars (#121212): a translucent white band reads as a moving highlight on them, where
150
+ near-black stops only darkened the bar and hid the animation. */
149
151
  .ssr-shimmer-dark {
150
152
  height: 100%;
151
153
  background-image: linear-gradient(
152
154
  to right,
153
- rgb(5, 5, 5, 0.5) 0%,
154
- rgba(10, 10, 10, 0.5) 30%,
155
- rgba(15, 15, 15, 0.5) 40%,
156
- rgba(10, 10, 10, 0.5) 70%,
157
- rgba(5, 5, 5, 0.5) 100%
155
+ rgba(255, 255, 255, 0.03) 0%,
156
+ rgba(255, 255, 255, 0.07) 30%,
157
+ rgba(255, 255, 255, 0.16) 45%,
158
+ rgba(255, 255, 255, 0.07) 60%,
159
+ rgba(255, 255, 255, 0.03) 100%
158
160
  );
159
161
  animation: ssr-shimmer-dark 3s linear infinite;
160
162
  }
@@ -170,11 +172,11 @@ SSRComponent = () => html`
170
172
  height: 100%;
171
173
  background-image: linear-gradient(
172
174
  to right,
173
- rgb(5, 5, 5, 0.5) 0%,
174
- rgba(10, 10, 10, 0.5) 30%,
175
- rgba(15, 15, 15, 0.5) 40%,
176
- rgba(10, 10, 10, 0.5) 70%,
177
- rgba(5, 5, 5, 0.5) 100%
175
+ rgba(255, 255, 255, 0.03) 0%,
176
+ rgba(255, 255, 255, 0.07) 30%,
177
+ rgba(255, 255, 255, 0.16) 45%,
178
+ rgba(255, 255, 255, 0.07) 60%,
179
+ rgba(255, 255, 255, 0.03) 100%
178
180
  );
179
181
  animation: ssr-shimmer-dark-search-box 3s linear infinite;
180
182
  }
@@ -9,12 +9,16 @@
9
9
  // /TEST/<bad> → the TEST 404 (return → /TEST/)
10
10
  // /<bad> → the default 404 (return → /)
11
11
  //
12
+ // The page never touches the URL. The gateway delivers it by intercepting the
13
+ // workload's 404, so the address bar must keep the URI the client requested —
14
+ // rewriting it to `<base>/404` would present the status page as a destination.
15
+ //
12
16
  // Base path resolution (first hit wins):
13
17
  // 1. window.CYBERIA_BASE_PATH — injected by the serving layer from the
14
18
  // deployment's CYBERIA_BASE_PATH env ("/FOREST", "/TEST", "" for default).
15
19
  // Authoritative: only the runtime knows its own instance prefix, since the
16
20
  // reverse proxy strips it before the backend sees the request.
17
- // 2. Fallback: the first URL path segment, minus a trailing "/404".
21
+ // 2. Fallback: the first URL path segment (none for a bare "/404").
18
22
  //
19
23
  // Visual contract: cyberpunk-MMORPG pixel art — Press Start 2P / VT323, neon
20
24
  // cyan/magenta on near-black, hard edges, scanlines, RGB-split glitch. Fully
@@ -25,8 +29,7 @@ const s = (el) => document.querySelector(el);
25
29
  const append = (el, htmlStr) => s(el).insertAdjacentHTML('beforeend', htmlStr);
26
30
 
27
31
  // The instance sub-path this 404 belongs to ("/FOREST", "/TEST", or "" default).
28
- // Prefer the injected value; fall back to the first URL segment (dropping a
29
- // trailing "/404" so the page is idempotent once already normalised).
32
+ // Prefer the injected value; fall back to the first URL segment.
30
33
  const basePath = () => {
31
34
  const injected = window.CYBERIA_BASE_PATH;
32
35
  if (typeof injected === 'string') return injected === '/' ? '' : injected.replace(/\/$/, '');
@@ -35,7 +38,6 @@ const basePath = () => {
35
38
  };
36
39
 
37
40
  const homeHref = () => `${basePath()}/`;
38
- const notFoundHref = () => `${basePath()}/404`;
39
41
 
40
42
  // Instance label shown on the badge — the code the sub-path maps to, or MAIN.
41
43
  const instanceLabel = () => {
@@ -44,11 +46,6 @@ const instanceLabel = () => {
44
46
  };
45
47
 
46
48
  const main = () => {
47
- // Normalise the URL to <base>/404 so a bad path reads as the instance's 404
48
- // (e.g. /FOREST/nope → /FOREST/404) without a server round-trip or a loop.
49
- const target = notFoundHref();
50
- if (window.location.pathname !== target) window.history.replaceState(null, '', target + window.location.search);
51
-
52
49
  append(
53
50
  'body',
54
51
  html`
@@ -155,7 +152,7 @@ const main = () => {
155
152
  text-align: center;
156
153
  padding: 24px;
157
154
  }
158
- .badge {
155
+ .status-badge {
159
156
  display: inline-flex;
160
157
  align-items: center;
161
158
  gap: 8px;
@@ -168,7 +165,7 @@ const main = () => {
168
165
  margin-bottom: 26px;
169
166
  box-shadow: 0 0 12px rgba(34, 211, 238, 0.35);
170
167
  }
171
- .badge .dot {
168
+ .status-badge .dot {
172
169
  width: 8px;
173
170
  height: 8px;
174
171
  background: var(--green);
@@ -296,7 +293,7 @@ const main = () => {
296
293
  </style>
297
294
  <div class="grid"></div>
298
295
  <main class="wrap">
299
- <span class="badge"><span class="dot"></span> INSTANCE ${instanceLabel()}</span>
296
+ <span class="status-badge"><span class="dot"></span> INSTANCE ${instanceLabel()}</span>
300
297
  <div class="code" data-text="404">404</div>
301
298
  <div class="headline">SIGNAL LOST</div>
302
299
  <p class="sub">
@@ -322,7 +319,6 @@ SSRComponent = () =>
322
319
  const append = ${append};
323
320
  const basePath = ${basePath};
324
321
  const homeHref = ${homeHref};
325
- const notFoundHref = ${notFoundHref};
326
322
  const instanceLabel = ${instanceLabel};
327
323
  const main = ${main};
328
324
  window.onload = main;
@@ -0,0 +1,75 @@
1
+ // NotFound.js — the default 404 status page for PWA clients.
2
+ //
3
+ // Built under `status-pages/404/` and served by the gateway, which intercepts the
4
+ // runtime's bare 404 and answers with this document. The request URI is left
5
+ // exactly as the client sent it: the page renders in place and never navigates.
6
+
7
+ const s = (el) => document.querySelector(el);
8
+
9
+ const append = (el, html) => s(el).insertAdjacentHTML('beforeend', html);
10
+
11
+ const getLang = () =>
12
+ (localStorage.getItem('lang') || navigator.language || navigator.userLanguage || s('html').lang)
13
+ .slice(0, 2)
14
+ .toLowerCase();
15
+
16
+ const main = () => {
17
+ const Translate = {
18
+ Data: {
19
+ ['page-not-found']: {
20
+ en: 'Page not found',
21
+ es: 'Página no encontrada',
22
+ },
23
+ ['back']: {
24
+ en: 'Back to <br> homepage',
25
+ es: 'Volver a <br> la pagina principal',
26
+ },
27
+ },
28
+ instance: function (id) {
29
+ return this.Data[id][getLang()] ? this.Data[id][getLang()] : this.Data[id]['en'];
30
+ },
31
+ };
32
+ const icon = html`<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 24 24">
33
+ <path
34
+ fill="currentColor"
35
+ d="M15.097 5.904A6.5 6.5 0 0 0 4 10.504l.001 1h-2v-1a8.5 8.5 0 1 1 15.176 5.258l5.344 5.345l-1.414 1.414l-5.344-5.345A8.48 8.48 0 0 1 10.5 19h-1v-2h1a6.5 6.5 0 0 0 4.596-11.096M1.672 13.257L4.5 16.086l2.829-2.829l1.414 1.415L5.915 17.5l2.828 2.828l-1.414 1.415L4.5 18.914l-2.828 2.829l-1.414-1.415L3.086 17.5L.258 14.672z"
36
+ />
37
+ </svg>`;
38
+
39
+ append(
40
+ 'body',
41
+ html` <style>
42
+ body {
43
+ font-family: arial;
44
+ font-size: 20px;
45
+ background-color: #d8d8d8;
46
+ color: #333;
47
+ }
48
+ a {
49
+ color: black;
50
+ }
51
+ </style>
52
+
53
+ <div class="abs center" style="top: 45%">
54
+ ${icon}
55
+ <br />
56
+ <br />
57
+ <span class="bold">404</span>
58
+ <br />
59
+ <br />${Translate.instance('page-not-found')} <br />
60
+ <br />
61
+ <a target="_top" href="${location.origin}">${Translate.instance('back')}</a>
62
+ </div>`,
63
+ );
64
+ };
65
+
66
+ SSRComponent = () =>
67
+ html`<script>
68
+ {
69
+ const s = ${s};
70
+ const append = ${append};
71
+ const getLang = ${getLang};
72
+ const main = ${main};
73
+ window.onload = main;
74
+ }
75
+ </script>`;
@@ -320,8 +320,8 @@ const pullClientBundle = async ({ deployId, hosts = [], dev = false }) => {
320
320
 
321
321
  // The previous deployment's document root can be owned by the web server user, so the
322
322
  // replace goes through sudo; the extracted tree this process just wrote does not.
323
- shellExec(`sudo rm -rf ${publicPath}`);
324
- if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
323
+ shellExec(`sudo rm -rf ${shellArgumentFactory(publicPath)}`);
324
+ if (routePath !== '/') shellExec(`sudo mkdir -p ${shellArgumentFactory(`public/${host}`)}`);
325
325
  fs.copySync(outputPath, publicPath);
326
326
 
327
327
  logger.info('pulled bundle', { host, routePath, publicPath });
package/src/index.js CHANGED
@@ -25,6 +25,7 @@ import UnderpostPackage from './cli/package.js';
25
25
  import UnderpostRepository from './cli/repository.js';
26
26
  import UnderpostRun from './cli/run.js';
27
27
  import UnderpostSecret from './cli/secrets.js';
28
+ import UnderpostSocketSecurity from './cli/socketsecurity.js';
28
29
  import UnderpostSSH from './cli/ssh.js';
29
30
  import UnderpostState from './cli/state.js';
30
31
  import UnderpostStatic from './cli/static.js';
@@ -52,7 +53,7 @@ class Underpost {
52
53
  * @type {String}
53
54
  * @memberof Underpost
54
55
  */
55
- static version = 'v3.3.76';
56
+ static version = 'v3.4.0';
56
57
 
57
58
  /**
58
59
  * Required Node.js major version
@@ -384,6 +385,16 @@ class Underpost {
384
385
  static get package() {
385
386
  return UnderpostPackage.API;
386
387
  }
388
+
389
+ /**
390
+ * Socket supply-chain security cli API
391
+ * @static
392
+ * @type {UnderpostSocketSecurity.API}
393
+ * @memberof Underpost
394
+ */
395
+ static get socketSecurity() {
396
+ return UnderpostSocketSecurity.API;
397
+ }
387
398
  }
388
399
 
389
400
  if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
@@ -422,6 +433,7 @@ export {
422
433
  UnderpostRepository,
423
434
  UnderpostRun,
424
435
  UnderpostSecret,
436
+ UnderpostSocketSecurity,
425
437
  UnderpostSSH,
426
438
  UnderpostState,
427
439
  UnderpostDns,
@@ -6,7 +6,7 @@
6
6
  # Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
7
7
  # ---------------------------------------------------------------------------
8
8
  FROM rockylinux/rockylinux:9 AS builder
9
- ARG UNDERPOST_VERSION=3.3.76
9
+ ARG UNDERPOST_VERSION=3.4.0
10
10
  # Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
11
11
  # underpost's dependencies require >=24.15.0, so install the official binary.
12
12
  ARG NODE_VERSION=24.15.0
@@ -6,7 +6,7 @@
6
6
  # Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
7
7
  # ---------------------------------------------------------------------------
8
8
  FROM rockylinux/rockylinux:9 AS builder
9
- ARG UNDERPOST_VERSION=3.3.76
9
+ ARG UNDERPOST_VERSION=3.4.0
10
10
  # Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
11
11
  # underpost's dependencies require >=24.15.0, so install the official binary.
12
12
  ARG NODE_VERSION=24.15.0
@@ -6,7 +6,7 @@
6
6
  # Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
7
7
  # ---------------------------------------------------------------------------
8
8
  FROM rockylinux/rockylinux:9 AS builder
9
- ARG UNDERPOST_VERSION=3.3.76
9
+ ARG UNDERPOST_VERSION=3.4.0
10
10
  # Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
11
11
  # underpost's dependencies require >=24.15.0, so install the official binary.
12
12
  ARG NODE_VERSION=24.15.0
@@ -24,6 +24,7 @@ IPFS_CLUSTER_IMAGE=ipfs/ipfs-cluster:latest
24
24
  # The engine-cyberia image ships only the underpost CLI; at start it runs the
25
25
  # `underpost start --build` bootstrap, which clones ${GITHUB_USERNAME}/engine-cyberia.
26
26
  GITHUB_USERNAME=underpostnet
27
+ GITHUB_ORG_NAME=underpost
27
28
  GITHUB_TOKEN=change_me_private_repo_token
28
29
 
29
30
  # --- MongoDB (translation of Secret mongodb-secret) --------------------------