@omega.js/desktop 0.50.0 → 0.52.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 (268) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/css/tokens/_index.scss +1 -1
  3. package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +4 -1
  4. package/dist/assets/themes/base/_includes/frontend/sections/footer.html +13 -6
  5. package/dist/assets/themes/base/_includes/frontend/sections/nav.html +10 -8
  6. package/dist/assets/themes/base/_includes/global/sections/account.html +3 -1
  7. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +12 -8
  8. package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +10 -6
  9. package/dist/assets/themes/base/_includes/global/sections/page-header.html +8 -4
  10. package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +24 -24
  11. package/dist/assets/themes/base/_layouts/frontend/pages/about.html +10 -10
  12. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +35 -35
  13. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +4 -4
  14. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +1 -1
  15. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +4 -4
  16. package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +3 -3
  17. package/dist/assets/themes/base/_layouts/frontend/pages/download.html +34 -33
  18. package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +11 -11
  19. package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +1 -1
  20. package/dist/assets/themes/base/_layouts/frontend/pages/status.html +1 -1
  21. package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +9 -7
  22. package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +5 -3
  23. package/dist/assets/themes/base/_sections/about/letter/section.html +1 -1
  24. package/dist/assets/themes/base/_sections/about/letter/section.json5 +4 -4
  25. package/dist/assets/themes/base/_sections/marketing/bento/section.html +1 -1
  26. package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +15 -15
  27. package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +1 -1
  28. package/dist/assets/themes/base/_sections/marketing/hero/section.html +6 -6
  29. package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +10 -10
  30. package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +1 -1
  31. package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +2 -2
  32. package/dist/assets/themes/base/_sections/marketing/stats/section.html +1 -1
  33. package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +5 -5
  34. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +1 -1
  35. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +2 -2
  36. package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +10 -10
  37. package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +10 -10
  38. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +2 -2
  39. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +4 -4
  40. package/dist/build.js +69 -28
  41. package/dist/cli-run.js +20 -13
  42. package/dist/cli.js +12 -7
  43. package/dist/commands/build.js +0 -2
  44. package/dist/commands/deploy.js +116 -44
  45. package/dist/commands/finalize-release.js +5 -4
  46. package/dist/commands/launch.js +12 -7
  47. package/dist/commands/lib/deploy-precheck.js +66 -34
  48. package/dist/commands/lib/ensure-target.js +71 -11
  49. package/dist/commands/package.js +0 -1
  50. package/dist/commands/publish.js +11 -4
  51. package/dist/commands/release.js +99 -252
  52. package/dist/commands/runner.js +176 -42
  53. package/dist/commands/sign-windows.js +46 -15
  54. package/dist/commands/test.js +4 -3
  55. package/dist/commands/validate-certs.js +291 -115
  56. package/dist/config/page-template.html +4 -0
  57. package/dist/defaults/.github/workflows/build.yml +146 -64
  58. package/dist/defaults/AGENTS.md +3 -2
  59. package/dist/defaults/_.gitignore +4 -2
  60. package/dist/defaults/config/certs/README.md +25 -45
  61. package/dist/defaults/config/omega.json5 +66 -32
  62. package/dist/defaults/hooks/deploy/pre.js +10 -0
  63. package/dist/defaults/src/assets/scss/main.scss +12 -2
  64. package/dist/defaults/src/integrations/tray/index.js +1 -1
  65. package/dist/gulp/main.js +13 -25
  66. package/dist/gulp/tasks/audit.js +18 -6
  67. package/dist/gulp/tasks/build-config.js +181 -60
  68. package/dist/gulp/tasks/bundle.js +108 -36
  69. package/dist/gulp/tasks/release.js +86 -4
  70. package/dist/gulp/tasks/sass.js +11 -0
  71. package/dist/hooks/lib/notarize-tools.js +137 -0
  72. package/dist/hooks/notarize-artifacts.js +57 -0
  73. package/dist/hooks/notarize.js +59 -8
  74. package/dist/lib/auth-persistence.js +25 -0
  75. package/dist/lib/client-bridge.js +11 -8
  76. package/dist/lib/deep-link.js +15 -8
  77. package/dist/lib/protocol.js +7 -1
  78. package/dist/lib/restart-manager/install.js +6 -3
  79. package/dist/lib/sign-helpers/auto-unlock.js +65 -27
  80. package/dist/lib/sign-helpers/console-lock.js +34 -0
  81. package/dist/lib/sign-helpers/exec-with-limit.js +68 -0
  82. package/dist/lib/sign-helpers/resolve-icons.js +6 -4
  83. package/dist/lib/tray.js +7 -6
  84. package/dist/main.js +16 -0
  85. package/dist/preload.js +8 -0
  86. package/dist/renderer.js +16 -4
  87. package/dist/runner/job-started.js +104 -0
  88. package/dist/test/fixtures/consumer-app/config/omega.json5 +7 -0
  89. package/dist/test/fixtures/consumer-app/package.json +1 -1
  90. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +7 -1
  91. package/dist/test/harness/main-entry.js +2 -1
  92. package/dist/test/harness/renderer-preload.js +12 -3
  93. package/dist/test/runners/boot.js +101 -11
  94. package/dist/test/runners/electron.js +1 -1
  95. package/dist/test/suites/boot/consumer-app-boots.test.js +54 -0
  96. package/dist/test/suites/build/audit.test.js +45 -6
  97. package/dist/test/suites/build/auth-persistence-resolve.test.js +119 -0
  98. package/dist/test/suites/build/auto-unlock.test.js +105 -0
  99. package/dist/test/suites/build/boot-runner-timeout.test.js +253 -0
  100. package/dist/test/suites/build/brand-scss.test.js +106 -0
  101. package/dist/test/suites/build/build-config.test.js +146 -36
  102. package/dist/test/suites/build/build-json-bake.test.js +245 -0
  103. package/dist/test/suites/build/build-verbs.test.js +48 -15
  104. package/dist/test/suites/build/build-workflow-jobs.test.js +190 -0
  105. package/dist/test/suites/build/cli.test.js +32 -10
  106. package/dist/test/suites/build/config-schema.test.js +4 -4
  107. package/dist/test/suites/build/console-lock.test.js +51 -0
  108. package/dist/test/suites/build/defaults-scaffold.test.js +71 -2
  109. package/dist/test/suites/build/deploy-direct.test.js +241 -0
  110. package/dist/test/suites/build/deploy-dispatch.test.js +287 -0
  111. package/dist/test/suites/build/deploy-hook.test.js +169 -0
  112. package/dist/test/suites/build/ensure-target.test.js +14 -2
  113. package/dist/test/suites/build/env-delivery.test.js +19 -10
  114. package/dist/test/suites/build/env-watch.test.js +18 -7
  115. package/dist/test/suites/build/esm-only-dependency.test.js +127 -0
  116. package/dist/test/suites/build/exec-with-limit.test.js +53 -0
  117. package/dist/test/suites/build/finalize-release.test.js +2 -2
  118. package/dist/test/suites/build/get-config.test.js +120 -8
  119. package/dist/test/suites/build/github-utils.test.js +12 -6
  120. package/dist/test/suites/build/license-stamp.test.js +6 -4
  121. package/dist/test/suites/build/manager.test.js +88 -68
  122. package/dist/test/suites/build/manifest-deps.test.js +116 -0
  123. package/dist/test/suites/build/merge-line-files.test.js +27 -4
  124. package/dist/test/suites/build/notarize-artifacts.test.js +135 -0
  125. package/dist/test/suites/build/notarize-tools.test.js +38 -0
  126. package/dist/test/suites/build/notarize.test.js +207 -0
  127. package/dist/test/suites/build/release-pipeline.test.js +38 -60
  128. package/dist/test/suites/build/release-skipped-upload.test.js +107 -0
  129. package/dist/test/suites/build/resolve-icons.test.js +35 -35
  130. package/dist/test/suites/build/runner-job-guard.test.js +182 -0
  131. package/dist/test/suites/build/runner.test.js +635 -5
  132. package/dist/test/suites/build/sentry.test.js +8 -3
  133. package/dist/test/suites/build/setup-scripts.test.js +3 -0
  134. package/dist/test/suites/build/sign-windows.test.js +92 -8
  135. package/dist/test/suites/build/test-stealth.test.js +17 -11
  136. package/dist/test/suites/build/url-helpers.test.js +37 -17
  137. package/dist/test/suites/build/validate-certs.test.js +428 -55
  138. package/dist/test/suites/build/validate-config.test.js +3 -3
  139. package/dist/test/suites/main/auth-flow.test.js +12 -0
  140. package/dist/test/suites/main/auth-persistence.test.js +14 -17
  141. package/dist/test/suites/main/auto-updater.test.js +2 -2
  142. package/dist/test/suites/main/boot-sequence.test.js +1 -1
  143. package/dist/test/suites/main/client-bridge.integration.test.js +5 -82
  144. package/dist/test/suites/main/client-bridge.test.js +19 -6
  145. package/dist/test/suites/main/deep-link.test.js +54 -0
  146. package/dist/test/suites/main/startup-paths-and-ua.test.js +1 -1
  147. package/dist/test/suites/main/url-helpers.test.js +81 -72
  148. package/dist/test/suites/renderer/cross-context-helpers.test.js +19 -16
  149. package/dist/utils/build-pipeline.js +10 -10
  150. package/dist/utils/github.js +12 -51
  151. package/dist/utils/load-env.js +66 -0
  152. package/dist/utils/mode-helpers.js +43 -111
  153. package/dist/utils/platform.js +37 -0
  154. package/dist/utils/runner-env.js +2 -1
  155. package/dist/utils/runner-job-guard.js +149 -0
  156. package/dist/utils/ship-keys.js +52 -0
  157. package/dist/utils/test-stealth.js +5 -3
  158. package/dist/utils/url-helpers.js +33 -17
  159. package/dist/vendor/config/bundle-id.js +53 -0
  160. package/dist/vendor/config/client-config.js +141 -0
  161. package/dist/vendor/config/company.js +334 -15
  162. package/dist/vendor/config/dev-facts.js +48 -0
  163. package/dist/vendor/config/env-delivery.js +231 -9
  164. package/dist/vendor/config/env-retired.js +137 -0
  165. package/dist/vendor/config/env-rules.js +22 -3
  166. package/dist/vendor/config/env-schema.js +234 -117
  167. package/dist/vendor/config/env.js +55 -26
  168. package/dist/vendor/config/environment.js +189 -0
  169. package/dist/vendor/config/hooks.js +13 -11
  170. package/dist/vendor/config/index.js +125 -44
  171. package/dist/vendor/config/load.js +366 -115
  172. package/dist/vendor/config/merge.js +2 -2
  173. package/dist/vendor/config/order.js +3 -3
  174. package/dist/vendor/config/platforms.js +276 -0
  175. package/dist/vendor/config/repo.js +249 -103
  176. package/dist/vendor/config/retired-keys.js +232 -27
  177. package/dist/vendor/config/schema.js +525 -99
  178. package/dist/vendor/config/site-global.js +63 -53
  179. package/dist/vendor/config/targets.js +187 -0
  180. package/dist/vendor/config/validate.js +119 -59
  181. package/dist/vendor/devkit/argv.js +118 -0
  182. package/dist/vendor/devkit/attach-log-file.js +21 -13
  183. package/dist/vendor/devkit/brand-tokens.js +278 -0
  184. package/dist/vendor/devkit/brand-version.js +425 -0
  185. package/dist/vendor/devkit/build-json.js +91 -0
  186. package/dist/vendor/devkit/bundle.js +48 -0
  187. package/dist/vendor/devkit/certificate-expiry.js +108 -0
  188. package/dist/vendor/devkit/certs.js +16 -194
  189. package/dist/vendor/devkit/ci-workflows.js +124 -8
  190. package/dist/vendor/devkit/cli-router.js +3 -3
  191. package/dist/vendor/devkit/defaults-engine.js +69 -7
  192. package/dist/vendor/devkit/deploy-follow.js +297 -0
  193. package/dist/vendor/devkit/deploy-precheck.js +23 -8
  194. package/dist/vendor/devkit/deploy-record.js +11 -27
  195. package/dist/vendor/devkit/deploy-snapshot.js +661 -0
  196. package/dist/vendor/devkit/deploy.js +505 -75
  197. package/dist/vendor/devkit/git-auth.js +73 -0
  198. package/dist/vendor/devkit/git-remote.js +172 -0
  199. package/dist/vendor/devkit/github-repo.js +290 -0
  200. package/dist/vendor/devkit/local.js +98 -3
  201. package/dist/vendor/devkit/lockfile.js +127 -0
  202. package/dist/vendor/devkit/merge-line-files.js +23 -16
  203. package/dist/vendor/devkit/omega-bin.js +18 -3
  204. package/dist/vendor/devkit/pack-local.js +388 -0
  205. package/dist/vendor/devkit/preludes/index.js +120 -0
  206. package/dist/vendor/devkit/preludes/origin-heal.js +142 -0
  207. package/dist/vendor/devkit/service-account.js +43 -0
  208. package/dist/vendor/devkit/ship-plan.js +112 -0
  209. package/dist/vendor/devkit/signing-env.js +180 -0
  210. package/dist/vendor/devkit/signing-tree.js +92 -0
  211. package/dist/vendor/devkit/target-seams.js +142 -0
  212. package/dist/vendor/devkit/target-secrets.js +231 -57
  213. package/dist/vendor/devkit/test/esm-only-fixture.js +48 -0
  214. package/dist/vendor/devkit/test/fixtures/esm-only-package/browser.js +19 -0
  215. package/dist/vendor/devkit/test/fixtures/esm-only-package/index.js +20 -0
  216. package/dist/vendor/devkit/test/fixtures/esm-only-package/package.json +13 -0
  217. package/dist/vendor/monitoring/env.js +20 -10
  218. package/dist/vendor/monitoring/main.js +1 -1
  219. package/dist/vendor/monitoring/preload.js +1 -1
  220. package/dist/vendor/monitoring/renderer.js +1 -1
  221. package/docs/analytics.md +1 -1
  222. package/docs/auto-updater.md +5 -5
  223. package/docs/boot-sequence.md +1 -1
  224. package/docs/build-system.md +15 -7
  225. package/docs/client-bridge.md +9 -7
  226. package/docs/config-schema.md +4 -4
  227. package/docs/css.md +8 -2
  228. package/docs/deep-link.md +12 -4
  229. package/docs/environment-detection.md +32 -24
  230. package/docs/hooks.md +3 -1
  231. package/docs/icons.md +7 -7
  232. package/docs/index.md +61 -23
  233. package/docs/installer-options.md +24 -21
  234. package/docs/logging.md +5 -5
  235. package/docs/releasing.md +29 -19
  236. package/docs/runner.md +51 -14
  237. package/docs/shared/brands.md +12 -6
  238. package/docs/shared/breaking-changes.md +375 -21
  239. package/docs/shared/config.md +764 -199
  240. package/docs/shared/deploys.md +217 -91
  241. package/docs/shared/icons.md +18 -0
  242. package/docs/shared/local-dev.md +28 -8
  243. package/docs/shared/logging.md +9 -6
  244. package/docs/shared/monitoring.md +27 -13
  245. package/docs/shared/publishing.md +3 -3
  246. package/docs/shared/rulings.md +2 -2
  247. package/docs/shared/testing.md +1 -1
  248. package/docs/shared/theming.md +26 -1
  249. package/docs/shared/translation.md +49 -7
  250. package/docs/shared/updates.md +1 -1
  251. package/docs/signing.md +60 -34
  252. package/docs/test-framework.md +10 -5
  253. package/docs/themes.md +15 -1
  254. package/package.json +18 -12
  255. package/bin/omega-desktop +0 -2
  256. package/dist/commands/push-secrets.js +0 -141
  257. package/dist/test/suites/build/deliver-certs.test.js +0 -95
  258. package/dist/test/suites/build/derive-signing-env.test.js +0 -122
  259. package/dist/test/suites/build/push-secrets.test.js +0 -226
  260. package/dist/test/suites/build/resolve-signing-cert.test.js +0 -342
  261. package/dist/utils/deliver-certs.js +0 -69
  262. package/dist/utils/derive-signing-env.js +0 -56
  263. package/dist/utils/resolve-signing-cert.js +0 -175
  264. package/dist/vendor/config/desktop-artifacts.js +0 -110
  265. package/dist/vendor/config/instances.js +0 -208
  266. /package/dist/defaults/config/icons/{macos → mac}/dmg.png +0 -0
  267. /package/dist/defaults/config/icons/{macos → mac}/icon.png +0 -0
  268. /package/dist/defaults/config/icons/{macos → mac}/tray.png +0 -0
package/docs/runner.md CHANGED
@@ -50,12 +50,13 @@ Then, in a normal (non-elevated) PowerShell:
50
50
  4. `npx omega sign-windows --smoke` — copies `where.exe` to a temp dir, signs it (the PIN is typed into SafeNet's Token Logon dialog for you), verifies it, cleans up. If this fails, stop and fix it: the runner calls exactly the same `signtool` underneath. Then `npx omega test --extended desktop:build/sign-windows-e2e` from a desktop target: the Windows-gated end-to-end suite, an `--extended` lane because it drives the real token (the bare lane skips its five signing cases everywhere, this box included), which signs, verifies, checks that verify rejects an unsigned file, reads back the event log, and runs one `windows-sign` job's command against the token (it skips itself everywhere but here). That rejection case leans on `where.exe` being catalog-signed rather than embedded-signed, so `signtool verify /pa` reports the unsigned copy as unsigned; if it comes back signed on your box, the sample carries an embedded signature there and the case needs a different one.
51
51
  5. `npx omega runner install` again. It tears down any prior install (the electron-manager era's included), downloads the pinned `actions/runner`, registers one runner per org in `OMEGA_RUNNER_ORGS` (every org you administer when it is empty), writes each Startup shortcut, and then brings every registered runner online detached, leaving your terminal free. A scripted install ends online too: nothing about that step needs a terminal.
52
52
  6. `npx omega runner status` — confirm the config keys are set, one Startup shortcut and one live listener per org, and that no listener reports `session=0`.
53
+ 7. Read the two allow lists the install wrote, and narrow them if you want to ([#875](https://github.com/Omega-JS-Stack/omega/issues/875), § The job guard below): `%LOCALAPPDATA%\omega-runner\allowed-repos.txt` and `allowed-actors.txt`. The install fills them with `<org>/*` for every org it registered and with your `GH_TOKEN` user's login; both are plain text, one entry per line, `#` comments allowed, and nothing rewrites a line you changed.
53
54
 
54
55
  `install` and `config` are ONE walk, so there is nothing to remember about which command changes what. Every key is asked, in the file's order, required or optional, and each question's DEFAULT is that key's current value: what the file saved, else what the shell or a CI job delivered, else the suggestion (the newest SDK `signtool`). Enter keeps what is there — including a secret, which is never echoed back at you ("Enter keeps the current value"). "Current" means current: a key the file does not have yet but your shell delivered is offered as the default, and Enter writes it into the box's file. Every value the walk ends with is written, the kept ones too (which is how an old file's bare `KEY=value` lines come back as `KEY="value"` without a single value moving), and they are written before the orgs question, so backing out of it never costs you the keys you just typed. A required key with nothing saved and an empty answer is refused, naming the key.
55
56
 
56
57
  Then the orgs: a checkbox of every org your token administers, in alphabetical order. Ticked by default are the orgs this box already answered for — the saved `OMEGA_RUNNER_ORGS`, else the orgs it actually registered, else NOTHING. A token that administers 35 orgs must never register 35 runners on one Enter. Ticking nothing is refused too — a runner registered against no org is not a runner. Off a TTY the walk asks nothing: `config` says so and stops (edit the file instead), `install` refuses only when a required key is missing, and a blank org list still means every org the token administers, because CI has no keyboard.
57
58
 
58
- `start` is the exception, deliberately: it asks only for MISSING required keys, never the checkbox, and warns when the file's org list is not the one this install registered.
59
+ `start` is the exception, deliberately: it asks only for MISSING required keys, never the checkbox, and warns when the file's org list is not the one this install registered. On a bare box (nothing installed yet) `start` runs install itself, so there it is install's full walk, checkbox included.
59
60
 
60
61
  Orgs in `OMEGA_RUNNER_ORGS` you do not administer are named in a warning and skipped.
61
62
 
@@ -63,13 +64,13 @@ Orgs in `OMEGA_RUNNER_ORGS` you do not administer are named in a warning and ski
63
64
 
64
65
  ```powershell
65
66
  npx omega runner status # registered orgs, Startup shortcuts, live listeners, legacy leftovers
66
- npx omega runner start # bring EVERY registered org's runner up, detached (idempotent: an org already alive is skipped)
67
+ npx omega runner start # the one command: install a bare box, else heal HOME and the job guard, refresh a stale actions/runner in place,
68
+ # register any admin org not served yet, then bring EVERY registered org up, detached (an org already alive is skipped)
67
69
  npx omega runner restart # stop, wait for the listeners to go, then start
68
70
  npx omega runner stop # kill every Runner.Listener.exe under the runner home
69
- npx omega runner install # idempotent full setup — tears down first, so re-running is safe
71
+ npx omega runner install # the explicit clean rebuild: tears down first, so re-running is safe
70
72
  npx omega runner config # the same full walk install runs — every key, current values as the defaults, plus the orgs
71
73
  npx omega runner register-org <org># register one specific org
72
- npx omega runner self-update # npm i -g @omega.js/desktop@latest
73
74
  npx omega runner uninstall # remove everything, legacy services, tasks and the em-runner install included
74
75
  npx omega runner monitor # tail the signing event log
75
76
  ```
@@ -78,13 +79,13 @@ Notes worth knowing before you use them:
78
79
 
79
80
  - **`config` is `install`'s configuration step, alone.** Same walk, same defaults, same order — it just does not go on to register anything. A saved org your token no longer administers is named before the checkbox, since it cannot appear in it. Windows-only, and terminal-only (with nothing to ask with it stops and names the file). When the orgs you pick are not the ones this install registered, it says to re-run `install`.
80
81
  - **Every subcommand tees its output to `<runner home>\logs\runner.log`**, and so does `npx omega sign-windows` — including inside a `windows-sign` job, which is exactly when the box's own record is wanted (the log lives in the runner home, never in a workspace, so the usual "no logs in CI" rule does not apply to it). `runner status` prints the path on its own line. It APPENDS, because more than one process writes it: `start` returns as soon as the runners are spawned, and every `sign-windows` those listeners go on to run adds to the same file. Each run stamps its own `# omega log` header; nothing rotates it, so delete the file when you want a clean one. `uninstall` keeps `.env` and `logs\`, since the log it is writing while it runs is the trail of that uninstall.
81
- - **A refusal writes nothing.** Off Windows every subcommand but `self-update` and `monitor` stops at the platform check before the log file is opened, so running one on a Mac by accident leaves no `.gh-runners/` in the directory you were standing in.
82
+ - **A refusal writes nothing.** Off Windows every subcommand but `monitor` stops at the platform check before the log file is opened, so running one on a Mac by accident leaves no `.gh-runners/` in the directory you were standing in.
82
83
  - **The box verbs ignore the project's `.env` cascade.** `runner` and `sign-windows` read the shell and `<runner home>\.env`, nothing else, so running them from inside a brand folder can never hand a brand's `GH_TOKEN` to the box ([#337](https://github.com/Omega-JS-Stack/omega/issues/337)). Every other verb keeps the cascade.
83
- - **`start` brings up EVERY registered org, detached.** It walks the Startup shortcuts in order and spawns one hidden runner per org, so the terminal comes straight back and nothing is lost by not watching it: the listener's output is the JSONL log `monitor` tails. Running it again is safe, which is the point of it: an org whose listener is already alive is named with its PID and skipped, never duplicated and never a refusal. Exit 0 when every org ends up online, 1 when a spawn failed. A listener sitting in session 0 is skipped too, but loudly: it is alive and will fail every job it picks up, so the line names the session and points at `restart`. Killing a listener is `stop`'s job, never a start's.
84
+ - **`start` brings up EVERY registered org, detached.** It walks the Startup shortcuts in order and spawns one hidden runner per org, so the terminal comes straight back and nothing is lost by not watching it: the listener's output is the JSONL log `monitor` tails. Running it again is safe, which is the point of it: an org whose listener is already alive is named with its PID and skipped, never duplicated and never a refusal. Exit 0 when every org ends up online, 1 when a spawn or a registration failed. A listener sitting in session 0 is skipped too, but loudly: it is alive and will fail every job it picks up, so the line names the session and points at `restart`. Killing a listener is `stop`'s job, never a start's.
84
85
  - **`restart` is `stop` then `start`**, in that order, for the loop you would otherwise run by hand after a config change. It settles two things the two commands typed in sequence do not: the box config walk runs FIRST, so a box missing a required key is refused before anything is killed rather than left stopped, and each org's dir is polled (5 seconds) until no listener stands in it, because `taskkill` returns before the process is gone and a start that raced it would read the dying listener as "already running". A listener still standing after the wait is reported and that org is left alone, exit 1.
85
86
  - **`stop` leaves the Startup shortcuts in place**, so a logout/login brings the runner back. For a permanent stop, run `uninstall`.
86
- - **Every subcommand except `self-update` and `monitor` refuses on non-Windows.** `OMEGA_RUNNER_FORCE=1` overrides it, for framework tests only.
87
- - **A test process cannot touch a real box.** Every subcommand that changes the machine — `install`, `config`, `register-org`, `start`, `restart`, `stop`, `uninstall`, `self-update` — refuses, before the platform check and naming `OMEGA_TEST_RUNNER`, whenever the run is a test (`omega test` sets that variable; the electron/boot runners set `OMEGA_TEST_MODE`) and a home it could act on is not a scratch one, under a `.temp` directory or the OS temp dir. BOTH homes are checked, the passed one and the module-level `RUNNER_HOME`, because the box's `.env` was already read into the process from the latter when the command module was required; the refusal names whichever is real. The marker is read from the process environment only — an injected environment is a fixture for the config walk, never an answer to "am I a test". The Startup folder is the THIRD surface checked, because no home scopes it — `uninstall` sweeps every `omega-runner-*.cmd` in the folder whatever home it was given, and a case whose two homes were both scratch deleted the box's three real shortcuts. It has its own scratch seam, `OMEGA_RUNNER_STARTUP_DIR`, and a test run against the real folder refuses naming both. The surfaces that no path can redirect at all — the watcher service, the legacy logon tasks, the `actions.runner.*` services, and the legacy `em-runner` homes — are simply not swept under a test run; `uninstall` says so on one line instead. A test run also never kills a process whose ExecutablePath it cannot read: that path belongs to another account, and matching it scopes to no home at all, so `stop` and `uninstall` drop the clause instead of `taskkill /F`-ing a stranger's runner. And it never deregisters on its own: `deregisterOrgRunners` mints a real removal token and runs each org directory's `config.cmd`, neither of them home-scoped, so with a roster to remove a test run refuses, naming the seam, unless `exec` is injected and, while `GH_TOKEN` is set, `getRemoveToken` too. A framework suite that wants to drive these points `OMEGA_RUNNER_HOME` and `OMEGA_RUNNER_STARTUP_DIR` at scratches *before* the command module is required, since both are resolved once, at require time. `sign-windows` follows the same rule for its log: from a test run pointed at a real home it tees nowhere rather than write the box's own record. This exists because a suite once ran a real `install` on the box and registered 34 orgs.
87
+ - **Every subcommand except `monitor` refuses on non-Windows.** `OMEGA_RUNNER_FORCE=1` overrides it, for framework tests only.
88
+ - **A test process cannot touch a real box.** Every subcommand that changes the machine (`install`, `config`, `register-org`, `start`, `restart`, `stop`, `uninstall`) refuses, before the platform check and naming `OMEGA_TEST_RUNNER`, whenever the run is a test (`omega test` sets that variable; `OMEGA_TEST_MODE` counts too, the marker a sibling framework's runner or a consumer's own test script sets) and a home it could act on is not a scratch one, under a `.temp` directory or the OS temp dir. BOTH homes are checked, the passed one and the module-level `RUNNER_HOME`, because the box's `.env` was already read into the process from the latter when the command module was required; the refusal names whichever is real. The marker is read from the process environment only — an injected environment is a fixture for the config walk, never an answer to "am I a test". The Startup folder is the THIRD surface checked, because no home scopes it — `uninstall` sweeps every `omega-runner-*.cmd` in the folder whatever home it was given, and a case whose two homes were both scratch deleted the box's three real shortcuts. It has its own scratch seam, `OMEGA_RUNNER_STARTUP_DIR`, and a test run against the real folder refuses naming both. The surfaces that no path can redirect at all — the watcher service, the legacy logon tasks, the `actions.runner.*` services, and the legacy `em-runner` homes — are simply not swept under a test run; `uninstall` says so on one line instead. A test run also never kills a process whose ExecutablePath it cannot read: that path belongs to another account, and matching it scopes to no home at all, so `stop` and `uninstall` drop the clause instead of `taskkill /F`-ing a stranger's runner. And it never deregisters on its own: `deregisterOrgRunners` mints a real removal token and runs each org directory's `config.cmd`, neither of them home-scoped, so with a roster to remove a test run refuses, naming the seam, unless `exec` is injected and, while `GH_TOKEN` is set, `getRemoveToken` too. `start` and `restart` skip their org check the same way, on one line, unless both `_discoverOrgs` and `_registerOrg` are injected: registering acts on GitHub, never on a home. A framework suite that wants to drive these points `OMEGA_RUNNER_HOME` and `OMEGA_RUNNER_STARTUP_DIR` at scratches *before* the command module is required, since both are resolved once, at require time. `sign-windows` follows the same rule for its log: from a test run pointed at a real home it tees nowhere rather than write the box's own record. This exists because a suite once ran a real `install` on the box and registered 34 orgs.
88
89
  - **`uninstall` deregisters on the GitHub side first, and keeps whatever did not come off.** It walks every `actions-runner-<org>\` directory under the runner home and runs that directory's own `config.cmd remove` before anything is deleted. If a removal exits non-zero — or `GH_TOKEN` is not set, so no removal token can be minted — that runner is still registered, so its directory SURVIVES the uninstall and the summary names the org. Re-running `uninstall` retries it. Belt and braces: `register-org` also deletes every org-side runner starting with this host's prefix before it registers a new one.
89
90
 
90
91
  ## Upgrading from the electron-manager runner
@@ -101,7 +102,9 @@ So the upgrade on the box is the ordinary one: `npx omega runner install` with `
101
102
 
102
103
  ## Adding a new org
103
104
 
104
- Nothing happens automatically — no process is watching for new orgs. Run `npx omega runner install` again (it is idempotent and re-registers everything), or register just the one org:
105
+ No process watches for new orgs, but `start` checks every time it runs: `npx omega runner start` lists the orgs your `GH_TOKEN` administers, narrows them by `OMEGA_RUNNER_ORGS` when it is set, registers each one this box does not serve yet (Startup shortcut included), and brings it online with the rest. Orgs registered outside the list stay registered; `npx omega runner install` is the command that reshapes the box to the list exactly. An org check that cannot reach GitHub is one warn line, and `start` carries on and brings every registered org online.
106
+
107
+ To register one specific org alone:
105
108
 
106
109
  ```powershell
107
110
  npx omega runner register-org <org-name>
@@ -109,6 +112,34 @@ npx omega runner register-org <org-name>
109
112
 
110
113
  `register-org` writes the Startup shortcut but does not launch the runner. Run `npx omega runner start` (it brings up every registered org and skips the ones already alive), or log out and back in.
111
114
 
115
+ ## The job guard
116
+
117
+ The box holds the EV token, so whoever can start a job on it can sign binaries with your cert. The workflows this framework generates fire only on a dispatch, but a workflow is one careless edit away from a `push` trigger, so the box refuses the job itself ([#875](https://github.com/Omega-JS-Stack/omega/issues/875)).
118
+
119
+ `npx omega runner install` (and `start` / `restart`, which heal a box installed before the guard existed) writes three files:
120
+
121
+ | File | What it is |
122
+ |---|---|
123
+ | `%LOCALAPPDATA%\omega-runner\job-started.js` | the hook, a copy of the framework's own, rewritten from the package on every install and start so an update reaches the box |
124
+ | `%LOCALAPPDATA%\omega-runner\allowed-repos.txt` | one `owner/name` per line; `owner/*` means every repo of one owner |
125
+ | `%LOCALAPPDATA%\omega-runner\allowed-actors.txt` | one GitHub login per line |
126
+
127
+ and delivers the hook's path to each registration through that runner's own `<runner dir>\.env` as `ACTIONS_RUNNER_HOOK_JOB_STARTED`: the same file the private HOME travels in, so one write reaches the Startup shortcut and the detached spawn alike. GitHub's runner executes it before a job's first step and FAILS the job on a nonzero exit, with the hook's output in the job's `Set up runner` step. The runner runs it with its own bundled node; nothing on your PATH is involved.
128
+
129
+ Three checks, all of which must pass:
130
+
131
+ | Check | Refused when |
132
+ |---|---|
133
+ | `GITHUB_EVENT_NAME` | it is not `workflow_dispatch`, the ONE trusted event (#923): every publish path (a CMS button, `omega deploy`, a plain HTTP call) dispatches it at the deploy ref |
134
+ | `GITHUB_REPOSITORY` | no line of `allowed-repos.txt` matches it |
135
+ | `GITHUB_ACTOR` | no line of `allowed-actors.txt` matches it. A dispatch carries the deploy token's user, which is the same account |
136
+
137
+ A miss prints ONE line naming the check and the value it saw, and the job dies with nothing run: no checkout, no install, no `signtool`.
138
+
139
+ **Editing the lists is the point.** They start as the scope the registration already has: an `<org>/*` line per registered org, and the `GH_TOKEN` user's login. Narrow `Omega-JS-Stack/*` to `Omega-JS-Stack/my-brand-omega` and the next `install` leaves it alone: an org that already has ANY line is never appended to again. Add a second account by adding a line. An EMPTY list refuses every job rather than passing one, which is what a box whose `GH_TOKEN` could not answer at install time will do until you fill `allowed-actors.txt` in.
140
+
141
+ `uninstall` keeps both lists (they are the box's configuration, like its `.env`) and removes the hook script, which the next install writes back.
142
+
112
143
  ## Environment variables
113
144
 
114
145
  Split by who sets them.
@@ -126,6 +157,8 @@ Split by who sets them.
126
157
 
127
158
  Shell-only (they decide where the file IS, so they cannot live in it): `OMEGA_RUNNER_HOME` overrides the runner home, `OMEGA_RUNNER_STARTUP_DIR` the Startup folder the shortcuts are written to and swept from (framework tests only — it is the folder's scratch seam), and `OMEGA_SIGN_LOG` the signing event log path.
128
159
 
160
+ **The runner's own `<runner dir>\.env`** carries two framework-written lines, `HOME` (below) and `ACTIONS_RUNNER_HOOK_JOB_STARTED` (§ The job guard). It is the file GitHub documents for runner env, one `KEY=value` per line, read by the listener at startup and applied to every job it runs; a change to it takes effect at the next `npx omega runner restart`.
161
+
129
162
  **The runner's own HOME** ([#807](https://github.com/Omega-JS-Stack/omega/issues/807)) is `%LOCALAPPDATA%\omega-runner\home`, and it holds one file: a real `.gitconfig` carrying `[core] autocrlf = false`. `actions/checkout` copies `$HOME\.gitconfig` into the temporary HOME it runs git under, and its copy helper recreates a SYMLINK source as a Windows junction, which is invalid for a file: a box whose `~/.gitconfig` is a dotfiles symlink (and it stays one) failed every git call in every job with `unknown error occurred while reading the configuration files`. HOME wins over the user's home directory in that lookup. It is delivered through each runner's own `<runner dir>\.env` (`HOME=...`, the file GitHub documents for proxy settings, one `KEY=value` per line, read by the listener at startup and applied to every job it runs), written by `install` and healed by `start` and `restart` for every registered org, so the Startup shortcut and the detached spawn both get it. `runner status` prints the path and whether the `.gitconfig` is there. A `.gitconfig` that is already there with different content is left alone and named in the output: it is yours to edit.
130
163
 
131
164
  The runner belongs to whoever is logged in. There is no service and no scheduled
@@ -133,17 +166,17 @@ task, so there is no separate account to configure: `WIN_RUNNER_LOGON_ACCOUNT`,
133
166
  `WIN_RUNNER_LOGON_PASSWORD` and the `set-credentials` subcommand were retired in
134
167
  [#337](https://github.com/Omega-JS-Stack/omega/issues/337).
135
168
 
136
- **Delivered by CI, optionally** — a brand MAY push `WIN_EV_TOKEN_PATH`, `WIN_CSC_KEY_PASSWORD` and `SIGNTOOL_PATH` as GitHub Actions secrets (`npx omega push-secrets` from its `.env`); the `windows-sign` job's env block carries them and they win over the box file. A self-hosted box normally holds all three itself, so a brand pushes none — the generated env block delivers them empty, and an empty value counts as absent.
169
+ **Delivered by CI, optionally**: a brand MAY push `WIN_EV_TOKEN_PATH`, `WIN_CSC_KEY_PASSWORD` and `SIGNTOOL_PATH` as GitHub Actions secrets (`omega deploy`'s precheck sends them from its `.env`); the `windows-sign` job's env block carries them and they win over the box file. A self-hosted box normally holds all three itself, so a brand pushes none: the generated env block delivers them empty, and an empty value counts as absent.
137
170
 
138
- The signing strategy itself is config, not env: `platforms.win.signing.strategy` in `config/omega.json5`, `self-hosted` by default.
171
+ The signing strategy itself is config, not env: `platforms.windows.signing.strategy` in `config/omega.json5`, `self-hosted` by default.
139
172
 
140
173
  ## How a signing job runs
141
174
 
142
175
  1. The mac/linux legs of the brand's `build.yml` package and publish as usual; the Windows leg packages **unsigned** and uploads a `windows-unsigned` artifact.
143
176
  2. The `windows-sign` job picks up on your box (labels `self-hosted, windows, ev-token`), downloads that artifact, and runs `npx omega sign-windows --in release --out release/signed`.
144
177
  3. For each `.exe` / `.msi`: copy to the output dir, then `signtool sign` with the resolved cert args, `/tr <timestamp url> /td sha256 /fd sha256`.
145
- 4. In thumbprint mode a helper starts alongside the sign call and types the PIN into SafeNet's "Token Logon" dialog if it appears.
146
- 5. **The sign call gets three attempts** with a short backoff. The timestamp server is a third party and a timeout or a 502 from it fixes itself; a failure signtool names as permanent (no matching certificate, wrong password, locked token, malformed file) stops on the first attempt instead of walking the token toward a lockout. Every attempt and every retry lands in the event log.
178
+ 4. In thumbprint mode a helper starts alongside the sign call and types the PIN into SafeNet's "Token Logon" dialog if it appears, then watches the dialog for ten seconds: one still open after typing is reported as "the keystrokes did not reach it". Before the first call, a locked console (`LogonUI.exe` running) is refused outright, naming the fix, because the dialog renders behind the lock screen and the PIN lands on the lock screen instead (§ A locked console).
179
+ 5. **The sign call gets three attempts** with a short backoff, and **each attempt has a three-minute limit**: a healthy sign takes about twenty seconds, so one still running minutes later is waiting on something that never comes, and its process tree is terminated with a message naming the limit (the attempt reads as transient, so the next one gets a fresh PIN watcher). The `windows-sign` job carries a thirty-minute `timeout-minutes` for the same reason. The timestamp server is a third party and a timeout or a 502 from it fixes itself; a failure signtool names as permanent (no matching certificate, wrong password, locked token, malformed file) stops on the first attempt instead of walking the token toward a lockout. Every attempt and every retry lands in the event log.
147
180
  6. `signtool verify /pa` runs once against the signed file. No retry — it is a local check.
148
181
  7. `latest.yml` and per-installer `.blockmap` files are written for the signed NSIS `.exe`s, because Windows is split into a post-build sign job and electron-updater has no other way to discover the release.
149
182
  8. `npx omega finalize-release --signed-dir release/signed` uploads the signed assets to the release the mac/linux legs created.
@@ -202,6 +235,10 @@ The Startup folder is the fix: Explorer launches its contents in the interactive
202
235
 
203
236
  `npx omega runner status` prints the session id of every live listener and warns loudly on `session=0`, and so does `start` when it meets one. If you see one, it is a leftover from an older install: `npx omega runner restart` replaces it.
204
237
 
238
+ ### A locked console
239
+
240
+ Session 1 is not enough on its own: the desktop has to be UNLOCKED. Behind the lock screen the Token Logon dialog still exists (the watcher enumerates it) but every keystroke lands on the lock screen, so the PIN never arrives and `signtool` waits for it forever ([#864](https://github.com/Omega-JS-Stack/omega/issues/864): a Windows Update restart at the end of active hours auto-signed the box in, locked, and the sign job hung six hours). `sign-windows` now refuses a locked console before the first call, naming `LogonUI.exe` and the fix: unlock the box and re-run. Keeping the box unlocked across an update restart is the box's own setting, not the framework's: a classic auto-logon (Sysinternals Autologon) signs in without locking, where Windows' own "finish setting up after an update" always locks.
241
+
205
242
  On a dedicated build box, enable Windows auto-logon so a reboot brings the session (and therefore the runner) back without a human.
206
243
 
207
244
  ### Two listeners on one registration
@@ -245,7 +282,7 @@ The SafeNet driver sometimes detaches after a major OS update. Open the SafeNet
245
282
 
246
283
  ## Upgrading the actions/runner binary
247
284
 
248
- `ACTIONS_RUNNER_VERSION` in `src/commands/runner.js` pins it. Bump the constant, ship a new `@omega.js/desktop`, then on the box: `npx omega runner self-update` followed by `npx omega runner install`. The install tears down the old tree and lays the new version down cleanly.
285
+ `ACTIONS_RUNNER_VERSION` in `src/commands/runner.js` pins it. Bump the constant, ship, then on the box run `npx omega runner start`. It sees that the version `config.json` recorded is not the pinned one, re-downloads `_template` in place, and copies it over each org dir whose listener is not running. `.runner`, `.credentials` and `.env` are not in the template, so every registration survives, and nothing is torn down. An org whose listener IS running has its binaries locked: it is skipped with one line naming `npx omega runner restart`, and the new version is recorded only once every org dir took it, so a `restart` (which stops first) finishes the job. `npx omega runner install` remains the clean rebuild when you want one.
249
286
 
250
287
  ## Related
251
288
 
@@ -12,25 +12,31 @@ Test-only forever, offline forever: project id `demo-naked-brand` (Firebase's em
12
12
 
13
13
  ### `brands/sandbox-brand` — the synthetic fixture
14
14
 
15
- Fixture for the AUTOMATED corpus/e2e suites: offline, `demo-*` Firebase, deterministic. Both targets are REAL framework consumers, `targets/backend` on `@omega.js/backend` and (since [#775](https://github.com/Omega-JS-Stack/omega/issues/775)) `targets/website` on `@omega.js/web`, whose `/e2e` page carries the hooks the brand's own lane drives: the fixture is the brand's content, never a stand-in for the frameworks. Test runs may mangle and reset it. Never touches real cloud. It deliberately carries no agent-docs chain — its files are test output, not a workspace an agent should be guided into.
15
+ Fixture for the AUTOMATED corpus/e2e suites: offline, `demo-*` Firebase, deterministic. Both targets are REAL framework consumers, `targets/backend` on `@omega.js/backend` and (since [#775](https://github.com/Omega-JS-Stack/omega/issues/775)) `targets/web` on `@omega.js/web`, whose `/e2e` page carries the hooks the brand's own lane drives: the fixture is the brand's content, never a stand-in for the frameworks. Test runs may mangle and reset it. Never touches real cloud. It deliberately carries no agent-docs chain: its files are test output, not a workspace an agent should be guided into.
16
16
 
17
- ### `brands/omega-playground` — "OMEGA Playground", the standing live test brand
17
+ ### `brands/playground-omega` — "OMEGA Playground", the standing live test brand
18
18
 
19
- Renamed from omega-brand (Ian 2026-07-11 — zero ambiguity). Born through the real wizard: id `omega-playground`, url playground.omegajs.dev — a SUBDOMAIN so derived surfaces never claim the real omegajs.dev. Points at the real-but-throwaway Firebase project `omegajs-playground` (ITW-org-owned since 2026-07-11; sanctioned for live proofs — Blaze it, break it, delete it; it is TEST INFRASTRUCTURE, never production).
19
+ Renamed from omega-brand (Ian 2026-07-11, zero ambiguity). Born through the real wizard: id `playground` (shortened on 2026-09-10, [#808](https://github.com/Omega-JS-Stack/omega/issues/808), so the `<brand.id>-<role>` rule derives clean repo names off it), url playground.omegajs.dev, a SUBDOMAIN so derived surfaces never claim the real omegajs.dev. Its THREE repos all derive from that id, none of them typed ([#883](https://github.com/Omega-JS-Stack/omega/issues/883), all under the one `repo: { provider: 'github', org: 'Omega-JS-Stack' }` block): `playground-omega` (the PUBLIC source repo since 2026-09-24, so its Actions runs never count against the org storage allowance; the folder name here, whose `main` every `omega deploy` snapshots this folder onto, [docs/shared/deploys.md](deploys.md)), `playground-releases` (public: the desktop installers, the extension zips and the autoupdater feed), and `playground-web` (public: the BUILT site alone, one force-orphan commit on `gh-pages`, served by Pages at playground.omegajs.dev, which is what let the source repo stay private on a free org before it went public). The third, the private `playground-rehearsal` snapshot, retired with the rehearsal itself ([#872](https://github.com/Omega-JS-Stack/omega/issues/872)). Points at the real-but-throwaway Firebase project `omegajs-playground` (ITW-org-owned since 2026-07-11; sanctioned for live proofs: Blaze it, break it, delete it; it is TEST INFRASTRUCTURE, never production).
20
20
 
21
21
  REBRANDED **OMEGA Playground** (Ian 2026-08-21, [#433](https://github.com/Omega-JS-Stack/omega/issues/433) — REVERSES the 2026-07-19 fiction ruling, whose invented brand read as a real product in the ad and analytics consoles). The doctrine now: the playground is openly the OMEGA test surface — "we can mess around here, nothing live actually matters" — so drift from the real omegajs.dev site reads as a demo doing its job, never as a broken copy. Infra identity unchanged (id, url, green accent, classy theme); name and voice are the only things that moved, and the catalog's display names stay placeholder demo copy. Cloud-side display names (the Meta pixel, the GA property + streams, the GCP project name) are the manager's follow-up.
22
22
 
23
+ **The playground converts FIRST, in the same change** (Ian 2026-09-11, [#883](https://github.com/Omega-JS-Stack/omega/issues/883)): "we should ALWAYS be updating playground to our new shapes." Every config, folder, repo, or workflow shape the frameworks adopt lands in `brands/playground-omega` in the commit that adopts it, and the playground deploy is the proof run. A playground on an old shape is a defect, never a pending chore.
24
+
25
+ **Its COMPANY is ITW Creative Works** (`company: { id: "itw-creative-works" }`, [#677](https://github.com/Omega-JS-Stack/omega/issues/677)): one top-level key names the parent, and the parent's name, url and wordmark, its config layer, its `.env`, its hooks and its signing tree all RESOLVE from it. ITW is not an OMEGA brand on this machine yet, so every playground run prints one line saying inheritance is off until it converts (the company layer is simply absent until then). The brand types `company: { webhooks: false }` ([#677](https://github.com/Omega-JS-Stack/omega/issues/677), the successor to the retired `parent: false`), which means only "the SendGrid ACCOUNT is shared with the real ITW brands and its one account-level webhook is theirs": the campaigns and newsletter services are its only readers.
26
+
23
27
  Secrets live only in `.env`/`.omega/secrets` (gitignored; the config loader hard-fails secret-shaped keys) — the committed omega.json5 carries public-by-design values only.
24
28
 
25
29
  ### `brands/newsflash-brand` — "The Daily Build", the second skin
26
30
 
27
31
  Added with Ian 2026-07-17. Id `daily-build`, url dailybuild.omegajs.dev (same subdomain rule). The standing SECOND-SKIN brand: a fictional dev-news publication wearing the newsflash theme permanently, so both first-party skins stay alive in real consumers (classy = playground, newsflash = here). Born by COPY of the playground — the wizard rehearsal is a separate queued exercise. OFFLINE-only (demo-* Firebase, no real cloud/services, never production); website + backend targets only; website dev port pinned 4100 for side-by-side, backend rides N7 bumps.
28
32
 
29
- ### `../omega-brand` — the REAL brand (sibling repo)
33
+ It plays the SUB-BRAND in the company-mode ads proof, so its company is the playground (`company: { id: "playground" }`, [#677](https://github.com/Omega-JS-Stack/omega/issues/677)): `advertising.providers.inhouse.source: 'company'` derives `api.<company host>` from the RESOLVED `company.url`, which is the playground's own url once any playground run has written its registry line on this machine (`scripts/e2e-verts-company.js` is the proof).
34
+
35
+ ### `../omega-omega` — the REAL brand (sibling repo)
30
36
 
31
- BORN 2026-07-18 (cp229), LIVE 2026-07-19 (cp232). Lives outside the monorepo with its own git history; folder + repo renamed from omegajs.dev per Ian 2026-07-19 — consistent with the `*-brand` family, and bare "omega" is the monorepo. GitHub: `itw-creative-works/omega-brand` via the `targets.backend.github.repo` slug (the legacy orgWebsite housing).
37
+ BORN 2026-07-18 (cp229), LIVE 2026-07-19 (cp232). Lives outside the monorepo with its own git history; folder + repo are `omega-omega` per the `<brand.id>-<role>` rule ([#808](https://github.com/Omega-JS-Stack/omega/issues/808), 2026-09-10; `omega-brand` before that, `omegajs.dev` before 2026-07-19). GitHub: `itw-creative-works/omega-omega`, from `repo: { provider: 'github', org: 'itw-creative-works' }` (the legacy orgWebsite housing) plus the `<brand.id>-<role>` rule; its site publishes to `itw-creative-works/omega-web` ([#883](https://github.com/Omega-JS-Stack/omega/issues/883)).
32
38
 
33
- Id `omega`, name "OMEGA", url omegajs.dev (LIVE — GitHub Pages + Cloudflare), classy theme, website port 4200, website + backend targets. Born by FORKING the playground's polished content 1:1 (never a promotion of the test project; stale es caches dropped and re-translated fresh). It is the first SUB-BRAND of Ian's eventual company umbrella — the config `company` layer models this.
39
+ Id `omega`, name "OMEGA", url omegajs.dev (LIVE: GitHub Pages + Cloudflare), classy theme, website port 4200, web + backend + extension targets. The EXTENSION target is the **OMEGA Companion** ([#927](https://github.com/Omega-JS-Stack/omega/issues/927), 2026-09-14): the browser extension that files brand bookmarks and carries the trusted-automation bridge, which used to sit inside `@omega.js/manager` as `extension/` with another product's identity and shipped to no store at all. It is a normal extension target now (Chrome first, the Chrome Web Store lane, no listing created yet), and the router's `omega-extension` upstream holds the MCP half. Born by FORKING the playground's polished content 1:1 (never a promotion of the test project; stale es caches dropped and re-translated fresh). It is the first SUB-BRAND of Ian's eventual company umbrella, which the config `company: { id }` key models ([#677](https://github.com/Omega-JS-Stack/omega/issues/677)).
34
40
 
35
41
  **Backend config is real** (flipped 2026-07-19): project `omegajs`, sdkconfig baked, `.firebaserc` real, Blaze linked to the Main Billing Account. authDomain = the BRAND host per cp268 — `omega build` self-hosts Firebase's `/__/auth/*` helper files (what the 2026-07-19 live 404 was actually missing), and a first-party authDomain keeps sign-in redirects working under browser storage partitioning.
36
42