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,279 @@
1
+ # Rigscope architecture
2
+
3
+ Rigscope has one hub application process and one collector per monitored machine. This is a custom-server deployment with a production Next.js UI, not `next start` or a serverless application.
4
+
5
+ ## Topology and ownership
6
+
7
+ - Install one collector per monitored machine.
8
+ - Each collector opens one outbound connection to each configured hub.
9
+ - A hub receives many collector connections and serves many authenticated browsers.
10
+ - Browsers connect to a hub; they need no direct access to collectors.
11
+ - Monitor the hub's machine by installing a collector there using the same protocol.
12
+ - Each hub owns enrollment, permissions, retention, alerts, and users independently.
13
+ Two hubs do not imply replicated databases or an automatic failover cluster.
14
+ - Multiple destinations are supported from the first collector implementation. Hub
15
+ federation, automatic discovery, and clustered hub deployments are separate requirements.
16
+
17
+ Only collector-to-hub and browser-to-hub reachability is necessary. LAN, Tailscale,
18
+ and ordinary routed networks use the same protocol. No collector-to-collector links
19
+ or inbound collector ports are required.
20
+
21
+ ## Transport choices
22
+
23
+ | Link / operation | Transport | Reason |
24
+ | --- | --- | --- |
25
+ | Collector ↔ hub | WebSocket over TLS (`wss://`) | One outbound session carries telemetry, acknowledgments, capabilities, and detail requests in both directions. |
26
+ | Hub → browser, live updates | SSE over HTTPS | Predominantly one-way updates; EventSource provides reconnects and event IDs. |
27
+ | Browser → hub, queries/actions | HTTPS JSON requests | Normal request/response semantics for history, settings, authentication, and explicit actions. |
28
+ | Collector enrollment | HTTPS JSON request | Exchange a short-lived enrollment token for a credential specific to that hub connection. |
29
+
30
+ WebSocket keeps acknowledgments and on-demand detail requests on the collector's existing outbound connection. Browser SSE and JSON requests keep live updates separate from explicit queries. Bounded JSON frames have no automatic transport fallback.
31
+
32
+ ## Runtime and module boundaries
33
+
34
+ Keep TypeScript/Node and Next.js, with two executable roles: `collector` and `hub`.
35
+ The hub has one explicit, long-running Node entry point that owns its HTTP server,
36
+ WebSocket upgrades, application services, and background jobs. It delegates UI requests to
37
+ Next.js. This preserves one hub application process/listener; a TLS proxy is optional
38
+ infrastructure. Collection belongs to the collector and continues without a browser.
39
+
40
+ Transport handlers receive services from the composition root. Do not assume a `globalThis`
41
+ object is shared between Next.js bundles or let route requests start monitoring loops.
42
+ This is a custom-server deployment, not unchanged `next start` or a serverless deployment.
43
+ The entry point has its own runtime bundle. Production ships `.next` with `dist/*.cjs`; development uses `.next-dev`. Collector upgrades go to `/api/collector/connect`; framework upgrades reach Next.js.
44
+
45
+ Module layout (responsibilities; consult the source tree for exact paths):
46
+
47
+ ```text
48
+ collector/
49
+ main.ts # lifecycle and local configuration
50
+ sampling/ # OS metrics, capabilities, monotonic rate calculation
51
+ destinations/ # independent delivery session/outbox per hub
52
+ src/
53
+ contracts/ # wire schemas, units, identifiers
54
+ hub/
55
+ main.ts # composition root; HTTP + upgrades + Next.js
56
+ enrollment/ # credentials and hub-local machine mapping
57
+ telemetry/ # validation, persistence, latest state, freshness
58
+ subscriptions/ # browser interests -> expiring detail requests
59
+ history/ # SQLite batches, queries, retention, rollups
60
+ alerts/ # incident lifecycle and notifications
61
+ transport/ # WebSocket, SSE, and HTTP handlers
62
+ app/ # fleet and machine-scoped UI
63
+ components/ # existing visual system
64
+ ```
65
+
66
+ Keep substantial behavior behind small module interfaces. Telemetry accepts batches,
67
+ returns state/history, and provides subscriptions; callers do not manage its deduplication
68
+ or freshness. Destination delivery hides reconnects/acks/outboxes from sampling. Sampling
69
+ hides OS/tool differences. Do not add generic interfaces for hypothetical implementations;
70
+ wire contracts are a real process boundary already.
71
+
72
+ ### Installation and component lifecycle
73
+
74
+ One release contains the hub, collector, UI and administration tools. The install
75
+ role is an initial preset; version 2 configuration records independent hub and
76
+ collector enablement. Version 1 roles are mapped in memory on read and migrated
77
+ on an explicit mutation. Read-only status never rewrites configuration or units.
78
+
79
+ User installations have separate hashed hub and collector systemd units, each
80
+ running `rigscope run hub|collector` with its own supervisor lease. System scope
81
+ uses separate dedicated accounts and hardened units. Neither service depends on
82
+ the other. Each retains its settings, credentials and data when disabled; a
83
+ collector can continue sending to remote hubs while its local hub is disabled.
84
+ An unconfigured collector reports Awaiting connection and stays dormant.
85
+
86
+ The configuration module owns presets, migration and component setup. The service
87
+ module owns systemd control and component service registration. The lifecycle
88
+ module starts and verifies selected components. Upgrade and rollback coordinate
89
+ one shared runtime, hold both component leases, back up hub storage and restore
90
+ the exact previous activity and startup settings. Uninstall removes both units,
91
+ launcher and runtime copies while retaining application data.
92
+
93
+ Automatic colocated pairing uses bootstrap configuration only before the first
94
+ hub start. Once SQLite owns the registry, pairing uses that registry and the
95
+ normal enrollment endpoint to update a live hub without restarting it. Existing
96
+ collector destinations are preserved. Explicit `connect` also supports a
97
+ one-time grant from another machine's hub. The [installation guide](docs/installation.md) describes component commands and hub-first setup.
98
+
99
+ The implemented coordination boundaries are:
100
+
101
+ - `src/live/machine-selection.ts` resolves route, authorized Fleet and remembered
102
+ selection with view-specific fallbacks, and coordinates navigation and persistence.
103
+ Views consume the selection; explicit missing Machines retain their access outcome.
104
+ - `src/hub/sample-ingestion.ts` authenticates Sample identity against the current
105
+ Collector session, commits through `TelemetryStore`, then publishes eligible live
106
+ Samples and Incident observations only while that session remains current. It also
107
+ restores persisted metrics, cadence, inventory and metadata at startup without
108
+ marking a Collector connected. Transport formats acknowledgments from its outcomes.
109
+ - `collector/destinations/connection.ts` owns each Hub connection's sharing policy,
110
+ cadence, recovered queue sanitation, inventory and durable append-before-send path.
111
+ Collector runtime fans shared Samples out to these connections. Sampling, GPU
112
+ polling, aggregate disk budgets and cross-Hub Detail scheduling remain shared.
113
+ Recovery applies privacy filtering without reapplying capture-time cadence.
114
+
115
+ ## Identity, enrollment, and messages
116
+
117
+ Generate a persistent collector installation ID locally. Enroll it separately with each hub,
118
+ which maps the authenticated collector to a hub-local machine ID. Addresses/hostnames are
119
+ metadata. Reinstall/replacement needs explicit enrollment or administrator reassociation;
120
+ cloned installations must generate new identities.
121
+
122
+ Each hub connection has an independent credential and local sharing policy. Verify the
123
+ hub's TLS certificate using ordinary certificate trust. Send credentials in an authorization
124
+ header, not a URL. Remote connections require TLS; loopback-only development may explicitly
125
+ use plaintext. Authenticate first and reject conflicting claimed collector identities.
126
+
127
+ JSON message families:
128
+
129
+ - `hello` / `welcome`: capabilities, inventory, hub/session identity.
130
+ - `samples` / `ack`: bounded batches; identify exactly which sample IDs were durably accepted
131
+ or permanently rejected.
132
+ - `detail.subscribe` / `detail.cancel` / `detail.result`: scoped, temporary monitoring detail.
133
+ - `heartbeat`, `collector.status`, `gap`: connection liveness, collection errors, and
134
+ monitoring intervals lost to delivery limits.
135
+
136
+ Sample fields: `collectorId`, `runId`, `sequence`, `bootId`, `capturedAt`,
137
+ `kind`, and validated `payload`. `runId` changes on collector restart; `bootId` identifies
138
+ an OS boot. `(collectorId, runId, sequence)` is unique, with sequence increasing across
139
+ sample kinds within a run. Each hub assigns `receivedAt` and its own browser-stream cursor.
140
+ Use stable resource IDs where possible, e.g. GPU UUID, not display order. Define units/CPU
141
+ normalization and calculate rates from local monotonic elapsed time.
142
+
143
+ Separate inventory, periodic metrics, and process detail. Unsupported/failed metrics have
144
+ explicit status and absent values, never successful zeroes. Process detail is live/on
145
+ demand and is not persisted by default.
146
+
147
+ ## Multiple hubs and subscription policy
148
+
149
+ Collect each metric once at the fastest locally permitted requested cadence, then fan it
150
+ out to authorized connections. Apply per-destination field filters and cadence before
151
+ queueing/transmission. Omit process arguments and environment variables by default. Hubs
152
+ cannot install destinations, change another destination's policy, or exceed local budgets.
153
+ If a destination's sharing policy is reduced, cancel disallowed subscriptions and purge
154
+ now-forbidden fields from its pending queue before further transmission.
155
+
156
+ Each destination owns its session, bounded queue, acknowledgment state, and reconnect
157
+ schedule. An offline hub must not block a healthy hub or sampling. Set a configurable
158
+ destination count and global resource budget as well as per-destination limits.
159
+
160
+ Example: A needs GPU summaries; B needs summaries and has someone viewing Processes. Share
161
+ one GPU collection pass, deliver process detail only to B, and stop extra scans when its
162
+ subscription expires. A's subscription must not authorize sensitive delivery to B.
163
+
164
+ ## Failure, delivery, and overload
165
+
166
+ - WebSocket ordering applies within a connection, not across reconnects. Use heartbeats,
167
+ idle deadlines, and exponential reconnect backoff with jitter.
168
+ - Periodic metrics use at-least-once delivery within the retention budget. Retry retained samples until acked;
169
+ deduplicate in the hub transaction. Ack after durable commit, not socket receipt. Reject
170
+ invalid records explicitly so bad samples cannot retry forever. Process results are
171
+ transient and request-scoped.
172
+ - Use disk-backed per-hub outboxes for periodic metrics to recover across collector restarts.
173
+ Bound age/bytes per destination and total disk usage. Default caps: 10 minutes / 64 MiB
174
+ per destination, whichever comes first; measure before finalizing. Evict oldest pending
175
+ telemetry at the cap and retain a bounded, coalesced gap notice. Bound the in-flight
176
+ window too. Do not spool sensitive process detail by default.
177
+ - Prioritize fresh metrics over rate-limited backfill. Backfill fills history but cannot
178
+ replace newer live state or trigger an old incident as if it just happened.
179
+ - Bound frames, ingestion rates, outstanding requests, and browser buffers. WebSocket does
180
+ not supply application delivery guarantees. Coalesce metric updates for a slow browser
181
+ or disconnect/resynchronize it; refetch incident state after gaps.
182
+ - Track connection health separately from sample freshness: a connected collector can
183
+ have broken GPU collection. Use expected cadence and receipt time for liveness; retain
184
+ capture time and flag clock skew rather than trusting remote wall clocks.
185
+ - Removing a destination stops delivery and purges its credential/outbox. Hub revocation
186
+ terminates existing sessions as well as rejecting new ones.
187
+
188
+ ## Browser flow
189
+
190
+ Use one authorized, multiplexed SSE stream per open app tab for its machines/event types,
191
+ not one per chart or machine. Prefer HTTP/2 at the TLS proxy because HTTP/1.1 browsers limit
192
+ concurrent SSE connections per origin. Disable proxy buffering for streams and configure
193
+ WebSocket upgrades/idle timeouts end to end.
194
+
195
+ Authenticate with same-origin secure, HttpOnly session cookies. Authorize scope on connect,
196
+ replay, and permission changes; protect mutations against CSRF. Filter before transmission.
197
+ Establish a consistent initial subscription/snapshot boundary, then emit updates. Provide
198
+ event IDs and bounded replay. Expired cursors or a previous hub run cause a reset/fresh
199
+ snapshot. EventSource reconnects do not themselves provide durable replay. History queries
200
+ use HTTPS with a point/result budget and suitable rollups.
201
+
202
+ Opening an authorized detail view creates/renews a bounded lease over the collector's
203
+ existing WebSocket. The hub combines equivalent viewer interests; the collector combines
204
+ compatible interests across hubs while preserving delivery permissions. Expire leases
205
+ using local monotonic TTLs. Browser disconnect, hub disconnect, or renewal failure releases
206
+ extra work promptly. Requests must fit local policy and reported capabilities.
207
+
208
+ ## Storage and scale
209
+
210
+ Each hub owns a SQLite database, one bounded write queue, batched writes, retention, and
211
+ rollups that preserve min/max. Never scan the OS or write telemetry once per browser.
212
+ Bound query/transaction work. Measure event loop delay during ingestion, history queries,
213
+ retention and rendering; move synchronous database work to a worker thread if needed.
214
+
215
+ One hub application instance owns connection registry, leases, latest state, and bounded
216
+ browser replay. Many connections do not require multiple backend replicas. Replicas later
217
+ need explicit session routing, pub/sub, shared storage, and background-job ownership.
218
+
219
+ Historical planning envelope: 1–50 machines with multiple viewers, not a measured capacity
220
+ claim. There is no fixed machine-count limit. The concurrent collector socket budget
221
+ defaults to 100 and is configurable with `HUB_MAX_COLLECTOR_CONNECTIONS`; it is a
222
+ deployment resource budget, not enrollment capacity. Benchmark representative payload
223
+ sizes, process counts, subscriptions, viewers, history queries and host resources.
224
+ Observe queue depth, stream count, active leases,
225
+ ingestion lag, dropped samples, memory, and event loop delay.
226
+
227
+ ## Future actions
228
+
229
+ The release is read-only. Future structured actions travel browser HTTPS →
230
+ hub authorization/audit → collector WebSocket → result → hub → browser SSE. Each destination
231
+ has an explicit action policy, default denied. Read-only hubs may request allowed monitoring
232
+ detail. Initially allow at most one mutating hub per collector to avoid conflicting control.
233
+
234
+ Actions need request IDs, deadlines, handling of ambiguous outcomes after connection loss,
235
+ and execution-time target validation (machine/boot/PID/start identity for processes).
236
+ Do not apply telemetry retry behavior to destructive actions. Specify action semantics
237
+ separately before implementing kill/renice or service restarts.
238
+
239
+ ## Terminology
240
+
241
+ **Machine**:
242
+ A physical server or virtual machine whose operating system and workloads are monitored.
243
+ _Avoid_: Node (when it means the monitored machine)
244
+
245
+ **Collector**:
246
+ The background service installed on a machine to gather its monitoring information and report it to configured hubs.
247
+ _Avoid_: Agent, AI agent, sensor, exporter (for this service)
248
+
249
+ **Hub**:
250
+ An independently operated monitoring service that receives information from collectors and makes it available to its users.
251
+ _Avoid_: Primary (unless an explicit primary relationship exists)
252
+
253
+ **Hub connection**:
254
+ A collector's independently configured relationship with one hub, including what information that hub may receive and what requests it may make.
255
+ _Avoid_: Replica, mirror (independent hubs do not imply replicated state)
256
+
257
+ **Fleet**:
258
+ The collection of machines a user can view through a particular hub.
259
+
260
+ **Browser session**:
261
+ A user's authenticated access to one independently operated hub.
262
+
263
+ **Machine scope**:
264
+ The set of machines a user is permitted to view through a hub. An empty scope grants no machine access.
265
+
266
+ **Sample**:
267
+ A measurement of a machine or resource at a particular time.
268
+
269
+ **Capability**:
270
+ A category of monitoring information or permitted operation that a collector can provide for a particular hub connection.
271
+
272
+ **Detail subscription**:
273
+ A temporary request for more detailed monitoring information about selected resources.
274
+
275
+ **Incident**:
276
+ A tracked occurrence of an alert condition, including its active and resolved states.
277
+
278
+
279
+ See [operations](docs/operations.md) for persistence, upgrade and recovery guarantees.
package/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # Release notes
2
+
3
+ ## 0.1.1
4
+
5
+ - Improve History chart behavior and add synchronized chart inspection.
6
+ - Add resource-guarded hub capacity testing with independent live-stream readers,
7
+ two-minute confirmations and reproducible measurements.
8
+ - Include the capacity report and measurement JSON in the npm package so the
9
+ scale-validation documentation links resolve after installation.
10
+
11
+ Known limitation: sufficiently large full-fleet snapshots can exceed the live
12
+ feed's 1 MiB buffer guard and disconnect viewers. The synthetic test crossed
13
+ that boundary at 530 machines; the exact count depends on payload size and scope.
14
+
15
+ ## 0.1.0
16
+
17
+ Initial public release of Rigscope, a self-hosted Linux system and process monitor.
18
+
19
+ - Fleet and machine views with CPU, memory, swap, GPU, disk and network readings.
20
+ - Durable History, per-core charts, live process inspection and CSV/JSON exports.
21
+ - Threshold and availability alerts, HTTPS webhooks, users and machine scopes.
22
+ - Outbound collectors with independent sharing and durable retry queues for up to
23
+ eight hubs per collector.
24
+ - Managed user installation, independent hub/collector services, enrollment,
25
+ diagnostics, verified upgrades, rollback and offline password recovery.
26
+ - Optional system installation with dedicated unprivileged service accounts and
27
+ narrowly scoped CPU package-energy access.
28
+
29
+ Requires Linux and Node.js 26+. Background user installation requires systemd.
30
+ NVIDIA and available AMD GPU counters are supported; Intel sensors are partial.
31
+ Missing or inaccessible readings remain unavailable. Remote process mutation,
32
+ hub federation and clustering are outside this release.
33
+
34
+ See [installation](docs/installation.md) for hardware limits and
35
+ [scale validation](docs/scale-validation.md) for measured synthetic workloads.
36
+ Internal configuration, database and wire-protocol revisions are independent of
37
+ the application version.
package/DESIGN.md ADDED
@@ -0,0 +1,299 @@
1
+ ---
2
+ name: Rigscope
3
+ description: A calm remote monitoring console with clear hierarchy, useful density, and coherent dark/light themes.
4
+ colors:
5
+ background: "#1a1b1e"
6
+ card: "#26272b"
7
+ card-strong: "#2d2e33"
8
+ border: "#3a3b3f"
9
+ foreground: "#e4e5e8"
10
+ muted-foreground: "#a2a4a9"
11
+ accent: "#3584e4"
12
+ accent-foreground: "#ffffff"
13
+ selected: "#1e3a5c"
14
+ ok: "#3db84a"
15
+ ok-soft: "rgba(61, 184, 74, 0.15)"
16
+ warn: "#e5a83b"
17
+ warn-soft: "rgba(229, 168, 59, 0.15)"
18
+ crit: "#e05e5e"
19
+ crit-soft: "rgba(224, 94, 94, 0.15)"
20
+ cpu: "#3584e4"
21
+ mem: "#4ec289"
22
+ swap: "#d8a23a"
23
+ scrollbar: "#3a3b3f"
24
+ typography:
25
+ page-title:
26
+ fontFamily: "system-ui, ui-sans-serif, 'Noto Sans', 'Inter', sans-serif"
27
+ fontSize: "1.125rem"
28
+ fontWeight: 600
29
+ letterSpacing: "-0.025em"
30
+ panel-title:
31
+ fontFamily: "system-ui, ui-sans-serif, 'Noto Sans', 'Inter', sans-serif"
32
+ fontSize: "0.875rem"
33
+ fontWeight: 500
34
+ letterSpacing: "-0.025em"
35
+ nav:
36
+ fontFamily: "system-ui, ui-sans-serif, 'Noto Sans', 'Inter', sans-serif"
37
+ fontSize: "0.875rem"
38
+ fontWeight: 400
39
+ letterSpacing: "-0.025em"
40
+ support:
41
+ fontFamily: "system-ui, ui-sans-serif, 'Noto Sans', 'Inter', sans-serif"
42
+ fontSize: "0.75rem"
43
+ fontWeight: 400
44
+ lineHeight: 1.5
45
+ data:
46
+ fontFamily: "system-ui, ui-sans-serif, 'Noto Sans', 'Inter', sans-serif"
47
+ fontSize: "1.25rem"
48
+ fontWeight: 600
49
+ fontFeature: "tabular-nums"
50
+ letterSpacing: "-0.025em"
51
+ rounded:
52
+ panel: "12px"
53
+ row: "8px"
54
+ control: "6px"
55
+ pill: "9999px"
56
+ spacing:
57
+ panel: "20px"
58
+ section: "20px"
59
+ page: "24px"
60
+ rail: "56px"
61
+ components:
62
+ panel:
63
+ backgroundColor: "{colors.card}"
64
+ textColor: "{colors.foreground}"
65
+ rounded: "{rounded.panel}"
66
+ padding: "20px"
67
+ panel-title:
68
+ textColor: "{colors.foreground}"
69
+ typography: "{typography.panel-title}"
70
+ empty-state:
71
+ backgroundColor: "transparent"
72
+ textColor: "{colors.muted-foreground}"
73
+ rounded: "{rounded.row}"
74
+ padding: "40px 24px"
75
+ nav-link-selected:
76
+ backgroundColor: "{colors.selected}"
77
+ textColor: "{colors.foreground}"
78
+ rounded: "{rounded.row}"
79
+ padding: "8px 10px"
80
+ nav-link:
81
+ backgroundColor: "transparent"
82
+ textColor: "{colors.muted-foreground}"
83
+ rounded: "{rounded.row}"
84
+ padding: "8px 10px"
85
+ theme-toggle:
86
+ backgroundColor: "transparent"
87
+ textColor: "{colors.muted-foreground}"
88
+ rounded: "{rounded.control}"
89
+ size: "36px"
90
+ ---
91
+
92
+ # Design System: Rigscope
93
+
94
+ ## Overview
95
+
96
+ **Creative North Star: "The Rig-as-Console"**
97
+
98
+ A calm, modern monitoring console inspired by KDE's system monitor: clear resource
99
+ readouts, useful density, restrained surfaces, and charts that make changes easy to see.
100
+ The same visual language should support a single GPU rig and a fleet of servers.
101
+
102
+ This document provides direction and starting values. The frontmatter records the current
103
+ shared tokens; it does not freeze the palette, type scale, layout, or component inventory.
104
+ Existing CSS is the source for shipped values. Extend shared tokens coherently when a
105
+ new view needs them and update this record when those choices become reusable.
106
+
107
+ Prioritize accurate data, legibility, accessibility, and understandable interaction.
108
+ Specific styling choices below can evolve through normal implementation judgment; a
109
+ useful table, larger heading, additional surface, or new component needs no special approval.
110
+
111
+ **Key Characteristics:**
112
+ - Clear hierarchy and scan-friendly data, with room for both dense and spacious views.
113
+ - Quiet neutral surfaces, purposeful color, and recognizable metric/state meanings.
114
+ - Dark and light themes treated as full peers.
115
+ - Consistent components where tasks repeat; specialized presentation where tasks differ.
116
+ - Honest loading, empty, stale, unavailable, error, and disconnected states.
117
+
118
+ ## Colors
119
+
120
+ Begin with the current neutral palette and blue accent. Reuse semantic tokens for common
121
+ roles so both themes stay coherent. The palette can expand to support new interactions,
122
+ alerts, chart series, hierarchy, and accessible contrast.
123
+
124
+ - **Primary:** accent for important actions, selection, focus, and useful emphasis. Filled
125
+ buttons, selected surfaces, and small brand treatments are allowed when they clarify hierarchy.
126
+ - **Neutral:** background/card/card-strong and foreground/muted-foreground are the starting
127
+ vocabulary. Add stronger dividers, intermediate text, or additional surfaces when needed;
128
+ there is no limit of two text colors or one border strength.
129
+ - **State:** healthy, warning, critical, informational, unknown and offline states should be
130
+ recognizable through labels/icons as well as color. Distinguish unavailable from healthy.
131
+ - **Metrics:** CPU blue, memory green, and swap amber are current defaults. Keep mappings
132
+ predictable within and across related views; add distinguishable GPU/resource series and
133
+ legends. A deliberate accessible palette update can revise defaults consistently.
134
+
135
+ Choose colors against their actual background in both themes. A token name alone does
136
+ not guarantee contrast. New reusable roles belong in the theme rather than scattered
137
+ component literals; exploratory values can be promoted once the design settles.
138
+
139
+ ## Typography
140
+
141
+ System sans is the starting family for familiar, quick-loading UI. The existing page
142
+ heading (18px), panel heading/body (14px), supporting text (12px), and gauge value (20px)
143
+ are examples from the scaffold, not a required scale or a maximum size.
144
+
145
+ Use whatever hierarchy makes the task clear. Fleet summaries may need larger readouts;
146
+ dense tables may need compact labels; setup and error explanations need comfortable prose.
147
+ Use meaningful heading order, adequate line height, and wrapping that survives narrow
148
+ viewports and zoom. Do not shrink essential information to preserve a layout.
149
+
150
+ Use tabular numerals for aligned/changing measurements. Monospace is appropriate for
151
+ commands, addresses, identifiers, or logs when it improves reading. Include units and
152
+ consistent precision; stable text/column placement matters more than a particular class.
153
+
154
+ ## Layout
155
+
156
+ Design for the information and the user's task. The current sidebar, header, and scrolling
157
+ main region are a useful baseline. Navigation, sidebar width, scrolling behavior, and
158
+ breakpoints may evolve as fleet, machine, settings and investigation views become real.
159
+
160
+ All pages render inside one shared content container owned by `AppShell`: centered,
161
+ capped at 1500px, with the standard page padding — page code adds no extra max-width or
162
+ centering wrappers. Workspace pages (Processes) fill the same container's full height.
163
+ Card grids that may hold a single item use auto-fit tracks so one card spans the row
164
+ instead of stranding beside empty columns.
165
+ The scaffold uses 20px panel gaps/padding and 24px page padding. Treat these as comfortable
166
+ defaults. Add compact/comfortable density, smaller mobile gutters, wider investigation
167
+ layouts, or other spacing when appropriate. Reuse shared spacing choices once established.
168
+
169
+ Tables, lists, charts, grouped sections, toolbars, and split views are valid building blocks.
170
+ A fleet table does not need a ring-gauge card for every machine, and every region does not
171
+ need a bordered panel. Use grouping and alignment before adding containers. Keep reading
172
+ order, reachable controls and critical status clear across screen sizes. Horizontal
173
+ scrolling can be appropriate for a dense table; contain it within that surface.
174
+
175
+ ## Elevation & Depth
176
+
177
+ Prefer the existing quiet surfaces and subtle borders as the default. Tonal differences,
178
+ stronger dividers, subtle shadows, or a combination can establish hierarchy. Menus, dialogs,
179
+ popovers, and other overlays may need elevation to remain distinct from the data behind them.
180
+
181
+ There is no global ban on shadows, gradients, or additional layers. Use them selectively,
182
+ keep both themes readable, and avoid overwhelming chart marks or making ordinary data
183
+ panels appear interactive. Focus indicators can use the most visible accessible treatment.
184
+
185
+ ## Shapes
186
+
187
+ The current 12px panel, 8px row, and 6px control radii provide a coherent starting family.
188
+ They are defaults, not mandatory dimensions. Tables can have square edges; compact tools,
189
+ chips, overlays, and new containers can choose shapes suited to their job.
190
+
191
+ Use circles for meaningful gauges/status marks where useful. Rings are an available
192
+ visualization, not a required signature for every metric. Choose charts and marks based
193
+ on what people need to compare.
194
+
195
+ ## Components
196
+
197
+ ### Existing foundations
198
+
199
+ `Panel`, `EmptyState`, `AppShell`, navigation, and theme toggle are reusable starting points.
200
+ Reuse them when they fit. Add variants or new components when that produces a clearer
201
+ interface; avoid forcing every view through `Panel` and `EmptyState`.
202
+
203
+ Current component dimensions and styles are examples of the shipped scaffold. A new
204
+ component should share the broader visual language and interaction conventions without
205
+ copying every measurement.
206
+
207
+ ### Data and connection states
208
+
209
+ Separate first load, empty results, unsupported metrics, permission failure, stale samples,
210
+ collector disconnect, and hub disconnect. Keep last-known values visible when useful,
211
+ with an explicit timestamp/state. A missing metric must not look like successful zero usage.
212
+
213
+ Dashed placeholder rings belong to the current development scaffold. Future empty/loading
214
+ states can use contextual explanations, skeletons, compact inline states, and appropriate
215
+ next actions. Avoid presenting an unavailable control as a working action.
216
+
217
+ ### Interactions and motion
218
+
219
+ Give controls understandable labels and relevant hover, focus, selected, disabled, loading,
220
+ and error feedback. Preserve keyboard access and accessible names when labels collapse.
221
+ Use familiar controls for filters, time ranges, enrollment, settings and confirmations.
222
+
223
+ Brief transitions can clarify changes and make the interface feel responsive. Existing
224
+ color transitions are a baseline; expansion, movement and other feedback are allowed when
225
+ useful. Honor reduced-motion preferences and keep frequent metric updates stable enough
226
+ for reading and selection.
227
+
228
+ Process tables fill the available workspace and preserve column proportions during live
229
+ updates and browser resizing. Minimum readable widths trigger scrolling inside the table;
230
+ the toolbar stays visible and table headers remain pinned while rows scroll. Drag column dividers to resize, use arrow keys on a focused
231
+ divider, or double-click to reset. View mode, per-table sorting and widths persist in the
232
+ browser; search, expanded applications, pause and machine selection last for the tab session.
233
+ Machine selection is shared across Fleet, Overview, History and Processes for the tab.
234
+ The sidebar keeps Fleet as the parent of a single machine selector and indented
235
+ Overview/History/Processes links. Global hub pages follow in a separate group. Header
236
+ breadcrumbs show Fleet / machine / view; page-level tabs and pickers are omitted. Fleet
237
+ rows show compact CPU/memory trends received since sign-in. Overview combines CPU/GPU
238
+ and RAM/VRAM into two charts in one fixed ten-minute panel. History owns range and metric
239
+ selection for detailed investigation. Its picker groups CPU, memory, GPU, storage and
240
+ network controls, keeping core/card selection inside the relevant group. Global Select all / Unselect all share the Metrics disclosure row;
241
+ per-resource All / None actions update metric selection in one operation, respecting
242
+ machine capabilities and the hub's recorded metric catalog. Automatic history polling
243
+ leaves Refresh visually unchanged; manual refresh alone disables and animates it until
244
+ the request settles. Resource
245
+ sections fill the content width; related charts share a row on desktop and stack on
246
+ phones. CPU usage, I/O wait and CPU pressure share a percentage chart, while temperature
247
+ and power retain separate units. RAM and filesystem capacity each use one chart with
248
+ a Bytes / % switch when both stored metrics are selected; unit choices persist per
249
+ machine and visible exports follow the displayed unit. Swap belongs in the Memory
250
+ section. Recent usage legends number GPU cards to distinguish identical models. Collector diagnostics remain behind a bounded disclosure;
251
+ obvious explanatory copy is omitted, including on sign-in.
252
+
253
+ Live sampling, process updates and visible-tab history refreshes default to one second.
254
+ Refresh requests never overlap or replace existing charts with loading panels. Process
255
+ freshness changes update the status indicator without adding a banner or moving the table.
256
+
257
+ Per-core History defaults to all available cores, with an explicit All cores / None
258
+ and a scrollable checkbox picker for custom subsets. Up to eight series use lines;
259
+ larger selections use a scrollable heatmap of stored bucket peaks, with latest/peak
260
+ values and optional busiest-first order. Missing readings remain unfilled; recorded
261
+ gaps mask the heatmap. Requests allow the collector contract's 1024 cores while
262
+ retaining the existing scan/time budgets and partial-result indicators.
263
+
264
+ History retains its time range and per-machine metric, GPU-card and series visibility
265
+ choices in the browser; its machine, control expansion and auto-refresh state last for the tab.
266
+ Returning to Processes keeps the last snapshot visible while fresh data is requested.
267
+ Snapshots stay in memory, are scoped to the browser login, and show their freshness state.
268
+
269
+ ## Do's and Don'ts
270
+
271
+ ### Do:
272
+ - **Do** choose the layout and density that help people answer the monitoring question.
273
+ - **Do** reuse or extend shared tokens/components when the behavior or role repeats.
274
+ - **Do** preserve units, freshness, explicit states, and consistent metric meanings.
275
+ - **Do** verify keyboard behavior, zoom, contrast, and both themes when changing UI.
276
+ - **Do** evolve this guidance when a better reusable pattern emerges.
277
+
278
+ ### Don't:
279
+ - **Don't** treat scaffold dimensions or examples as permanent product requirements.
280
+ - **Don't** force every screen into the same cards, chart types, or scrolling structure.
281
+ - **Don't** rely on color alone for status, selection, or chart interpretation.
282
+ - **Don't** show stale/unavailable data as healthy live measurements or fake interactivity.
283
+ - **Don't** sacrifice legibility or access to controls to preserve a visual convention.
284
+
285
+ ## Release setup
286
+
287
+ Rigscope retains the established console styling. Fleet and Connections expose an
288
+ admin-only Add machine action that links to the collector enrollment section. Generated
289
+ commands use the current hub origin, and prompt privately for the separate, one-time
290
+ grant. Copy controls have visible status and a manual-selection fallback. Enrollment
291
+ adds no live-state banners above monitoring data.
292
+
293
+ ## Product and identity
294
+
295
+ Rigscope is a self-hosted Linux resource/process monitor for a machine owner or small-fleet operator. Each collector connects outbound to independently operated hubs; each hub owns its users, permissions and history. The main tasks are spotting stale/unhealthy machines, inspecting resource history and opening live process detail. Remote mutating process actions are outside this release.
296
+
297
+ App surfaces use Operate mode: precise data, quiet surfaces, stable live updates and familiar controls. Freshness changes update existing status indicators without inserting banners above data. Interface copy consists of labels, values and actionable errors; omit redundant explanatory paragraphs.
298
+
299
+ The identity is an open rack frame around a rising trace and two sample points. `BrandMark` is the shared, decorative 24-unit SVG in the sidebar and sign-in row; the neighboring Rigscope text/link provides its accessible name. The app icon uses the same geometry with a dark backing and bright blue stroke for light/dark browser chrome. It is legible at 16–32px, with no animation, raster assets or layout changes. Keep this small brand gesture separate from metric semantics.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Triet (@Triet1402)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.