rigscope 0.0.0-stage → 0.1.1

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 (224) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-path-routes-manifest.json +13 -0
  3. package/.next/build-manifest.json +20 -0
  4. package/.next/export-marker.json +6 -0
  5. package/.next/images-manifest.json +68 -0
  6. package/.next/next-minimal-server.js.nft.json +1 -0
  7. package/.next/next-server.js.nft.json +1 -0
  8. package/.next/package.json +1 -0
  9. package/.next/prerender-manifest.json +297 -0
  10. package/.next/react-loadable-manifest.json +1 -0
  11. package/.next/required-server-files.json +369 -0
  12. package/.next/routes-manifest.json +114 -0
  13. package/.next/server/app/_global-error/page.js +31 -0
  14. package/.next/server/app/_global-error/page.js.nft.json +1 -0
  15. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -0
  16. package/.next/server/app/_global-error.html +1 -0
  17. package/.next/server/app/_global-error.meta +35 -0
  18. package/.next/server/app/_global-error.rsc +18 -0
  19. package/.next/server/app/_global-error.segments/_full.segment.rsc +18 -0
  20. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +24 -0
  21. package/.next/server/app/_global-error.segments/_tree.segment.rsc +3 -0
  22. package/.next/server/app/_not-found/page.js +1 -0
  23. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  24. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  25. package/.next/server/app/_not-found.html +1 -0
  26. package/.next/server/app/_not-found.meta +35 -0
  27. package/.next/server/app/_not-found.rsc +24 -0
  28. package/.next/server/app/_not-found.segments/_full.segment.rsc +24 -0
  29. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +30 -0
  30. package/.next/server/app/_not-found.segments/_tree.segment.rsc +4 -0
  31. package/.next/server/app/administration/page.js +1 -0
  32. package/.next/server/app/administration/page.js.nft.json +1 -0
  33. package/.next/server/app/administration/page_client-reference-manifest.js +1 -0
  34. package/.next/server/app/administration.html +1 -0
  35. package/.next/server/app/administration.meta +34 -0
  36. package/.next/server/app/administration.rsc +29 -0
  37. package/.next/server/app/administration.segments/_full.segment.rsc +29 -0
  38. package/.next/server/app/administration.segments/_tree.segment.rsc +4 -0
  39. package/.next/server/app/administration.segments/administration/__PAGE__.segment.rsc +35 -0
  40. package/.next/server/app/alerts/page.js +1 -0
  41. package/.next/server/app/alerts/page.js.nft.json +1 -0
  42. package/.next/server/app/alerts/page_client-reference-manifest.js +1 -0
  43. package/.next/server/app/alerts.html +1 -0
  44. package/.next/server/app/alerts.meta +34 -0
  45. package/.next/server/app/alerts.rsc +28 -0
  46. package/.next/server/app/alerts.segments/_full.segment.rsc +28 -0
  47. package/.next/server/app/alerts.segments/_tree.segment.rsc +4 -0
  48. package/.next/server/app/alerts.segments/alerts/__PAGE__.segment.rsc +34 -0
  49. package/.next/server/app/connections/page.js +1 -0
  50. package/.next/server/app/connections/page.js.nft.json +1 -0
  51. package/.next/server/app/connections/page_client-reference-manifest.js +1 -0
  52. package/.next/server/app/connections.html +1 -0
  53. package/.next/server/app/connections.meta +34 -0
  54. package/.next/server/app/connections.rsc +29 -0
  55. package/.next/server/app/connections.segments/_full.segment.rsc +29 -0
  56. package/.next/server/app/connections.segments/_tree.segment.rsc +4 -0
  57. package/.next/server/app/connections.segments/connections/__PAGE__.segment.rsc +35 -0
  58. package/.next/server/app/history/page.js +2 -0
  59. package/.next/server/app/history/page.js.nft.json +1 -0
  60. package/.next/server/app/history/page_client-reference-manifest.js +1 -0
  61. package/.next/server/app/history.html +1 -0
  62. package/.next/server/app/history.meta +34 -0
  63. package/.next/server/app/history.rsc +33 -0
  64. package/.next/server/app/history.segments/_full.segment.rsc +33 -0
  65. package/.next/server/app/history.segments/_tree.segment.rsc +4 -0
  66. package/.next/server/app/history.segments/history/__PAGE__.segment.rsc +37 -0
  67. package/.next/server/app/icon.svg/route.js +1 -0
  68. package/.next/server/app/icon.svg/route.js.nft.json +1 -0
  69. package/.next/server/app/icon.svg.body +1 -0
  70. package/.next/server/app/icon.svg.meta +1 -0
  71. package/.next/server/app/index.html +1 -0
  72. package/.next/server/app/index.meta +29 -0
  73. package/.next/server/app/index.rsc +26 -0
  74. package/.next/server/app/index.segments/__PAGE__.segment.rsc +31 -0
  75. package/.next/server/app/index.segments/_full.segment.rsc +26 -0
  76. package/.next/server/app/index.segments/_tree.segment.rsc +3 -0
  77. package/.next/server/app/login/page.js +1 -0
  78. package/.next/server/app/login/page.js.nft.json +1 -0
  79. package/.next/server/app/login/page_client-reference-manifest.js +1 -0
  80. package/.next/server/app/login.html +1 -0
  81. package/.next/server/app/login.meta +34 -0
  82. package/.next/server/app/login.rsc +26 -0
  83. package/.next/server/app/login.segments/_full.segment.rsc +26 -0
  84. package/.next/server/app/login.segments/_tree.segment.rsc +4 -0
  85. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +32 -0
  86. package/.next/server/app/machines/[machineId]/page.js +1 -0
  87. package/.next/server/app/machines/[machineId]/page.js.nft.json +1 -0
  88. package/.next/server/app/machines/[machineId]/page_client-reference-manifest.js +1 -0
  89. package/.next/server/app/page.js +1 -0
  90. package/.next/server/app/page.js.nft.json +1 -0
  91. package/.next/server/app/page_client-reference-manifest.js +1 -0
  92. package/.next/server/app/processes/page.js +2 -0
  93. package/.next/server/app/processes/page.js.nft.json +1 -0
  94. package/.next/server/app/processes/page_client-reference-manifest.js +1 -0
  95. package/.next/server/app/processes.html +1 -0
  96. package/.next/server/app/processes.meta +34 -0
  97. package/.next/server/app/processes.rsc +30 -0
  98. package/.next/server/app/processes.segments/_full.segment.rsc +30 -0
  99. package/.next/server/app/processes.segments/_tree.segment.rsc +4 -0
  100. package/.next/server/app/processes.segments/processes/__PAGE__.segment.rsc +36 -0
  101. package/.next/server/app-paths-manifest.json +13 -0
  102. package/.next/server/chunks/161.js +1 -0
  103. package/.next/server/chunks/168.js +1 -0
  104. package/.next/server/chunks/266.js +68 -0
  105. package/.next/server/chunks/430.js +116 -0
  106. package/.next/server/chunks/721.js +1 -0
  107. package/.next/server/chunks/751.js +1 -0
  108. package/.next/server/chunks/877.js +1 -0
  109. package/.next/server/chunks/894.js +2 -0
  110. package/.next/server/functions-config-manifest.json +4 -0
  111. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  112. package/.next/server/middleware-build-manifest.js +1 -0
  113. package/.next/server/middleware-manifest.json +6 -0
  114. package/.next/server/middleware-react-loadable-manifest.js +1 -0
  115. package/.next/server/next-font-manifest.js +1 -0
  116. package/.next/server/next-font-manifest.json +1 -0
  117. package/.next/server/pages/404.html +1 -0
  118. package/.next/server/pages/500.html +1 -0
  119. package/.next/server/pages-manifest.json +4 -0
  120. package/.next/server/prefetch-hints.json +123 -0
  121. package/.next/server/preview-props.json +5 -0
  122. package/.next/server/server-reference-manifest.js +1 -0
  123. package/.next/server/server-reference-manifest.json +1 -0
  124. package/.next/server/webpack-runtime.js +1 -0
  125. package/.next/static/NE6PGEiJxUnKRceuzUWPc/_buildManifest.js +1 -0
  126. package/.next/static/NE6PGEiJxUnKRceuzUWPc/_ssgManifest.js +1 -0
  127. package/.next/static/chunks/103-c47318242b24e9ac.js +1 -0
  128. package/.next/static/chunks/157-0d36ebf66af048ac.js +1 -0
  129. package/.next/static/chunks/227-417939c480f50747.js +1 -0
  130. package/.next/static/chunks/408-dc6158e0bb450ef6.js +1 -0
  131. package/.next/static/chunks/4bd1b696-c7408084347438ae.js +1 -0
  132. package/.next/static/chunks/794-ceeb732a1a81bc27.js +52 -0
  133. package/.next/static/chunks/939-2600d7c8e583d5ef.js +1 -0
  134. package/.next/static/chunks/app/_global-error/page-aba28f88d5781771.js +1 -0
  135. package/.next/static/chunks/app/_not-found/page-a2a510233e4836e2.js +1 -0
  136. package/.next/static/chunks/app/administration/page-4b16b19bb6a39c36.js +1 -0
  137. package/.next/static/chunks/app/alerts/page-fe0e877778c60af8.js +1 -0
  138. package/.next/static/chunks/app/connections/page-99cd54d6929b3f1f.js +1 -0
  139. package/.next/static/chunks/app/history/page-1475454737279ee0.js +3 -0
  140. package/.next/static/chunks/app/layout-4feff677c4071745.js +1 -0
  141. package/.next/static/chunks/app/login/page-c57e12dbe5146737.js +1 -0
  142. package/.next/static/chunks/app/machines/[machineId]/page-916ee73de5eb067d.js +1 -0
  143. package/.next/static/chunks/app/page-1ae9fc94c897785b.js +1 -0
  144. package/.next/static/chunks/app/processes/page-e471ed23a1d5632f.js +3 -0
  145. package/.next/static/chunks/framework-ea4f97ca039bd777.js +1 -0
  146. package/.next/static/chunks/main-a458d32948147b84.js +5 -0
  147. package/.next/static/chunks/main-app-f92328c8950d2e53.js +1 -0
  148. package/.next/static/chunks/next/dist/client/components/builtin/app-error-aba28f88d5781771.js +1 -0
  149. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-aba28f88d5781771.js +1 -0
  150. package/.next/static/chunks/next/dist/client/components/builtin/global-error-43cbaa3aa1576eb4.js +1 -0
  151. package/.next/static/chunks/next/dist/client/components/builtin/not-found-aba28f88d5781771.js +1 -0
  152. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-aba28f88d5781771.js +1 -0
  153. package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  154. package/.next/static/chunks/webpack-9ca43f4ce1bb8938.js +1 -0
  155. package/.next/static/css/e45c0c7ef5ba0336.css +1 -0
  156. package/ARCHITECTURE.md +279 -0
  157. package/CHANGELOG.md +37 -0
  158. package/DESIGN.md +299 -0
  159. package/LICENSE +21 -0
  160. package/README.md +74 -2
  161. package/SECURITY.md +12 -0
  162. package/THIRD_PARTY_NOTICES.md +8847 -0
  163. package/bin/rigscope.cjs +6 -0
  164. package/dist/cli.cjs +9075 -0
  165. package/dist/collector.cjs +5679 -0
  166. package/dist/hub.cjs +8364 -0
  167. package/dist/ops.cjs +4976 -0
  168. package/dist/system.cjs +4333 -0
  169. package/docs/bottlenecks.md +62 -0
  170. package/docs/configuration.md +85 -0
  171. package/docs/development.md +46 -0
  172. package/docs/exports.md +39 -0
  173. package/docs/installation.md +306 -0
  174. package/docs/monitoring.md +112 -0
  175. package/docs/operations.md +550 -0
  176. package/docs/releasing.md +154 -0
  177. package/docs/scale-acceptance.json +412 -0
  178. package/docs/scale-validation.json +1898 -0
  179. package/docs/scale-validation.md +294 -0
  180. package/docs/stress-2026-11-04/180301-probe-m100-u1-s1-20000ms.json +412 -0
  181. package/docs/stress-2026-11-04/180335-probe-m200-u1-s1-20000ms.json +412 -0
  182. package/docs/stress-2026-11-04/180409-probe-m400-u1-s1-20000ms.json +412 -0
  183. package/docs/stress-2026-11-04/180443-probe-m800-u1-s1-20000ms.json +412 -0
  184. package/docs/stress-2026-11-04/180518-probe-m600-u1-s1-20000ms.json +412 -0
  185. package/docs/stress-2026-11-04/180553-probe-m500-u1-s1-20000ms.json +412 -0
  186. package/docs/stress-2026-11-04/180628-probe-m550-u1-s1-20000ms.json +412 -0
  187. package/docs/stress-2026-11-04/180716-probe-m800-u1-s0-20000ms.json +415 -0
  188. package/docs/stress-2026-11-04/180751-probe-m1600-u1-s0-20000ms.json +415 -0
  189. package/docs/stress-2026-11-04/180828-probe-m3200-u1-s0-20000ms.json +416 -0
  190. package/docs/stress-2026-11-04/180941-probe-m2400-u1-s0-20000ms.json +416 -0
  191. package/docs/stress-2026-11-04/181031-probe-m2000-u1-s0-20000ms.json +416 -0
  192. package/docs/stress-2026-11-04/181114-probe-m1800-u1-s0-20000ms.json +416 -0
  193. package/docs/stress-2026-11-04/181203-probe-m100-u64-s64-20000ms.json +418 -0
  194. package/docs/stress-2026-11-04/181304-probe-m200-u64-s64-20000ms.json +418 -0
  195. package/docs/stress-2026-11-04/181405-probe-m400-u64-s64-20000ms.json +418 -0
  196. package/docs/stress-2026-11-04/181507-probe-m500-u64-s64-20000ms.json +418 -0
  197. package/docs/stress-2026-11-04/181609-probe-m450-u64-s64-20000ms.json +418 -0
  198. package/docs/stress-2026-11-04/181845-probe-m2-u2-s2-3000ms.json +419 -0
  199. package/docs/stress-2026-11-04/181931-probe-m500-u64-s64-30000ms.json +420 -0
  200. package/docs/stress-2026-11-04/182108-probe-m1800-u1-s0-120000ms.json +418 -0
  201. package/docs/stress-2026-11-04/182354-probe-m1200-u1-s0-120000ms.json +418 -0
  202. package/docs/stress-2026-11-04/182625-probe-m900-u1-s0-120000ms.json +418 -0
  203. package/docs/stress-2026-11-04/182847-probe-m1050-u1-s0-120000ms.json +418 -0
  204. package/docs/stress-2026-11-04/183112-probe-m1125-u1-s0-120000ms.json +418 -0
  205. package/docs/stress-2026-11-04/193608-probe-m500-u64-s64-120000ms.json +420 -0
  206. package/docs/stress-2026-11-04/193909-probe-m529-u1-s1-15000ms.json +420 -0
  207. package/docs/stress-2026-11-04/193940-probe-m530-u1-s1-15000ms.json +420 -0
  208. package/docs/stress-2026-11-04/194011-probe-m100-u1-s65-10000ms.json +421 -0
  209. package/docs/stress-2026-11-04/README.md +205 -0
  210. package/docs/stress-2026-11-04/index.jsonl +29 -0
  211. package/docs/stress-2026-11-04/manifest.json +13 -0
  212. package/docs/stress-2026-11-04/results.html +22 -0
  213. package/docs/system-installation.md +195 -0
  214. package/next.config.js +38 -0
  215. package/package.json +117 -4
  216. package/packaging/README.md +5 -0
  217. package/packaging/collector.env.example +24 -0
  218. package/packaging/hub.env.example +31 -0
  219. package/packaging/system/90-rigscope-rapl.rules +7 -0
  220. package/packaging/system/README.md +5 -0
  221. package/packaging/system/rigscope-collector.service.example +53 -0
  222. package/packaging/system/rigscope-hub.service.example +50 -0
  223. package/packaging/system/rigscope-rapl.sysusers +5 -0
  224. package/packaging/system/rigscope.sysusers +7 -0
@@ -0,0 +1,154 @@
1
+ # Releasing Rigscope
2
+
3
+ The first public version, `0.1.0`, is published on
4
+ [npm](https://www.npmjs.com/package/rigscope). Source and issues are on
5
+ [GitHub](https://github.com/Triet1402/rigscope). The package includes MIT licensing,
6
+ dependency notices, the security policy and [release notes](../CHANGELOG.md).
7
+
8
+ The commands below use `0.1.0` as an example. For a new release, substitute its
9
+ new version everywhere; do not attempt to republish `0.1.0`. Documentation edits
10
+ can be committed and pushed to GitHub independently. The published archive and
11
+ its bundled documentation stay unchanged; ship updated package documentation
12
+ with the next version. Keep each release tag on the revision used to build it.
13
+
14
+ ## Recommended release order
15
+
16
+ 1. Update the version, docs, release notes and repository metadata. Commit them.
17
+ 2. Push the source to GitHub and run the manual CI workflow on that revision.
18
+ CI runs manually; pushes and pull requests do not trigger it.
19
+ 3. Build one npm archive from the final revision. Retain it, its SHA-256 checksum
20
+ and verification results. Test and publish that archive without rebuilding it.
21
+ 4. Run the publish dry run, check the npm account, and publish the verified archive.
22
+ 5. Verify the registry version and install it into a disposable test installation.
23
+ Check sign-in, fresh telemetry and the installed command.
24
+ 6. Tag the exact source revision as `v0.1.0`, push the tag, and create a GitHub
25
+ Release with the release notes, verified archive and checksum. Announce it
26
+ after the registry installation works.
27
+
28
+ For subsequent releases, update the package and lockfile versions together
29
+ (for example, `npm version patch --no-git-tag-version`), update the release notes,
30
+ and repeat the same sequence. A published name/version cannot be overwritten,
31
+ so a correction after publication needs a new version. Prereleases should use
32
+ an explicit tag such as `next`; ordinary `rigscope upgrade` follows `latest`.
33
+
34
+ ## Build the final archive
35
+
36
+ Run from the source checkout with Linux and Node.js 26+:
37
+
38
+ ```sh
39
+ npm ci
40
+ npm run typecheck
41
+ mkdir -p out/release
42
+ npm pack --pack-destination out/release
43
+ ```
44
+
45
+ `npm pack` runs `prepack`, which regenerates dependency notices, builds fresh
46
+ runtime/UI artifacts and verifies them. If notice generation changes a tracked
47
+ file, include that change in the release commit before producing the final
48
+ archive. Reuse the archive for every check below. Replace `0.1.0` in these
49
+ commands when releasing another version.
50
+
51
+ ## Verify the exact archive
52
+
53
+ ```sh
54
+ node scripts/verify-package.cjs ./out/release/rigscope-0.1.0.tgz
55
+ npm run verify:installed -- --archive ./out/release/rigscope-0.1.0.tgz
56
+ npm test
57
+ node scripts/test-runtime.cjs
58
+ node scripts/test.cjs --suite development
59
+ ```
60
+
61
+ Package inspection verifies the built CLI/runtime/UI and excludes source tests,
62
+ Git data, private environment files and caches. Inspect the file list for
63
+ unexpected state, databases, credentials or logs. Installed-package verification
64
+ uses production dependencies in a temporary directory, audits the resolved
65
+ dependency tree and checks the CLI version plus authenticated sign-in/History.
66
+
67
+ The following opt-in checks require a working Linux systemd user manager. They
68
+ create disposable installations with separate state, launchers, ports and unit
69
+ names, then remove their services. They do not select an existing installation.
70
+
71
+ ```sh
72
+ RIGSCOPE_TEST_ARCHIVE="$PWD/out/release/rigscope-0.1.0.tgz" \
73
+ node --import tsx --test tests/cli-install.integration.ts
74
+ node scripts/verify-operations.cjs ./out/release/rigscope-0.1.0.tgz
75
+ ```
76
+
77
+ These cover installation, pairing, independent component controls, bootstrap
78
+ removal, retained-data reinstall, upgrade/rollback, failed-start recovery and
79
+ offline password recovery. Runtime tests additionally cover authenticated HTTPS,
80
+ SSE, collector connections, persistence and reconnect behavior in isolated setups.
81
+
82
+ On the reference host with 48 logical cores, two RTX 3090s and an ASPEED adapter,
83
+ run the hardware-specific test explicitly:
84
+
85
+ ```sh
86
+ RIGSCOPE_REAL_HW=1 node --import tsx --test tests/sampling-real.test.ts
87
+ ```
88
+
89
+ That test reads the actual host and is skipped by default. Its exact inventory
90
+ assertions are specific to that rig; it is not a portable hardware test.
91
+
92
+ Record the final checksum after verification:
93
+
94
+ ```sh
95
+ cd out/release
96
+ sha256sum rigscope-0.1.0.tgz > SHA256SUMS
97
+ sha256sum --check SHA256SUMS
98
+ ```
99
+
100
+ Keep the source revision, archive checksum, check results and any skips together.
101
+ Record boot enablement separately from an actual reboot. Physical laptop testing
102
+ and synthetic scale workloads are separate evidence; see
103
+ [hardware support](installation.md#hardware) and [scale validation](scale-validation.md).
104
+
105
+ ## Publish dry run and publication
106
+
107
+ Run these commands from the source checkout. Sign in interactively if needed,
108
+ then verify the account and preview the exact archive:
109
+
110
+ ```sh
111
+ npm login --registry=https://registry.npmjs.org
112
+ npm whoami --registry=https://registry.npmjs.org
113
+ npm publish ./out/release/rigscope-0.1.0.tgz --access public --tag latest \
114
+ --registry=https://registry.npmjs.org --dry-run
115
+ ```
116
+
117
+ The dry run checks the package without publishing. It does not verify permission
118
+ to publish or complete two-factor authentication.
119
+
120
+ When releasing, this command actually publishes the verified archive publicly:
121
+
122
+ ```sh
123
+ npm publish ./out/release/rigscope-0.1.0.tgz --access public --tag latest \
124
+ --registry=https://registry.npmjs.org
125
+ npm view rigscope@0.1.0 version dist.integrity --registry=https://registry.npmjs.org
126
+ ```
127
+
128
+ Then use `npx --yes rigscope@0.1.0 install` with a disposable `--state-dir`, an
129
+ unused `--port` and an isolated `XDG_BIN_HOME` for the registry installation
130
+ check. Uninstall that test installation afterward. Use the same isolation
131
+ settings for all test commands so the check does not select a real installation.
132
+
133
+ The public quick start uses `npx --yes rigscope@latest install --boot` and the
134
+ installed `rigscope upgrade`. Archive commands remain available for downloaded
135
+ releases and testing before publication. Users do not need a global npm
136
+ installation of Rigscope.
137
+
138
+ GitHub Actions trusted publishing can automate later releases. Configure it for
139
+ the actual repository and publishing workflow; the current CI workflow only
140
+ verifies and does not publish. See npm's
141
+ [publishing documentation](https://docs.npmjs.com/cli/commands/npm-publish/) and
142
+ [trusted publishing guide](https://docs.npmjs.com/trusted-publishers/).
143
+
144
+ ## Alternative manifest archives
145
+
146
+ `npm run package:release` creates an operations `.tar.gz` with a hashed manifest.
147
+ It is a separate format from the npm `.tgz`. The low-level installer validates
148
+ size/hash and required artifacts before activation. Production dependencies must
149
+ be installed in the staged release; activation never silently runs npm or service
150
+ commands. See [operations](operations.md#low-level-manifest-archive-workflow) and
151
+ [system installation](system-installation.md).
152
+
153
+ Configuration, database schema and wire-protocol revisions are internal
154
+ compatibility checks, independent of the application package version.
@@ -0,0 +1,412 @@
1
+ {
2
+ "tool": "rigscope-scale-harness",
3
+ "synthetic": true,
4
+ "disclaimer": "Synthetic bounded workload on an isolated temporary hub/database, not a capacity, latency, or hardware guarantee. Values depend on this host, disk, Node version, payload, cadence, viewer count and concurrent activity.",
5
+ "generatedAt": "2026-11-04T00:57:26.376Z",
6
+ "node": "v26.13.0",
7
+ "host": {
8
+ "platform": "linux",
9
+ "release": "7.3.0-9-generic",
10
+ "arch": "x64",
11
+ "cpuModel": "AMD EPYC 7402P 24-Core Processor",
12
+ "logicalCores": 48,
13
+ "totalMemoryBytes": 130351214592,
14
+ "loadAverage": [
15
+ 0.56,
16
+ 0.78,
17
+ 0.79
18
+ ],
19
+ "node": "v26.13.0"
20
+ },
21
+ "command": "npx tsx scripts/scale-harness.ts --case fleet-75m-churn --machines 75 --viewers 8 --sse-clients 4 --churn-machines 15 --churn-every-ms 3000 --duration-ms 8000 --json-out docs/scale-acceptance.json",
22
+ "suite": [
23
+ {
24
+ "name": "fleet-75m-churn",
25
+ "description": "75 machines with collector reconnect/replay churn and SSE reconnect/reset churn.",
26
+ "machines": 75,
27
+ "durationMs": 8000,
28
+ "sampleIntervalMs": 1000,
29
+ "recordsPerBatch": 1,
30
+ "viewers": 8,
31
+ "sseClients": 4,
32
+ "slowSseClients": 0,
33
+ "slowSsePauseAfterMs": 0,
34
+ "slowSsePauseMs": 0,
35
+ "historyIntervalMs": 1000,
36
+ "historyMetricSet": "standard",
37
+ "churnEveryMs": 3000,
38
+ "churnMachines": 15,
39
+ "replayRecords": 20
40
+ }
41
+ ],
42
+ "cases": [
43
+ {
44
+ "name": "fleet-75m-churn",
45
+ "description": "75 machines with collector reconnect/replay churn and SSE reconnect/reset churn.",
46
+ "config": {
47
+ "name": "fleet-75m-churn",
48
+ "description": "75 machines with collector reconnect/replay churn and SSE reconnect/reset churn.",
49
+ "machines": 75,
50
+ "durationMs": 8000,
51
+ "sampleIntervalMs": 1000,
52
+ "recordsPerBatch": 1,
53
+ "viewers": 8,
54
+ "sseClients": 4,
55
+ "slowSseClients": 0,
56
+ "slowSsePauseAfterMs": 0,
57
+ "slowSsePauseMs": 0,
58
+ "historyIntervalMs": 1000,
59
+ "historyMetricSet": "standard",
60
+ "churnEveryMs": 3000,
61
+ "churnMachines": 15,
62
+ "replayRecords": 20,
63
+ "warmupMs": 1500,
64
+ "maxCollectorConnections": 100,
65
+ "databaseBudgetBytes": 536870912
66
+ },
67
+ "host": {
68
+ "platform": "linux",
69
+ "release": "7.3.0-9-generic",
70
+ "arch": "x64",
71
+ "cpuModel": "AMD EPYC 7402P 24-Core Processor",
72
+ "logicalCores": 48,
73
+ "totalMemoryBytes": 130351214592,
74
+ "loadAverage": [
75
+ 0.56,
76
+ 0.78,
77
+ 0.79
78
+ ],
79
+ "node": "v26.13.0"
80
+ },
81
+ "authorization": {
82
+ "anonymousDenied": true,
83
+ "scopedHistory": true,
84
+ "scopedSnapshot": true,
85
+ "scopedReset": true
86
+ },
87
+ "ingestion": {
88
+ "recordsSent": 930,
89
+ "recordsAccepted": 930,
90
+ "recordsRejected": 0,
91
+ "replayRecordsSent": 600,
92
+ "replayRecordsAccepted": 600,
93
+ "retryAcks": 0,
94
+ "unackedLiveRecords": 0,
95
+ "sendErrors": 0,
96
+ "ackLatencyMs": {
97
+ "count": 930,
98
+ "min": 9.86,
99
+ "avg": 37.89,
100
+ "p50": 33.83,
101
+ "p95": 63.44,
102
+ "p99": 114.87,
103
+ "max": 152.07
104
+ },
105
+ "sampleToCommitLagMs": {
106
+ "count": 930,
107
+ "min": 10,
108
+ "avg": 37.91,
109
+ "p50": 34,
110
+ "p95": 63,
111
+ "p99": 115,
112
+ "max": 152
113
+ },
114
+ "messagesSent": 1065,
115
+ "backpressureSkips": 0,
116
+ "connects": 105,
117
+ "reconnects": 30,
118
+ "reconnectFailures": 0,
119
+ "collectorRecoveryMs": {
120
+ "count": 30,
121
+ "min": 65.97,
122
+ "avg": 124.94,
123
+ "p50": 130.34,
124
+ "p95": 182.2,
125
+ "p99": 185.7,
126
+ "max": 185.7
127
+ }
128
+ },
129
+ "history": {
130
+ "queries": 69,
131
+ "ok": 69,
132
+ "rateLimited": 0,
133
+ "errors": 0,
134
+ "latencyMs": {
135
+ "count": 69,
136
+ "min": 3.88,
137
+ "avg": 11.5,
138
+ "p50": 9.6,
139
+ "p95": 26.22,
140
+ "p99": 30.96,
141
+ "max": 30.96
142
+ },
143
+ "series": {
144
+ "count": 69,
145
+ "min": 4,
146
+ "avg": 4,
147
+ "p50": 4,
148
+ "p95": 4,
149
+ "p99": 4,
150
+ "max": 4
151
+ },
152
+ "points": {
153
+ "count": 69,
154
+ "min": 8,
155
+ "avg": 32.87,
156
+ "p50": 24,
157
+ "p95": 92,
158
+ "p99": 100,
159
+ "max": 100
160
+ },
161
+ "truncated": 0,
162
+ "tiers": {
163
+ "raw": 69
164
+ }
165
+ },
166
+ "stream": {
167
+ "clients": 4,
168
+ "slowClients": 0,
169
+ "events": 52,
170
+ "snapshots": 4,
171
+ "updates": 40,
172
+ "resets": 8,
173
+ "bytes": 7695647,
174
+ "eventLagMs": {
175
+ "count": 52,
176
+ "min": 108,
177
+ "avg": 315.27,
178
+ "p50": 162,
179
+ "p95": 842,
180
+ "p99": 848,
181
+ "max": 848
182
+ },
183
+ "normalEventLagMs": {
184
+ "count": 52,
185
+ "min": 108,
186
+ "avg": 315.27,
187
+ "p50": 162,
188
+ "p95": 842,
189
+ "p99": 848,
190
+ "max": 848
191
+ },
192
+ "slowEventLagMs": {
193
+ "count": 0,
194
+ "min": 0,
195
+ "avg": 0,
196
+ "p50": 0,
197
+ "p95": 0,
198
+ "p99": 0,
199
+ "max": 0
200
+ },
201
+ "firstEventMs": {
202
+ "count": 12,
203
+ "min": 4.69,
204
+ "avg": 10.82,
205
+ "p50": 10.29,
206
+ "p95": 22.7,
207
+ "p99": 22.7,
208
+ "max": 22.7
209
+ },
210
+ "reconnects": 8,
211
+ "resetsAfterReconnect": 8,
212
+ "slowPauses": 0,
213
+ "slowDrops": 0,
214
+ "sseRecoveryMs": {
215
+ "count": 8,
216
+ "min": 4.7,
217
+ "avg": 7.93,
218
+ "p50": 8.14,
219
+ "p95": 12.54,
220
+ "p99": 12.54,
221
+ "max": 12.54
222
+ }
223
+ },
224
+ "hub": {
225
+ "baseline": {
226
+ "pid": 4068661,
227
+ "uptimeMs": 2411.99,
228
+ "hubRunId": "590bcb13-33c0-4678-bad4-c4e6151e4c15",
229
+ "eventLoopDelayMs": {
230
+ "p50": 10.11,
231
+ "p95": 11.13,
232
+ "p99": 28.85,
233
+ "max": 44.53
234
+ },
235
+ "eventLoopUtilization": 0.1224,
236
+ "cpuPercent": 37,
237
+ "cpuMs": {
238
+ "user": 726.22,
239
+ "system": 101.19
240
+ },
241
+ "rssBytes": 159789056,
242
+ "rssPeakBytes": 159789056,
243
+ "heapUsedBytes": 19570600,
244
+ "database": {
245
+ "bytes": 2746680,
246
+ "walBytes": 2509112,
247
+ "mainBytes": 237568
248
+ },
249
+ "store": {
250
+ "queuedRecords": 0,
251
+ "queuedBatches": 0,
252
+ "lastFlushMs": 1793753832716,
253
+ "lastFlushDurationMs": 2,
254
+ "committedRecords": 150,
255
+ "duplicateRecords": 0,
256
+ "rejectedRecords": 0,
257
+ "writeFailures": 0,
258
+ "lastWriteError": null,
259
+ "diskFullFailures": 0,
260
+ "dbBytes": 2746680,
261
+ "dbWalBytes": 2509112,
262
+ "dbBytesLimit": 536870912,
263
+ "retention": {
264
+ "lastRunAtMs": 0,
265
+ "lastRunDurationMs": 0,
266
+ "deletedRows": 0,
267
+ "pressure": false,
268
+ "effectiveRawCutoffMs": null,
269
+ "physicalBytes": 2746680,
270
+ "physicalLimitBytes": 536870912,
271
+ "physicalPressure": false,
272
+ "walBytes": 2509112
273
+ }
274
+ },
275
+ "liveClients": 0,
276
+ "sessions": 9,
277
+ "connectedMachines": 75,
278
+ "configuredMachines": 75
279
+ },
280
+ "window": {
281
+ "pid": 4068661,
282
+ "uptimeMs": 10431.17,
283
+ "hubRunId": "590bcb13-33c0-4678-bad4-c4e6151e4c15",
284
+ "eventLoopDelayMs": {
285
+ "p50": 10.11,
286
+ "p95": 16.25,
287
+ "p99": 34.18,
288
+ "max": 38.34
289
+ },
290
+ "eventLoopUtilization": 0.1453,
291
+ "cpuPercent": 17.3,
292
+ "cpuMs": {
293
+ "user": 1228.81,
294
+ "system": 157.98
295
+ },
296
+ "rssBytes": 179965952,
297
+ "rssPeakBytes": 179965952,
298
+ "heapUsedBytes": 17027672,
299
+ "database": {
300
+ "bytes": 7939784,
301
+ "walBytes": 4511432,
302
+ "mainBytes": 3428352
303
+ },
304
+ "store": {
305
+ "queuedRecords": 0,
306
+ "queuedBatches": 0,
307
+ "lastFlushMs": 1793753840710,
308
+ "lastFlushDurationMs": 24,
309
+ "committedRecords": 1380,
310
+ "duplicateRecords": 0,
311
+ "rejectedRecords": 0,
312
+ "writeFailures": 0,
313
+ "lastWriteError": null,
314
+ "diskFullFailures": 0,
315
+ "dbBytes": 7939784,
316
+ "dbWalBytes": 4511432,
317
+ "dbBytesLimit": 536870912,
318
+ "retention": {
319
+ "lastRunAtMs": 0,
320
+ "lastRunDurationMs": 0,
321
+ "deletedRows": 0,
322
+ "pressure": false,
323
+ "effectiveRawCutoffMs": null,
324
+ "physicalBytes": 7939784,
325
+ "physicalLimitBytes": 536870912,
326
+ "physicalPressure": false,
327
+ "walBytes": 4511432
328
+ }
329
+ },
330
+ "liveClients": 4,
331
+ "sessions": 9,
332
+ "connectedMachines": 75,
333
+ "configuredMachines": 75
334
+ },
335
+ "final": {
336
+ "pid": 4068661,
337
+ "uptimeMs": 13434.61,
338
+ "hubRunId": "590bcb13-33c0-4678-bad4-c4e6151e4c15",
339
+ "eventLoopDelayMs": {
340
+ "p50": 10.11,
341
+ "p95": 10.6,
342
+ "p99": 24.99,
343
+ "max": 38.24
344
+ },
345
+ "eventLoopUtilization": 0.0646,
346
+ "cpuPercent": 8.37,
347
+ "cpuMs": {
348
+ "user": 208.87,
349
+ "system": 42.59
350
+ },
351
+ "rssBytes": 181145600,
352
+ "rssPeakBytes": 181145600,
353
+ "heapUsedBytes": 20405600,
354
+ "database": {
355
+ "bytes": 8095528,
356
+ "walBytes": 4527912,
357
+ "mainBytes": 3567616
358
+ },
359
+ "store": {
360
+ "queuedRecords": 0,
361
+ "queuedBatches": 0,
362
+ "lastFlushMs": 1793753842709,
363
+ "lastFlushDurationMs": 23,
364
+ "committedRecords": 1530,
365
+ "duplicateRecords": 0,
366
+ "rejectedRecords": 0,
367
+ "writeFailures": 0,
368
+ "lastWriteError": null,
369
+ "diskFullFailures": 0,
370
+ "dbBytes": 8095528,
371
+ "dbWalBytes": 4527912,
372
+ "dbBytesLimit": 536870912,
373
+ "retention": {
374
+ "lastRunAtMs": 0,
375
+ "lastRunDurationMs": 0,
376
+ "deletedRows": 0,
377
+ "pressure": false,
378
+ "effectiveRawCutoffMs": null,
379
+ "physicalBytes": 8095528,
380
+ "physicalLimitBytes": 536870912,
381
+ "physicalPressure": false,
382
+ "walBytes": 4527912
383
+ }
384
+ },
385
+ "liveClients": 4,
386
+ "sessions": 9,
387
+ "connectedMachines": 75,
388
+ "configuredMachines": 75
389
+ },
390
+ "databaseGrowth": {
391
+ "bytes": 5348848,
392
+ "walBytes": 2018800,
393
+ "mainBytes": 3330048
394
+ }
395
+ },
396
+ "elapsedMs": 8006.82,
397
+ "artifacts": {
398
+ "directory": "/tmp/rigscope-scale-gc3kVr",
399
+ "kept": false,
400
+ "configPath": "/tmp/rigscope-scale-gc3kVr/hub-config.json",
401
+ "databasePath": "/tmp/rigscope-scale-gc3kVr/scale.sqlite"
402
+ }
403
+ }
404
+ ],
405
+ "limitations": [
406
+ "Collector payload values and cadence are synthetic; the hub, database, auth, SSE and History paths are real modules.",
407
+ "The hub child and all load generators share one host; the hub event loop is single-threaded, while load processes can use other cores. This is not distributed load.",
408
+ "History queries are synchronous on the hub event loop, so 'concurrent' viewers model overlapping HTTP requests, not parallel SQLite readers.",
409
+ "The measurement never contacts the live service; it uses a fresh temporary database and loopback ephemeral port.",
410
+ "A passing case is evidence for this host and workload, not an unlimited-capacity or latency guarantee."
411
+ ]
412
+ }