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,29 @@
1
+ {"pass": true, "reasons": [], "machines": 100, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 100.0, "sampleCadenceRatio": 0.9998, "historyCadenceRatio": 0.9998, "ackP95Ms": 61.13, "historyP95Ms": 11.29, "eventLoopP99Ms": 34.28, "sseNewestSampleP95Ms": 405, "hubCpuPercentOneCore": 12.69, "hubRssPeakMiB": 131.3, "sseMegabytes": 4.5, "report": "docs/stress-2026-11-04/180301-probe-m100-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180301-probe-m100-u1-s1-20000ms.resources.json", "wallSeconds": 34.1, "minimumAvailableMemoryGiB": 109.4, "peakHostCpuPercent": 6.15, "peakTestRssMiB": 274.1, "swapOutDeltaBytes": 0}
2
+ {"pass": true, "reasons": [], "machines": 200, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 199.9, "sampleCadenceRatio": 0.9997, "historyCadenceRatio": 0.9997, "ackP95Ms": 63.33, "historyP95Ms": 13.55, "eventLoopP99Ms": 44.73, "sseNewestSampleP95Ms": 266, "hubCpuPercentOneCore": 20.42, "hubRssPeakMiB": 153.5, "sseMegabytes": 9.1, "report": "docs/stress-2026-11-04/180335-probe-m200-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180335-probe-m200-u1-s1-20000ms.resources.json", "wallSeconds": 34.1, "minimumAvailableMemoryGiB": 109.8, "peakHostCpuPercent": 3.94, "peakTestRssMiB": 272.3, "swapOutDeltaBytes": 0}
3
+ {"pass": true, "reasons": [], "machines": 400, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 399.1, "sampleCadenceRatio": 0.9978, "historyCadenceRatio": 1.0498, "ackP95Ms": 56.62, "historyP95Ms": 30.29, "eventLoopP99Ms": 45.38, "sseNewestSampleP95Ms": 79, "hubCpuPercentOneCore": 34.98, "hubRssPeakMiB": 192.4, "sseMegabytes": 18.2, "report": "docs/stress-2026-11-04/180409-probe-m400-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180409-probe-m400-u1-s1-20000ms.resources.json", "wallSeconds": 34.1, "minimumAvailableMemoryGiB": 109.7, "peakHostCpuPercent": 4.98, "peakTestRssMiB": 366.0, "swapOutDeltaBytes": 0}
4
+ {"pass": false, "reasons": ["SSE admission/delivery incomplete (0/1)"], "machines": 800, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 799.8, "sampleCadenceRatio": 0.9998, "historyCadenceRatio": 1.0498, "ackP95Ms": 53.04, "historyP95Ms": 5.26, "eventLoopP99Ms": 48.37, "sseNewestSampleP95Ms": 133, "hubCpuPercentOneCore": 60.76, "hubRssPeakMiB": 237.6, "sseMegabytes": 1.6, "report": "docs/stress-2026-11-04/180443-probe-m800-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180443-probe-m800-u1-s1-20000ms.resources.json", "wallSeconds": 35.1, "minimumAvailableMemoryGiB": 109.5, "peakHostCpuPercent": 4.76, "peakTestRssMiB": 382.2, "swapOutDeltaBytes": 0}
5
+ {"pass": false, "reasons": ["SSE admission/delivery incomplete (0/1)"], "machines": 600, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 599.8, "sampleCadenceRatio": 0.9996, "historyCadenceRatio": 1.0498, "ackP95Ms": 54.43, "historyP95Ms": 14.49, "eventLoopP99Ms": 46.2, "sseNewestSampleP95Ms": 71, "hubCpuPercentOneCore": 49.28, "hubRssPeakMiB": 214.1, "sseMegabytes": 1.2, "report": "docs/stress-2026-11-04/180518-probe-m600-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180518-probe-m600-u1-s1-20000ms.resources.json", "wallSeconds": 35.1, "minimumAvailableMemoryGiB": 109.6, "peakHostCpuPercent": 4.08, "peakTestRssMiB": 351.9, "swapOutDeltaBytes": 0}
6
+ {"pass": true, "reasons": [], "machines": 500, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 499.9, "sampleCadenceRatio": 0.9997, "historyCadenceRatio": 1.0497, "ackP95Ms": 55.22, "historyP95Ms": 22.71, "eventLoopP99Ms": 56.03, "sseNewestSampleP95Ms": 149, "hubCpuPercentOneCore": 43.76, "hubRssPeakMiB": 212.6, "sseMegabytes": 37.6, "report": "docs/stress-2026-11-04/180553-probe-m500-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180553-probe-m500-u1-s1-20000ms.resources.json", "wallSeconds": 35.1, "minimumAvailableMemoryGiB": 109.6, "peakHostCpuPercent": 4.1, "peakTestRssMiB": 384.7, "swapOutDeltaBytes": 0}
7
+ {"pass": false, "reasons": ["SSE admission/delivery incomplete (0/1)"], "machines": 550, "users": 1, "sseClients": 1, "durationMs": 20000, "committedPerSecond": 549.8, "sampleCadenceRatio": 0.9997, "historyCadenceRatio": 1.0498, "ackP95Ms": 57.6, "historyP95Ms": 6.43, "eventLoopP99Ms": 50.23, "sseNewestSampleP95Ms": 69, "hubCpuPercentOneCore": 46.03, "hubRssPeakMiB": 184.0, "sseMegabytes": 1.1, "report": "docs/stress-2026-11-04/180628-probe-m550-u1-s1-20000ms.json", "resources": "docs/stress-2026-11-04/180628-probe-m550-u1-s1-20000ms.resources.json", "wallSeconds": 35.1, "minimumAvailableMemoryGiB": 109.7, "peakHostCpuPercent": 4.14, "peakTestRssMiB": 322.7, "swapOutDeltaBytes": 0}
8
+ {"pass": true, "reasons": [], "machines": 800, "users": 1, "sseClients": 0, "durationMs": 20000, "committedPerSecond": 799.9, "sampleCadenceRatio": 0.9999, "historyCadenceRatio": 1.0499, "ackP95Ms": 53.46, "historyP95Ms": 10.13, "eventLoopP99Ms": 46.33, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 61.88, "hubRssPeakMiB": 217.0, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/180716-probe-m800-u1-s0-20000ms.json", "resources": "docs/stress-2026-11-04/180716-probe-m800-u1-s0-20000ms.resources.json", "wallSeconds": 35.1, "minimumAvailableMemoryGiB": 109.6, "peakHostCpuPercent": 4.45, "peakTestRssMiB": 383.4, "swapOutDeltaBytes": 0}
9
+ {"pass": true, "reasons": [], "machines": 1600, "users": 1, "sseClients": 0, "durationMs": 20000, "committedPerSecond": 1605.4, "sampleCadenceRatio": 1.0034, "historyCadenceRatio": 1.0499, "ackP95Ms": 59.54, "historyP95Ms": 19.92, "eventLoopP99Ms": 40.99, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 95.33, "hubRssPeakMiB": 279.9, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/180751-probe-m1600-u1-s0-20000ms.json", "resources": "docs/stress-2026-11-04/180751-probe-m1600-u1-s0-20000ms.resources.json", "wallSeconds": 37.1, "minimumAvailableMemoryGiB": 109.5, "peakHostCpuPercent": 4.41, "peakTestRssMiB": 445.5, "swapOutDeltaBytes": 0}
10
+ {"pass": false, "reasons": ["ack p95 3464.84 > 500 ms", "durable sample cadence 37.1% < 95%", "collector rejection, retry, loss, backpressure or send error", "storage queue, write failure or physical pressure"], "machines": 3200, "users": 1, "sseClients": 0, "durationMs": 20000, "committedPerSecond": 1188.7, "sampleCadenceRatio": 0.3715, "historyCadenceRatio": 1.0499, "ackP95Ms": 3464.84, "historyP95Ms": 33.87, "eventLoopP99Ms": 37.98, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 110.71, "hubRssPeakMiB": 401.6, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/180828-probe-m3200-u1-s0-20000ms.json", "resources": "docs/stress-2026-11-04/180828-probe-m3200-u1-s0-20000ms.resources.json", "wallSeconds": 72.3, "minimumAvailableMemoryGiB": 109.2, "peakHostCpuPercent": 5.59, "peakTestRssMiB": 653.5, "swapOutDeltaBytes": 0}
11
+ {"pass": false, "reasons": ["ack p95 2897.57 > 500 ms", "durable sample cadence 61.6% < 95%", "collector rejection, retry, loss, backpressure or send error", "storage queue, write failure or physical pressure"], "machines": 2400, "users": 1, "sseClients": 0, "durationMs": 20000, "committedPerSecond": 1478.3, "sampleCadenceRatio": 0.616, "historyCadenceRatio": 1.0499, "ackP95Ms": 2897.57, "historyP95Ms": 27.21, "eventLoopP99Ms": 31.46, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 110.85, "hubRssPeakMiB": 339.8, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/180941-probe-m2400-u1-s0-20000ms.json", "resources": "docs/stress-2026-11-04/180941-probe-m2400-u1-s0-20000ms.resources.json", "wallSeconds": 50.6, "minimumAvailableMemoryGiB": 109.3, "peakHostCpuPercent": 4.9, "peakTestRssMiB": 543.2, "swapOutDeltaBytes": 0}
12
+ {"pass": false, "reasons": ["ack p95 2524.02 > 500 ms", "durable sample cadence 87.4% < 95%", "collector rejection, retry, loss, backpressure or send error", "storage queue, write failure or physical pressure"], "machines": 2000, "users": 1, "sseClients": 0, "durationMs": 20000, "committedPerSecond": 1747.1, "sampleCadenceRatio": 0.8736, "historyCadenceRatio": 1.05, "ackP95Ms": 2524.02, "historyP95Ms": 20.9, "eventLoopP99Ms": 24.85, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 111.43, "hubRssPeakMiB": 328.9, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/181031-probe-m2000-u1-s0-20000ms.json", "resources": "docs/stress-2026-11-04/181031-probe-m2000-u1-s0-20000ms.resources.json", "wallSeconds": 42.3, "minimumAvailableMemoryGiB": 109.4, "peakHostCpuPercent": 5.37, "peakTestRssMiB": 548.2, "swapOutDeltaBytes": 0}
13
+ {"pass": true, "reasons": [], "machines": 1800, "users": 1, "sseClients": 0, "durationMs": 20000, "committedPerSecond": 1802.1, "sampleCadenceRatio": 1.0012, "historyCadenceRatio": 1.0499, "ackP95Ms": 88.45, "historyP95Ms": 23.84, "eventLoopP99Ms": 31.03, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 103.43, "hubRssPeakMiB": 291.2, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/181114-probe-m1800-u1-s0-20000ms.json", "resources": "docs/stress-2026-11-04/181114-probe-m1800-u1-s0-20000ms.resources.json", "wallSeconds": 38.2, "minimumAvailableMemoryGiB": 109.4, "peakHostCpuPercent": 4.04, "peakTestRssMiB": 469.2, "swapOutDeltaBytes": 0}
14
+ {"pass": true, "reasons": [], "machines": 100, "users": 64, "sseClients": 64, "durationMs": 20000, "committedPerSecond": 99.8, "sampleCadenceRatio": 0.998, "historyCadenceRatio": 1.0472, "ackP95Ms": 60.85, "historyP95Ms": 78.43, "eventLoopP99Ms": 44.89, "sseNewestSampleP95Ms": 544, "hubCpuPercentOneCore": 23.82, "hubRssPeakMiB": 288.0, "sseMegabytes": 290.5, "report": "docs/stress-2026-11-04/181203-probe-m100-u64-s64-20000ms.json", "resources": "docs/stress-2026-11-04/181203-probe-m100-u64-s64-20000ms.resources.json", "wallSeconds": 60.9, "minimumAvailableMemoryGiB": 109.4, "peakHostCpuPercent": 3.36, "peakTestRssMiB": 525.8, "swapOutDeltaBytes": 0}
15
+ {"pass": true, "reasons": [], "machines": 200, "users": 64, "sseClients": 64, "durationMs": 20000, "committedPerSecond": 199.6, "sampleCadenceRatio": 0.9982, "historyCadenceRatio": 1.0091, "ackP95Ms": 69.06, "historyP95Ms": 211.65, "eventLoopP99Ms": 61.31, "sseNewestSampleP95Ms": 764, "hubCpuPercentOneCore": 33.47, "hubRssPeakMiB": 298.1, "sseMegabytes": 582.1, "report": "docs/stress-2026-11-04/181304-probe-m200-u64-s64-20000ms.json", "resources": "docs/stress-2026-11-04/181304-probe-m200-u64-s64-20000ms.resources.json", "wallSeconds": 61.0, "minimumAvailableMemoryGiB": 109.4, "peakHostCpuPercent": 3.31, "peakTestRssMiB": 588.6, "swapOutDeltaBytes": 0}
16
+ {"pass": true, "reasons": [], "machines": 400, "users": 64, "sseClients": 64, "durationMs": 20000, "committedPerSecond": 381.0, "sampleCadenceRatio": 0.9526, "historyCadenceRatio": 0.9425, "ackP95Ms": 407.39, "historyP95Ms": 493.21, "eventLoopP99Ms": 96.4, "sseNewestSampleP95Ms": 963, "hubCpuPercentOneCore": 48.46, "hubRssPeakMiB": 363.2, "sseMegabytes": 1266.4, "report": "docs/stress-2026-11-04/181405-probe-m400-u64-s64-20000ms.json", "resources": "docs/stress-2026-11-04/181405-probe-m400-u64-s64-20000ms.resources.json", "wallSeconds": 62.0, "minimumAvailableMemoryGiB": 109.2, "peakHostCpuPercent": 4.64, "peakTestRssMiB": 840.1, "swapOutDeltaBytes": 0}
17
+ {"pass": false, "reasons": ["ack p95 533.47 > 500 ms", "durable sample cadence 88.7% < 95%", "History cadence 89.9% < 90%"], "machines": 500, "users": 64, "sseClients": 64, "durationMs": 20000, "committedPerSecond": 443.6, "sampleCadenceRatio": 0.8872, "historyCadenceRatio": 0.8987, "ackP95Ms": 533.47, "historyP95Ms": 585.44, "eventLoopP99Ms": 94.24, "sseNewestSampleP95Ms": 1174, "hubCpuPercentOneCore": 49.77, "hubRssPeakMiB": 452.7, "sseMegabytes": 1393.3, "report": "docs/stress-2026-11-04/181507-probe-m500-u64-s64-20000ms.json", "resources": "docs/stress-2026-11-04/181507-probe-m500-u64-s64-20000ms.resources.json", "wallSeconds": 62.0, "minimumAvailableMemoryGiB": 108.8, "peakHostCpuPercent": 4.8, "peakTestRssMiB": 966.3, "swapOutDeltaBytes": 0}
18
+ {"pass": false, "reasons": ["ack p95 560.42 > 500 ms", "durable sample cadence 87.7% < 95%", "History cadence 88.9% < 90%"], "machines": 450, "users": 64, "sseClients": 64, "durationMs": 20000, "committedPerSecond": 394.8, "sampleCadenceRatio": 0.8774, "historyCadenceRatio": 0.8888, "ackP95Ms": 560.42, "historyP95Ms": 541.9, "eventLoopP99Ms": 91.95, "sseNewestSampleP95Ms": 1060, "hubCpuPercentOneCore": 49.71, "hubRssPeakMiB": 369.7, "sseMegabytes": 1310.9, "report": "docs/stress-2026-11-04/181609-probe-m450-u64-s64-20000ms.json", "resources": "docs/stress-2026-11-04/181609-probe-m450-u64-s64-20000ms.resources.json", "wallSeconds": 62.0, "minimumAvailableMemoryGiB": 109.0, "peakHostCpuPercent": 4.33, "peakTestRssMiB": 742.5, "swapOutDeltaBytes": 0}
19
+ {"pass": false, "reasons": ["durable sample cadence 92.8% < 95%"], "machines": 2, "users": 2, "sseClients": 2, "durationMs": 3000, "committedPerSecond": 1.9, "sampleCadenceRatio": 0.9281, "historyCadenceRatio": 1.2375, "ackP95Ms": 31.04, "historyP95Ms": 5.89, "eventLoopP99Ms": 11.32, "sseNewestSampleP95Ms": 848, "hubCpuPercentOneCore": 2.73, "hubRssPeakMiB": 115.9, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/181845-probe-m2-u2-s2-3000ms.json", "resources": "docs/stress-2026-11-04/181845-probe-m2-u2-s2-3000ms.resources.json", "wallSeconds": 17.5, "minimumAvailableMemoryGiB": 109.7, "peakHostCpuPercent": 3.33, "peakTestRssMiB": 403.5, "swapOutDeltaBytes": 0}
20
+ {"pass": true, "reasons": [], "machines": 500, "users": 64, "sseClients": 64, "durationMs": 30000, "committedPerSecond": 500.2, "sampleCadenceRatio": 1.0003, "historyCadenceRatio": 1.0325, "ackP95Ms": 172.35, "historyP95Ms": 154.04, "eventLoopP99Ms": 92.6, "sseNewestSampleP95Ms": 384, "hubCpuPercentOneCore": 66.11, "hubRssPeakMiB": 430.0, "sseMegabytes": 3187.7, "report": "docs/stress-2026-11-04/181931-probe-m500-u64-s64-30000ms.json", "resources": "docs/stress-2026-11-04/181931-probe-m500-u64-s64-30000ms.resources.json", "wallSeconds": 73.3, "minimumAvailableMemoryGiB": 108.4, "peakHostCpuPercent": 9.69, "peakTestRssMiB": 1349.9, "swapOutDeltaBytes": 0}
21
+ {"pass": false, "reasons": ["ack p95 3872.03 > 500 ms", "durable sample cadence 74.4% < 95%", "collector rejection, retry, loss, backpressure or send error", "storage queue, write failure or physical pressure"], "machines": 1800, "users": 1, "sseClients": 0, "durationMs": 120000, "committedPerSecond": 1338.8, "sampleCadenceRatio": 0.7438, "historyCadenceRatio": 1.0083, "ackP95Ms": 3872.03, "historyP95Ms": 34.32, "eventLoopP99Ms": 37.75, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 108.35, "hubRssPeakMiB": 311.0, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/182108-probe-m1800-u1-s0-120000ms.json", "resources": "docs/stress-2026-11-04/182108-probe-m1800-u1-s0-120000ms.resources.json", "wallSeconds": 138.4, "minimumAvailableMemoryGiB": 109.2, "peakHostCpuPercent": 5.16, "peakTestRssMiB": 508.7, "swapOutDeltaBytes": 0}
22
+ {"pass": false, "reasons": ["ack p95 2154.32 > 500 ms", "collector rejection, retry, loss, backpressure or send error", "storage queue, write failure or physical pressure"], "machines": 1200, "users": 1, "sseClients": 0, "durationMs": 120000, "committedPerSecond": 1167.0, "sampleCadenceRatio": 0.9725, "historyCadenceRatio": 1.0083, "ackP95Ms": 2154.32, "historyP95Ms": 29.82, "eventLoopP99Ms": 49.51, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 92.41, "hubRssPeakMiB": 262.4, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/182354-probe-m1200-u1-s0-120000ms.json", "resources": "docs/stress-2026-11-04/182354-probe-m1200-u1-s0-120000ms.resources.json", "wallSeconds": 136.4, "minimumAvailableMemoryGiB": 109.3, "peakHostCpuPercent": 4.22, "peakTestRssMiB": 440.1, "swapOutDeltaBytes": 0}
23
+ {"pass": true, "reasons": [], "machines": 900, "users": 1, "sseClients": 0, "durationMs": 120000, "committedPerSecond": 900.0, "sampleCadenceRatio": 0.9999, "historyCadenceRatio": 1.0083, "ackP95Ms": 71.98, "historyP95Ms": 27.25, "eventLoopP99Ms": 60.85, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 75.13, "hubRssPeakMiB": 225.0, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/182625-probe-m900-u1-s0-120000ms.json", "resources": "docs/stress-2026-11-04/182625-probe-m900-u1-s0-120000ms.resources.json", "wallSeconds": 135.3, "minimumAvailableMemoryGiB": 109.4, "peakHostCpuPercent": 6.34, "peakTestRssMiB": 371.3, "swapOutDeltaBytes": 0}
24
+ {"pass": true, "reasons": [], "machines": 1050, "users": 1, "sseClients": 0, "durationMs": 120000, "committedPerSecond": 1046.0, "sseWorkers": 4, "clientWarmupMs": 0, "historyMetricSet": "standard", "historyIntervalMs": 1000, "sampleCadenceRatio": 0.9962, "historyCadenceRatio": 1.0083, "ackP95Ms": 140.06, "historyP95Ms": 27.62, "eventLoopP99Ms": 54.85, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 82.24, "hubRssPeakMiB": 260.2, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/182847-probe-m1050-u1-s0-120000ms.json", "resources": "docs/stress-2026-11-04/182847-probe-m1050-u1-s0-120000ms.resources.json", "wallSeconds": 135.3, "minimumAvailableMemoryGiB": 109.2, "peakHostCpuPercent": 5.34, "peakTestRssMiB": 432.1, "swapOutDeltaBytes": 0}
25
+ {"pass": false, "reasons": ["ack p95 538.2 > 500 ms"], "machines": 1125, "users": 1, "sseClients": 0, "durationMs": 120000, "committedPerSecond": 1113.5, "sseWorkers": 4, "clientWarmupMs": 0, "historyMetricSet": "standard", "historyIntervalMs": 1000, "sampleCadenceRatio": 0.9898, "historyCadenceRatio": 1.0083, "ackP95Ms": 538.2, "historyP95Ms": 30.91, "eventLoopP99Ms": 51.9, "sseNewestSampleP95Ms": 0, "hubCpuPercentOneCore": 86.73, "hubRssPeakMiB": 258.7, "sseMegabytes": 0.0, "report": "docs/stress-2026-11-04/183112-probe-m1125-u1-s0-120000ms.json", "resources": "docs/stress-2026-11-04/183112-probe-m1125-u1-s0-120000ms.resources.json", "wallSeconds": 135.3, "minimumAvailableMemoryGiB": 108.9, "peakHostCpuPercent": 8.98, "peakTestRssMiB": 439.3, "swapOutDeltaBytes": 0}
26
+ {"pass": true, "reasons": [], "machines": 500, "users": 64, "sseClients": 64, "durationMs": 120000, "committedPerSecond": 499.8, "sseWorkers": 4, "clientWarmupMs": 1000, "historyMetricSet": "standard", "historyIntervalMs": 1000, "sampleCadenceRatio": 0.9996, "historyCadenceRatio": 1.0081, "ackP95Ms": 222.83, "historyP95Ms": 180.56, "eventLoopP99Ms": 123.34, "sseNewestSampleP95Ms": 407, "hubCpuPercentOneCore": 74.68, "hubRssPeakMiB": 559.5, "sseMegabytes": 11733.6, "report": "docs/stress-2026-11-04/193608-probe-m500-u64-s64-120000ms.json", "resources": "docs/stress-2026-11-04/193608-probe-m500-u64-s64-120000ms.resources.json", "wallSeconds": 163.0, "minimumAvailableMemoryGiB": 108.2, "peakHostCpuPercent": 8.27, "peakTestRssMiB": 1474.6, "swapOutDeltaBytes": 0}
27
+ {"pass": true, "reasons": [], "machines": 529, "users": 1, "sseClients": 1, "durationMs": 15000, "committedPerSecond": 529.0, "sseWorkers": 1, "clientWarmupMs": 1000, "historyMetricSet": "standard", "historyIntervalMs": 1000, "sampleCadenceRatio": 1.0, "historyCadenceRatio": 1.0665, "ackP95Ms": 57.98, "historyP95Ms": 8.92, "eventLoopP99Ms": 52, "sseNewestSampleP95Ms": 79, "hubCpuPercentOneCore": 45.72, "hubRssPeakMiB": 200.4, "sseMegabytes": 32.5, "report": "docs/stress-2026-11-04/193909-probe-m529-u1-s1-15000ms.json", "resources": "docs/stress-2026-11-04/193909-probe-m529-u1-s1-15000ms.resources.json", "wallSeconds": 31.0, "minimumAvailableMemoryGiB": 109.5, "peakHostCpuPercent": 4.52, "peakTestRssMiB": 465.4, "swapOutDeltaBytes": 0}
28
+ {"pass": false, "reasons": ["SSE admission/delivery incomplete (0/1)", "live update rate below one update per two seconds per viewer"], "machines": 530, "users": 1, "sseClients": 1, "durationMs": 15000, "committedPerSecond": 531.9, "sseWorkers": 1, "clientWarmupMs": 1000, "historyMetricSet": "standard", "historyIntervalMs": 1000, "sampleCadenceRatio": 1.0037, "historyCadenceRatio": 1.0666, "ackP95Ms": 57.67, "historyP95Ms": 18.22, "eventLoopP99Ms": 51.41, "sseNewestSampleP95Ms": 64, "hubCpuPercentOneCore": 45.05, "hubRssPeakMiB": 189.2, "sseMegabytes": 1.0, "report": "docs/stress-2026-11-04/193940-probe-m530-u1-s1-15000ms.json", "resources": "docs/stress-2026-11-04/193940-probe-m530-u1-s1-15000ms.resources.json", "wallSeconds": 31.0, "minimumAvailableMemoryGiB": 109.5, "peakHostCpuPercent": 5.69, "peakTestRssMiB": 418.2, "swapOutDeltaBytes": 0}
29
+ {"pass": false, "reasons": ["SSE admission/delivery incomplete (64/65)"], "machines": 100, "users": 1, "sseClients": 65, "durationMs": 10000, "committedPerSecond": 100.0, "sseWorkers": 4, "clientWarmupMs": 1000, "historyMetricSet": "standard", "historyIntervalMs": 1000, "sampleCadenceRatio": 0.9999, "historyCadenceRatio": 0.9999, "ackP95Ms": 63.78, "historyP95Ms": 5.28, "eventLoopP99Ms": 37.03, "sseNewestSampleP95Ms": 481, "hubCpuPercentOneCore": 15.65, "hubRssPeakMiB": 162.4, "sseMegabytes": 176.8, "report": "docs/stress-2026-11-04/194011-probe-m100-u1-s65-10000ms.json", "resources": "docs/stress-2026-11-04/194011-probe-m100-u1-s65-10000ms.resources.json", "wallSeconds": 24.8, "minimumAvailableMemoryGiB": 109.3, "peakHostCpuPercent": 5.14, "peakTestRssMiB": 772.3, "swapOutDeltaBytes": 0}
@@ -0,0 +1,13 @@
1
+ {
2
+ "baseRevision": "3b77feaed4afc9ebd9af156008c78bd0278f9015",
3
+ "attemptCount": 29,
4
+ "resourceAborts": [],
5
+ "remainingTemporaryDirectories": [],
6
+ "totalWriteFailuresAcrossCases": 0,
7
+ "largestFinalDatabaseMiB": 320.3,
8
+ "sourceSha256": {
9
+ "scripts/scale-harness.ts": "8d67637bee926bfea6458536828939b95be4919edfec78f1c8b562f05b3c2f30",
10
+ "scripts/stress-hub.py": "1519a5868c0654eec76eab66df0c38f486c7aa4e88b235bcba83017b9f1ad7e6",
11
+ "tests/scale-harness.test.ts": "f87279588b95f02a78bf12861d27086131033ad5eca8191900004635d645584e"
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Rigscope hub stress results</title><style>
2
+ #stress-results {font-family:var(--font-sans, system-ui);color:var(--foreground, CanvasText);font-size:14px;line-height:1.45}
3
+ #stress-results h2 {font-size:16px;font-weight:500;margin:0 0 4px}
4
+ #stress-results p {margin:0 0 18px;color:var(--muted-foreground, GrayText);font-size:12px}
5
+ #stress-results .axis,#stress-results .row {display:grid;grid-template-columns:70px minmax(60px,1fr) 118px;gap:12px;align-items:center}
6
+ #stress-results .axis {font-size:11px;color:var(--muted-foreground, GrayText);margin-bottom:8px}
7
+ #stress-results .axis-scale {position:relative;display:flex;justify-content:space-between}
8
+ #stress-results .axis-scale b {position:absolute;left:20%;transform:translateX(-50%);font-weight:400;white-space:nowrap;color:var(--foreground, CanvasText)}
9
+ #stress-results .row {height:44px;font-variant-numeric:tabular-nums}
10
+ #stress-results .track {position:relative;height:20px;background:color-mix(in srgb,var(--foreground, CanvasText) 6%,transparent);border-radius:3px}
11
+ #stress-results .bar {height:20px;border-radius:3px;min-width:3px}
12
+ #stress-results .pass {background:var(--chart-1, Highlight)}
13
+ #stress-results .fail {background:var(--chart-2, GrayText)}
14
+ #stress-results .limit {position:absolute;top:-7px;bottom:-7px;left:20%;border-left:1px dashed var(--foreground, CanvasText);opacity:.6}
15
+ #stress-results .value {text-align:right}
16
+ #stress-results small {display:block;font-size:11px;color:var(--muted-foreground, GrayText)}
17
+ #stress-results .mixed {margin-top:22px;padding-top:14px;border-top:1px solid var(--border, GrayText)}
18
+ #stress-results .metrics {display:flex;flex-wrap:wrap;gap:14px 32px;margin-top:10px}
19
+ #stress-results .metric {font-variant-numeric:tabular-nums}
20
+ #stress-results .metric strong {font-size:18px;font-weight:500}
21
+ @media(max-width:440px){#stress-results .axis,#stress-results .row{grid-template-columns:55px minmax(55px,1fr) 94px;gap:8px}#stress-results .axis-scale b{top:-16px}#stress-results .axis{margin-top:28px}}
22
+ </style></head><body><div id="stress-results"><h2>Ingestion gets slower near 1,100 collectors</h2><p>Two-minute runs · one sample per collector per second · no live viewers</p><div class="axis"><span>Collectors</span><span class="axis-scale"><span>0</span><b>500 ms limit</b><span>2,500 ms</span></span><span style="text-align:right">Ack p95</span></div><div class="row" aria-label="900 collectors, acknowledgment p95 72 milliseconds, Pass"><span>900</span><div class="track"><div class="bar pass" style="width:2.88%"></div><span class="limit"></span></div><span class="value">72 ms <small>Pass</small></span></div><div class="row" aria-label="1050 collectors, acknowledgment p95 140 milliseconds, Pass"><span>1,050</span><div class="track"><div class="bar pass" style="width:5.60%"></div><span class="limit"></span></div><span class="value">140 ms <small>Pass</small></span></div><div class="row" aria-label="1125 collectors, acknowledgment p95 538 milliseconds, Over threshold"><span>1,125</span><div class="track"><div class="bar fail" style="width:21.53%"></div><span class="limit"></span></div><span class="value">538 ms <small>Over threshold</small></span></div><div class="row" aria-label="1200 collectors, acknowledgment p95 2154 milliseconds, Over threshold"><span>1,200</span><div class="track"><div class="bar fail" style="width:86.17%"></div><span class="limit"></span></div><span class="value">2,154 ms <small>Over threshold</small></span></div><div class="mixed"><h2>500 collectors + 64 live sessions passed for two minutes</h2><div class="metrics"><div class="metric"><strong>223 ms</strong><small>Acknowledgment p95</small></div><div class="metric"><strong>181 ms</strong><small>History p95 · 64 polls/s</small></div><div class="metric"><strong>560 MiB</strong><small>Hub memory peak</small></div></div></div></div></body></html>
@@ -0,0 +1,195 @@
1
+ # System installation
2
+
3
+ System installation runs Rigscope under dedicated service accounts and starts
4
+ at boot without user lingering. For the default per-user npm setup, use the
5
+ [installation guide](installation.md).
6
+
7
+ Review the plan before applying changes. Commands that change the installation
8
+ require root and `--apply`; otherwise they only print the proposed changes.
9
+ The installed `rigscope system` command and `dist/system.cjs` use the same code.
10
+
11
+ | Task | Section |
12
+ | --- | --- |
13
+ | Review paths and permissions | [Generated layout](#scope-and-generated-layout) |
14
+ | Install or control services | [Component lifecycle](#system-component-lifecycle) |
15
+ | Enroll with another hub | [Connect to a hub](#connecting-to-an-independent-hub) |
16
+ | Grant package-energy access | [CPU package power](#cpu-package-power) |
17
+
18
+ ### Scope and generated layout
19
+
20
+ ```text
21
+ /opt/rigscope/ root:root 0755
22
+ releases/<version>/ immutable root-owned release
23
+ current -> releases/<version>
24
+ previous -> releases/<previous> retained for rollback
25
+ node/node root-owned Node 26 when the selected binary was not root-owned
26
+ /etc/rigscope/
27
+ hub.env root:root 0600 (credentials, never printed)
28
+ collector.env root:root 0600
29
+ /var/lib/rigscope/
30
+ hub/telemetry.sqlite rigscope-hub 0600, private 0700 directory
31
+ hub/enrollment.json rigscope-hub 0600
32
+ hub/run/hub.sock private Unix socket (remote mode)
33
+ collector/state.json rigscope-collector 0600
34
+ collector/outbox/ durable collector outboxes
35
+ backups/ root:root 0700, pre-upgrade/pre-rollback SQLite copies
36
+ system-install.json root:root 0600, version 2 component state/versions/backups/adoption
37
+ power-access.json root:root 0600, original counter modes for reversal
38
+ /etc/systemd/system/rigscope-hub.service root:root 0644, hub boot unit
39
+ /etc/systemd/system/rigscope-collector.service root:root 0644, collector boot unit
40
+ /etc/sysusers.d/rigscope.conf root:root 0644, both nologin accounts
41
+ /etc/sysusers.d/rigscope-rapl.conf root:root 0644, collector-only power membership
42
+ /etc/udev/rules.d/90-rigscope-rapl.rules root:root 0644
43
+ ```
44
+
45
+ Build the checkout before using `dist/system.cjs`; an installed package exposes the same commands through `rigscope system`. Reference copies are in `packaging/system/`. They are for review only; the
46
+ renderer in `src/cli/system.ts` is authoritative.
47
+
48
+ `systemd-sysusers` creates `rigscope-hub` and `rigscope-collector` as nologin
49
+ system accounts even when one component is disabled, so the other can be
50
+ enabled later without reinstalling. The hub and collector never run as root.
51
+ The units set `NoNewPrivileges=yes`, `ProtectSystem=strict`,
52
+ `ProtectHome=yes`, an empty `CapabilityBoundingSet=`/`AmbientCapabilities=`,
53
+ `UMask=0077`, explicit writable state paths and a read-only release tree. The
54
+ collector uses `--await-connection` to exit cleanly when no destinations are
55
+ configured, including after an interrupted enrollment. The
56
+ collector does **not** use `PrivateDevices=yes` because GPU telemetry still
57
+ needs real device nodes. The hub does.
58
+
59
+ ### System component lifecycle
60
+
61
+ All mutating commands require `--apply` and root. Without `--apply`, the exact
62
+ files and command sequence are printed and nothing is written. Every component
63
+ operation touches only its own unit; enabling or starting one component never
64
+ stops or restarts the other.
65
+
66
+ ```sh
67
+ # Package first (from a built checkout).
68
+ node scripts/package-release.cjs --output /var/tmp/rigscope-<version>.tar.gz
69
+
70
+ # Review a release before granting sudo.
71
+ node dist/system.cjs plan \
72
+ --source /var/tmp/rigscope-<version>.tar.gz --role local --node /usr/bin/node
73
+
74
+ # Install one complete package. --role selects the initial enabled preset.
75
+ sudo node dist/system.cjs install \
76
+ --source /var/tmp/rigscope-<version>.tar.gz --role hub \
77
+ --node /usr/bin/node --install-deps --enable --apply
78
+
79
+ # Add the other component later; no reinstall and no new package are needed.
80
+ sudo node dist/system.cjs enable collector --apply
81
+ sudo node dist/system.cjs enable hub --apply
82
+
83
+ # Component-specific lifecycle when both exist.
84
+ sudo node dist/system.cjs disable collector --apply
85
+ sudo node dist/system.cjs restart hub --apply
86
+ sudo node dist/system.cjs restart collector --apply
87
+ sudo node dist/system.cjs restart --apply # enabled components only
88
+
89
+ # Component-specific status and logs.
90
+ node dist/system.cjs status
91
+ node dist/system.cjs status collector --json
92
+ node dist/system.cjs logs hub --follow
93
+ ```
94
+
95
+ `install` stages a complete release, verifies both runtimes and UI artifacts,
96
+ copies a non-root-owned Node executable into the root-owned prefix, installs
97
+ production dependencies when `--install-deps` is given, writes private
98
+ configuration and both units, creates both accounts, then switches `current`.
99
+ Staging happens before any service is stopped.
100
+
101
+ - With `--enable`, `--role local` initially enables both components.
102
+ - With `--enable`, `--role hub` initially enables only the hub.
103
+ - With `--enable`, `--role collector` initially enables only the collector.
104
+ - Without `--enable`, both components are installed but disabled.
105
+ - `--start` starts only the requested preset and verifies those components.
106
+ - `--without-power` skips the collector RAPL grant at that mutation.
107
+
108
+ `enable hub` creates the owner account once when `HUB_BROWSER_USERS` is empty
109
+ and prints the password only in that mutation. It is never stored in plaintext
110
+ or printed again. `enable collector` completes the collector account, unit,
111
+ identity and RAPL grant. With no destination it tries the colocated hub:
112
+
113
+ - A live configured hub gets a short-lived, single-use grant in its
114
+ authoritative SQLite registry. The collector then enrolls through the normal
115
+ hub API, which updates the live registry without stopping or restarting the
116
+ hub.
117
+ - A stopped hub gets the identity written to the authoritative registry (or a
118
+ static `HUB_COLLECTORS` bootstrap when the registry is not initialized yet);
119
+ enabling the collector never starts the hub.
120
+ - If there is no configured hub, the collector is enabled but stays dormant and
121
+ reports `Awaiting connection`. Its unit uses `--await-connection` to exit
122
+ cleanly before collection until a destination is saved.
123
+
124
+ Configured remote destinations and credentials are never overwritten. Re-running
125
+ `enable collector` leaves its destination, token, identity and outboxes intact.
126
+
127
+ ### Connecting to an independent hub
128
+
129
+ For a hub-first test where the hub and collector are separate installations,
130
+ create a connection grant in the hub's admin Collectors panel. Save the grant to
131
+ a private file and connect the system collector:
132
+
133
+ ```sh
134
+ # On the collector host; the grant file must be root-owned and 0600.
135
+ sudo install -m 0600 /dev/null /root/collector-grant
136
+ sudoedit /root/collector-grant # paste the single-use grant, save, quit
137
+ sudo node dist/system.cjs connect https://<hub-host> \
138
+ --token-file /root/collector-grant --apply
139
+ sudo rm /root/collector-grant
140
+ ```
141
+
142
+ `connect` stops only the collector while it enrolls, calls the hub's normal
143
+ enrollment API, persists the issued credential under
144
+ `/var/lib/rigscope/collector/state.json`, and starts the collector if it is
145
+ enabled. The grant is never printed. Re-running `connect` on a configured
146
+ collector is refused rather than replacing a remote destination.
147
+
148
+ Status reports each component independently:
149
+
150
+ ```text
151
+ Components:
152
+ hub: installed, enabled, active, ready
153
+ collector: installed, enabled, active, ready, 1 destination(s)
154
+ ```
155
+
156
+ `readiness` is `ready`, `stopped`, `unconfigured`, `awaiting-connection`,
157
+ `invalid-config`, `invalid-state` or `missing`.
158
+
159
+ ### CPU package power
160
+
161
+ `collector/sampling/hwmon.ts` already reads top-level RAPL package counters,
162
+ sums multiple sockets, handles wrap/reset and returns unavailable rather than
163
+ zero. On hosts where `energy_uj` is root-only, system scope grants a narrow
164
+ read path instead of elevating Node.
165
+
166
+ ```sh
167
+ node dist/system.cjs power status
168
+ sudo node dist/system.cjs power grant --apply
169
+ sudo node dist/system.cjs power revoke --apply
170
+ ```
171
+
172
+ Mechanism:
173
+
174
+ - `/etc/sysusers.d/rigscope-rapl.conf` creates `rigscope-rapl` and adds only
175
+ `rigscope-collector`; the hub is not a member.
176
+ - A `powercap` udev rule matches only top-level `intel-rapl:N` devices named
177
+ `package*` and changes only `energy_uj` and `max_energy_range_uj` to
178
+ `0440 root:rigscope-rapl`. Subdomains, constraint files and power-limit
179
+ writes are untouched.
180
+ - `udevadm trigger --subsystem-match=powercap --action=change` applies the rule
181
+ immediately. Because the rule lives in `/etc/udev/rules.d`, device
182
+ recreation and boot reapply it.
183
+ - The collector unit adds `SupplementaryGroups=rigscope-rapl`; it does not use
184
+ file capabilities, ambient capabilities or a privileged Node helper.
185
+ - Grant records each existing counter's original mode/uid/gid in
186
+ `/var/lib/rigscope/power-access.json`. Revoke removes the rule and group
187
+ membership and restores those recorded values (defaults `0400` for
188
+ `energy_uj` and `0444` for `max_energy_range_uj` when no record exists).
189
+ - `power status` reports driver modules, package domains, collector
190
+ readability, persistence, world readability and every problem. A missing
191
+ counter is reported unavailable, never as zero watts.
192
+
193
+ Verify real watts in History after granting access and restarting the collector;
194
+ do not treat a mocked value or a one-time manual `chmod` as installation
195
+ evidence. Unsupported hardware remains unavailable.
package/next.config.js ADDED
@@ -0,0 +1,38 @@
1
+ const { PHASE_DEVELOPMENT_SERVER } = require("next/constants");
2
+ const { availableParallelism } = require("node:os");
3
+
4
+ const workers = Number(process.env.RIGSCOPE_BUILD_WORKERS ?? Math.min(4, availableParallelism()));
5
+ if (!Number.isInteger(workers) || workers < 1 || workers > 64) {
6
+ throw new Error(
7
+ `RIGSCOPE_BUILD_WORKERS must be an integer from 1 to 64 (received ${JSON.stringify(process.env.RIGSCOPE_BUILD_WORKERS)}). ` +
8
+ "Set it to the number of build workers, or leave it unset to auto-select up to four.",
9
+ );
10
+ }
11
+
12
+ /** @param {string} phase @returns {import("next").NextConfig} */
13
+ const nextConfig = (phase) => ({
14
+ // Keep dev writes separate from the packaged hub's production artifact.
15
+ // Use a small static-generation worker pool for this six-page scaffold.
16
+ distDir: phase === PHASE_DEVELOPMENT_SERVER ? ".next-dev" : ".next",
17
+ experimental: { cpus: workers },
18
+ // Remote development goes through an outer TLS terminator and a Unix
19
+ // socket, so the browser's Origin hostname is the configured public origin,
20
+ // not localhost. Keep an explicit hostname allowlist for /_next assets and
21
+ // HMR (Next also adds the createNextApp hostname). Configuring this list
22
+ // makes Next's dev guard block unlisted origins instead of only warning.
23
+ // The hub validates the full exact origin before delegation.
24
+ allowedDevOrigins: phase === PHASE_DEVELOPMENT_SERVER ? publicDevOrigin() : undefined,
25
+ });
26
+
27
+ function publicDevOrigin() {
28
+ const value = process.env.HUB_PUBLIC_ORIGIN;
29
+ if (value === undefined || value === "") return undefined;
30
+ try {
31
+ const origin = new URL(value);
32
+ return origin.protocol === "https:" ? [origin.hostname] : undefined;
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ }
37
+
38
+ module.exports = nextConfig;
package/package.json CHANGED
@@ -1,6 +1,119 @@
1
1
  {
2
2
  "name": "rigscope",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "rigscopeConfigVersion": 2,
5
+ "private": false,
6
+ "engines": {
7
+ "node": ">=26.0.0"
8
+ },
9
+ "os": [
10
+ "linux"
11
+ ],
12
+ "scripts": {
13
+ "dev": "NODE_ENV=development tsx src/hub/main.ts",
14
+ "build": "npm run build:ui && npm run build:runtime",
15
+ "build:runtime": "node scripts/build-runtime.cjs",
16
+ "start": "node dist/hub.cjs",
17
+ "collector:mock": "COLLECTOR_MODE=mock tsx collector/main.ts",
18
+ "collector:enroll": "tsx collector/main.ts --enroll",
19
+ "collector:start": "node dist/collector.cjs",
20
+ "ops": "node dist/ops.cjs",
21
+ "package:release": "npm run build && node scripts/package-release.cjs",
22
+ "typecheck": "tsc --noEmit",
23
+ "test": "node scripts/test.cjs",
24
+ "test:runtime": "npm run build:runtime && node scripts/test-runtime.cjs",
25
+ "test:dev": "npm run build:runtime && node scripts/test.cjs --suite development",
26
+ "validate:https": "node scripts/validate-https.cjs",
27
+ "build:ui": "next build --webpack",
28
+ "rigscope": "node bin/rigscope.cjs",
29
+ "verify:build": "node scripts/verify-build.cjs",
30
+ "verify:package": "node scripts/verify-package.cjs",
31
+ "verify:installed": "node scripts/verify-installed-package.cjs",
32
+ "prepack": "node scripts/prepack.cjs",
33
+ "package:npm": "npm pack",
34
+ "notices": "node scripts/third-party-notices.cjs"
35
+ },
36
+ "dependencies": {
37
+ "lucide-react": "^0.475.0",
38
+ "next": "^16.4.5",
39
+ "next-themes": "^0.4.6",
40
+ "react": "^19.1.0",
41
+ "react-dom": "^19.1.0",
42
+ "ws": "^8.22.1"
43
+ },
44
+ "devDependencies": {
45
+ "@tailwindcss/postcss": "^4.1.0",
46
+ "@types/node": "^22.13.0",
47
+ "@types/react": "^19.1.0",
48
+ "@types/react-dom": "^19.1.0",
49
+ "@types/ws": "^8.18.2",
50
+ "esbuild": "^0.28.2",
51
+ "postcss": "^8.5.23",
52
+ "tailwindcss": "^4.1.0",
53
+ "tsx": "^4.23.15",
54
+ "typescript": "^5.7.0"
55
+ },
56
+ "description": "Your machines, in view. A self-hosted Linux system and process monitor.",
57
+ "bin": {
58
+ "rigscope": "bin/rigscope.cjs"
59
+ },
60
+ "files": [
61
+ "bin/",
62
+ "dist/",
63
+ ".next/BUILD_ID",
64
+ ".next/*.json",
65
+ ".next/server/",
66
+ ".next/static/",
67
+ "next.config.js",
68
+ "README.md",
69
+ "CHANGELOG.md",
70
+ "docs/installation.md",
71
+ "docs/system-installation.md",
72
+ "docs/configuration.md",
73
+ "docs/monitoring.md",
74
+ "docs/operations.md",
75
+ "docs/bottlenecks.md",
76
+ "docs/exports.md",
77
+ "docs/development.md",
78
+ "docs/releasing.md",
79
+ "docs/scale-validation.md",
80
+ "docs/scale-validation.json",
81
+ "docs/scale-acceptance.json",
82
+ "docs/stress-2026-11-04/README.md",
83
+ "docs/stress-2026-11-04/index.jsonl",
84
+ "docs/stress-2026-11-04/manifest.json",
85
+ "docs/stress-2026-11-04/results.html",
86
+ "docs/stress-2026-11-04/*ms.json",
87
+ "ARCHITECTURE.md",
88
+ "DESIGN.md",
89
+ "LICENSE",
90
+ "SECURITY.md",
91
+ "THIRD_PARTY_NOTICES.md",
92
+ "packaging/"
93
+ ],
94
+ "license": "MIT",
95
+ "author": {
96
+ "name": "Triet (@Triet1402)",
97
+ "url": "https://github.com/Triet1402"
98
+ },
99
+ "repository": {
100
+ "type": "git",
101
+ "url": "git+https://github.com/Triet1402/rigscope.git"
102
+ },
103
+ "homepage": "https://github.com/Triet1402/rigscope#readme",
104
+ "bugs": {
105
+ "url": "https://github.com/Triet1402/rigscope/issues"
106
+ },
107
+ "keywords": [
108
+ "linux",
109
+ "monitoring",
110
+ "system-monitor",
111
+ "process-monitor",
112
+ "self-hosted",
113
+ "gpu"
114
+ ],
115
+ "publishConfig": {
116
+ "access": "public",
117
+ "registry": "https://registry.npmjs.org/"
118
+ }
119
+ }
@@ -0,0 +1,5 @@
1
+ # Packaging reference files
2
+
3
+ The `.env.example` files contain public examples, never deployment credentials. Copy configuration to private files (0600) outside the source tree and generate fresh credentials. No configuration starts a production deployment by itself.
4
+
5
+ Use [installation](../docs/installation.md) for managed user/system installs, [operations](../docs/operations.md) for manifest archives and recovery, and [releasing](../docs/releasing.md) for package validation. System reference units are in [system](system/README.md). Generated units in the installer source are authoritative.
@@ -0,0 +1,24 @@
1
+ # Collector environment example
2
+ #
3
+ # Copy what you need into the private collector.env created by the installer
4
+ # (mode 0600). The generated file starts with every value commented out: with
5
+ # no destinations the collector fails closed and delivers nothing.
6
+ #
7
+ # PUBLIC DEMO VALUES ONLY. demo-token-local is a deliberately public local test
8
+ # string, not a credential for any real deployment.
9
+
10
+ # COLLECTOR_MODE=real
11
+ # COLLECTOR_STATE_FILE=/home/operator/.local/state/rigscope/collector/collector.json
12
+ # COLLECTOR_INTERVAL_MS=1000
13
+ # COLLECTOR_HOSTNAME=rig-01
14
+
15
+ # Enroll once against the hub, then remove the one-use grant from the hub.
16
+ # COLLECTOR_ENROLL_URL=ws://127.0.0.1:8081/api/collector/connect
17
+ # COLLECTOR_ENROLL_TOKEN=<one-use grant from the hub>
18
+
19
+ # Or supply destinations explicitly (enrollment writes these into private state
20
+ # for real installations). Full websocket URL including the connect path:
21
+ # COLLECTOR_HUBS=[{"url":"ws://127.0.0.1:8081/api/collector/connect","token":"demo-token-local"}]
22
+
23
+ # Per-destination sharing policy can withhold sensitive families. Defaults are
24
+ # CPU/memory only; process details remain request-scoped and are never spooled.
@@ -0,0 +1,31 @@
1
+ # Hub environment example
2
+ #
3
+ # Copy the values you need into the private hub.env created by the installer
4
+ # (mode 0600), not into a repository file. The generated hub.env starts with
5
+ # every value commented out: no browser users and no collector credentials
6
+ # means protected browser data fails closed.
7
+ #
8
+ # PUBLIC DEMO VALUES ONLY below. Never use demo-token-local for a real host,
9
+ # never reuse a demo browser password, and never put credentials in URLs.
10
+
11
+ # Loopback-only development listener.
12
+ # HUB_HOST=127.0.0.1
13
+ # HUB_PORT=8081
14
+
15
+ # Remote mode is selected only when both are set. The socket must live under a
16
+ # private directory (the installer's state directory is writable by the unit).
17
+ # HUB_PUBLIC_ORIGIN=https://monitor.example.invalid
18
+ # HUB_SOCKET_PATH=/home/operator/.local/state/rigscope/run/hub.sock
19
+
20
+ # Browser access fails closed without this. Generate the real hash locally with
21
+ # scripts/hash-browser-password.cjs; never paste a plaintext password here.
22
+ # HUB_BROWSER_USERS=[{"username":"operator","passwordHash":"<private hash>","machineIds":"*"}]
23
+
24
+ # Static collector credentials (public demo shape/token).
25
+ # HUB_COLLECTORS=[{"collectorId":"demo-collector","machineId":"demo-machine","token":"demo-token-local"}]
26
+
27
+ # Enrollment/registry state. The installer sets HUB_DATABASE_PATH in the unit;
28
+ # keep enrollment state outside the repository, and HUB_STATE_FILE also serves
29
+ # as the recovery import source for previously enrolled collectors.
30
+ # HUB_STATE_FILE=/home/operator/.local/state/rigscope/hub/enrollment.json
31
+ # HUB_ENROLLMENT_GRANTS=[{"token":"<one-use grant>","expiresAt":"<ISO timestamp>"}]
@@ -0,0 +1,7 @@
1
+ # Managed by Rigscope: read-only package energy/range counters for the collector group.
2
+ # Top-level package devices only; subdomains and constraint files are not touched.
3
+ SUBSYSTEM=="powercap", ACTION=="add|change", KERNEL=="intel-rapl:[0-9]*", KERNEL!="intel-rapl:*:*", ATTR{name}=="package*", \
4
+ RUN+="/usr/bin/chgrp rigscope-rapl /sys%p/energy_uj", \
5
+ RUN+="/usr/bin/chmod 0440 /sys%p/energy_uj", \
6
+ RUN+="/usr/bin/chgrp rigscope-rapl /sys%p/max_energy_range_uj", \
7
+ RUN+="/usr/bin/chmod 0440 /sys%p/max_energy_range_uj"
@@ -0,0 +1,5 @@
1
+ # System installation references
2
+
3
+ These files illustrate dedicated `rigscope-hub` and `rigscope-collector` accounts, systemd units and read-only package-energy access. The renderer in `src/cli/system.ts` is authoritative; do not install examples blindly.
4
+
5
+ See [System installation](../../docs/system-installation.md) for planning, `--apply`, component controls and CPU package-power permissions. The collector needs GPU device access and does not use `PrivateDevices=yes`; the hub does. Both run without root privileges.
@@ -0,0 +1,53 @@
1
+ # Managed by `rigscope system`. Enable explicitly after enrollment.
2
+ # Independent component: enabling or restarting this unit never touches the hub.
3
+ # The collector is nologin and has no capability grants.
4
+ # Package power reads use the private group rule installed separately.
5
+ # Node receives no capabilities, no root, and no device-control access.
6
+ [Unit]
7
+ Description=Rigscope collector (system scope)
8
+ Documentation=file:/opt/rigscope/current/README.md
9
+ After=network-online.target
10
+ Wants=network-online.target
11
+ StartLimitIntervalSec=60
12
+ StartLimitBurst=5
13
+
14
+ [Service]
15
+ Type=simple
16
+ User=rigscope-collector
17
+ Group=rigscope-collector
18
+ WorkingDirectory=/opt/rigscope/current
19
+ EnvironmentFile=/etc/rigscope/collector.env
20
+ Environment=NODE_ENV=production
21
+ Restart=on-failure
22
+ RestartSec=2s
23
+ KillSignal=SIGTERM
24
+ UMask=0077
25
+ NoNewPrivileges=yes
26
+ PrivateTmp=yes
27
+ ProtectSystem=strict
28
+ ProtectHome=yes
29
+ ProtectKernelTunables=yes
30
+ ProtectKernelModules=yes
31
+ ProtectKernelLogs=yes
32
+ ProtectControlGroups=yes
33
+ RestrictRealtime=yes
34
+ RestrictSUIDSGID=yes
35
+ RestrictNamespaces=yes
36
+ LockPersonality=yes
37
+ RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
38
+ CapabilityBoundingSet=
39
+ AmbientCapabilities=
40
+ ExecStart="/opt/rigscope/node/node" "/opt/rigscope/current/dist/collector.cjs" --await-connection
41
+ SupplementaryGroups=rigscope-rapl
42
+ Environment="COLLECTOR_MODE=real"
43
+ Environment="COLLECTOR_STATE_FILE=/var/lib/rigscope/collector/state.json"
44
+ Environment="COLLECTOR_HEALTH_FILE=/var/lib/rigscope/collector/health.json"
45
+ ReadWritePaths="/var/lib/rigscope/collector"
46
+ ReadOnlyPaths="/opt/rigscope/current"
47
+ TimeoutStopSec=10s
48
+ StandardOutput=journal
49
+ StandardError=journal
50
+ SyslogIdentifier=rigscope-collector
51
+
52
+ [Install]
53
+ WantedBy=multi-user.target