@crvouga/atlas 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (379) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +178 -617
  3. package/dist/bun/src/driver.d.ts +41 -7
  4. package/dist/bun/src/driver.js +47 -6
  5. package/dist/bun/src/index.d.ts +8 -0
  6. package/dist/bun/src/index.js +8 -0
  7. package/dist/core/src/cli.d.ts +14 -1
  8. package/dist/core/src/cli.js +30 -1
  9. package/dist/core/src/config.d.ts +70 -4
  10. package/dist/core/src/config.js +62 -3
  11. package/dist/core/src/events/cloudevents.d.ts +54 -9
  12. package/dist/core/src/events/cloudevents.js +60 -9
  13. package/dist/core/src/graph.d.ts +21 -0
  14. package/dist/core/src/graph.js +26 -0
  15. package/dist/core/src/index.d.ts +11 -0
  16. package/dist/core/src/index.js +11 -0
  17. package/dist/core/src/lint.d.ts +30 -0
  18. package/dist/core/src/lint.js +13 -0
  19. package/dist/core/src/plan.d.ts +39 -1
  20. package/dist/core/src/plan.js +28 -1
  21. package/dist/core/src/privacy.d.ts +29 -2
  22. package/dist/core/src/privacy.js +19 -2
  23. package/dist/core/src/report/formats.d.ts +34 -4
  24. package/dist/core/src/report/formats.js +24 -4
  25. package/dist/core/src/report/markdown.d.ts +15 -0
  26. package/dist/core/src/report/markdown.js +8 -0
  27. package/dist/core/src/run/clients.d.ts +44 -5
  28. package/dist/core/src/run/clients.js +46 -2
  29. package/dist/core/src/run/execute.d.ts +69 -16
  30. package/dist/core/src/run/execute.js +67 -16
  31. package/dist/core/src/run/frames.d.ts +13 -4
  32. package/dist/core/src/run/frames.js +19 -4
  33. package/dist/core/src/run/http.d.ts +34 -1
  34. package/dist/core/src/run/http.js +14 -1
  35. package/dist/core/src/run/types.d.ts +76 -3
  36. package/dist/core/src/run/types.js +19 -3
  37. package/dist/core/src/signals.d.ts +49 -5
  38. package/dist/core/src/signals.js +22 -5
  39. package/dist/core/src/spec/compose.d.ts +23 -1
  40. package/dist/core/src/spec/compose.js +10 -1
  41. package/dist/core/src/spec/index.d.ts +9 -1
  42. package/dist/core/src/spec/index.js +9 -1
  43. package/dist/core/src/spec/load.d.ts +22 -1
  44. package/dist/core/src/spec/load.js +31 -1
  45. package/dist/core/src/spec/parse.d.ts +16 -2
  46. package/dist/core/src/spec/parse.js +25 -2
  47. package/dist/core/src/spec/scxml.d.ts +25 -4
  48. package/dist/core/src/spec/scxml.js +36 -4
  49. package/dist/core/src/spec/types.d.ts +52 -1
  50. package/dist/core/src/spec/types.js +24 -0
  51. package/dist/detox/src/index.d.ts +54 -11
  52. package/dist/detox/src/index.js +45 -7
  53. package/dist/playwright/src/driver.d.ts +39 -3
  54. package/dist/playwright/src/driver.js +27 -3
  55. package/dist/playwright/src/index.d.ts +10 -0
  56. package/dist/playwright/src/index.js +10 -0
  57. package/dist/playwright/src/media.d.ts +50 -4
  58. package/dist/playwright/src/media.js +55 -4
  59. package/dist/playwright/src/signals.d.ts +44 -5
  60. package/dist/playwright/src/signals.js +29 -5
  61. package/dist/reports/src/attachment-files.d.ts +44 -0
  62. package/dist/reports/src/attachment-files.js +121 -0
  63. package/dist/reports/src/attachment-response.d.ts +31 -0
  64. package/dist/reports/src/attachment-response.js +55 -0
  65. package/dist/reports/src/aws-credentials.d.ts +48 -0
  66. package/dist/reports/src/aws-credentials.js +221 -0
  67. package/dist/reports/src/browser-navigation.d.ts +14 -0
  68. package/dist/reports/src/browser-navigation.js +7 -0
  69. package/dist/reports/src/bundle.d.ts +34 -2
  70. package/dist/reports/src/bundle.js +38 -2
  71. package/dist/reports/src/catalog.d.ts +34 -3
  72. package/dist/reports/src/catalog.js +17 -3
  73. package/dist/reports/src/cli-stores.d.ts +36 -0
  74. package/dist/reports/src/cli-stores.js +73 -0
  75. package/dist/reports/src/clock.d.ts +7 -2
  76. package/dist/reports/src/clock.js +5 -2
  77. package/dist/reports/src/completion-order.d.ts +18 -5
  78. package/dist/reports/src/completion-order.js +18 -5
  79. package/dist/reports/src/compose.d.ts +80 -0
  80. package/dist/reports/src/compose.js +117 -0
  81. package/dist/reports/src/copy-store.d.ts +29 -0
  82. package/dist/reports/src/copy-store.js +52 -0
  83. package/dist/reports/src/directory-outline.d.ts +38 -0
  84. package/dist/reports/src/directory-outline.js +103 -0
  85. package/dist/reports/src/directory-store.d.ts +20 -0
  86. package/dist/reports/src/directory-store.js +166 -0
  87. package/dist/reports/src/doctor.d.ts +29 -0
  88. package/dist/reports/src/doctor.js +83 -0
  89. package/dist/reports/src/effective-delta.d.ts +34 -0
  90. package/dist/reports/src/effective-delta.js +25 -0
  91. package/dist/reports/src/evidence-detail.d.ts +64 -0
  92. package/dist/reports/src/evidence-detail.js +92 -0
  93. package/dist/reports/src/evidence-live.d.ts +35 -5
  94. package/dist/reports/src/evidence-live.js +18 -2
  95. package/dist/reports/src/evidence-types.d.ts +68 -1
  96. package/dist/reports/src/evidence-types.js +5 -1
  97. package/dist/reports/src/evidence-view.d.ts +43 -4
  98. package/dist/reports/src/evidence-view.js +13 -2
  99. package/dist/reports/src/explorer-types.d.ts +40 -3
  100. package/dist/reports/src/explorer-types.js +8 -0
  101. package/dist/reports/src/explorer-versions.d.ts +9 -1
  102. package/dist/reports/src/explorer-versions.js +18 -1
  103. package/dist/reports/src/export-evidence.d.ts +16 -1
  104. package/dist/reports/src/export-evidence.js +52 -1
  105. package/dist/reports/src/fetcher.d.ts +27 -3
  106. package/dist/reports/src/fetcher.js +11 -3
  107. package/dist/reports/src/format-date.d.ts +6 -0
  108. package/dist/reports/src/format-date.js +6 -0
  109. package/dist/reports/src/format.d.ts +16 -0
  110. package/dist/reports/src/format.js +10 -0
  111. package/dist/reports/src/frames.d.ts +36 -3
  112. package/dist/reports/src/frames.js +22 -3
  113. package/dist/reports/src/hash.d.ts +18 -0
  114. package/dist/reports/src/hash.js +13 -0
  115. package/dist/reports/src/host.d.ts +29 -0
  116. package/dist/reports/src/host.js +64 -0
  117. package/dist/reports/src/html-frame.d.ts +5 -6
  118. package/dist/reports/src/html-frame.js +12 -1
  119. package/dist/reports/src/http-store.d.ts +52 -0
  120. package/dist/reports/src/http-store.js +145 -0
  121. package/dist/reports/src/http.d.ts +7 -0
  122. package/dist/reports/src/http.js +6 -0
  123. package/dist/reports/src/ingest.d.ts +54 -0
  124. package/dist/reports/src/ingest.js +210 -0
  125. package/dist/reports/src/init.d.ts +40 -0
  126. package/dist/reports/src/init.js +364 -0
  127. package/dist/reports/src/junit.d.ts +33 -6
  128. package/dist/reports/src/junit.js +87 -21
  129. package/dist/reports/src/keyboard.d.ts +20 -0
  130. package/dist/reports/src/keyboard.js +11 -0
  131. package/dist/reports/src/keys.d.ts +21 -3
  132. package/dist/reports/src/keys.js +21 -3
  133. package/dist/reports/src/layer-axes.d.ts +35 -5
  134. package/dist/reports/src/layer-axes.js +25 -5
  135. package/dist/reports/src/layer-catalog.d.ts +28 -1
  136. package/dist/reports/src/layer-catalog.js +18 -1
  137. package/dist/reports/src/layer-merge.d.ts +98 -9
  138. package/dist/reports/src/layer-merge.js +147 -31
  139. package/dist/reports/src/layer-source.d.ts +18 -0
  140. package/dist/reports/src/layer-source.js +11 -0
  141. package/dist/reports/src/layer-store.d.ts +42 -0
  142. package/dist/reports/src/layer-store.js +63 -5
  143. package/dist/reports/src/layer-types.d.ts +32 -1
  144. package/dist/reports/src/layers-route.d.ts +47 -4
  145. package/dist/reports/src/layers-route.js +86 -7
  146. package/dist/reports/src/media-types.d.ts +11 -0
  147. package/dist/reports/src/media-types.js +22 -0
  148. package/dist/reports/src/namespace-guard.d.ts +10 -1
  149. package/dist/reports/src/namespace-guard.js +10 -1
  150. package/dist/reports/src/navigation.d.ts +12 -0
  151. package/dist/reports/src/navigation.js +7 -0
  152. package/dist/reports/src/object-io.d.ts +11 -4
  153. package/dist/reports/src/object-io.js +4 -4
  154. package/dist/reports/src/object-store-conformance.d.ts +27 -0
  155. package/dist/reports/src/object-store-conformance.js +87 -0
  156. package/dist/reports/src/object-store.d.ts +33 -24
  157. package/dist/reports/src/object-store.js +17 -24
  158. package/dist/reports/src/parse-with.d.ts +16 -0
  159. package/dist/reports/src/parse-with.js +14 -0
  160. package/dist/reports/src/performance-part.d.ts +25 -1
  161. package/dist/reports/src/performance-part.js +15 -1
  162. package/dist/reports/src/performance.d.ts +22 -2
  163. package/dist/reports/src/performance.js +12 -2
  164. package/dist/reports/src/persist.d.ts +12 -3
  165. package/dist/reports/src/persist.js +12 -3
  166. package/dist/reports/src/phone.d.ts +18 -1
  167. package/dist/reports/src/phone.js +18 -1
  168. package/dist/reports/src/protocol.d.ts +26 -4
  169. package/dist/reports/src/protocol.js +15 -4
  170. package/dist/reports/src/publication.d.ts +21 -1
  171. package/dist/reports/src/publication.js +21 -1
  172. package/dist/reports/src/publish-checks.d.ts +35 -0
  173. package/dist/reports/src/publish-checks.js +122 -0
  174. package/dist/reports/src/publish.d.ts +42 -9
  175. package/dist/reports/src/publish.js +36 -7
  176. package/dist/reports/src/read.d.ts +46 -16
  177. package/dist/reports/src/read.js +38 -14
  178. package/dist/reports/src/recording-bundle.d.ts +13 -2
  179. package/dist/reports/src/recording-bundle.js +4 -2
  180. package/dist/reports/src/recording-keys.d.ts +15 -1
  181. package/dist/reports/src/recording-keys.js +15 -1
  182. package/dist/reports/src/remote-report.d.ts +28 -7
  183. package/dist/reports/src/remote-report.js +15 -7
  184. package/dist/reports/src/report-cli.d.ts +51 -0
  185. package/dist/reports/src/report-cli.js +485 -0
  186. package/dist/reports/src/report-index.d.ts +99 -0
  187. package/dist/reports/src/report-index.js +337 -0
  188. package/dist/reports/src/report-loader.d.ts +10 -0
  189. package/dist/reports/src/report-loader.js +6 -0
  190. package/dist/reports/src/report-parts.d.ts +29 -3
  191. package/dist/reports/src/report-parts.js +37 -3
  192. package/dist/reports/src/report-status.d.ts +48 -0
  193. package/dist/reports/src/report-status.js +156 -0
  194. package/dist/reports/src/report-types.d.ts +73 -0
  195. package/dist/reports/src/report-types.js +3 -0
  196. package/dist/reports/src/reporter.d.ts +67 -0
  197. package/dist/reports/src/reporter.js +128 -0
  198. package/dist/reports/src/reports-explorer-route.d.ts +541 -0
  199. package/dist/reports/src/reports-explorer-route.js +11 -0
  200. package/dist/reports/src/reports-list-route.d.ts +9 -0
  201. package/dist/reports/src/reports-list-route.js +9 -0
  202. package/dist/reports/src/reports-route.d.ts +561 -22
  203. package/dist/reports/src/reports-route.js +11 -3
  204. package/dist/reports/src/reports-store.d.ts +19 -2
  205. package/dist/reports/src/reports-store.js +10 -2
  206. package/dist/reports/src/resilient-cache.d.ts +14 -1
  207. package/dist/reports/src/resilient-cache.js +8 -1
  208. package/dist/reports/src/retention.d.ts +17 -1
  209. package/dist/reports/src/retention.js +5 -1
  210. package/dist/reports/src/run-bundle.d.ts +63 -0
  211. package/dist/reports/src/run-bundle.js +159 -0
  212. package/dist/reports/src/s3-store.d.ts +79 -0
  213. package/dist/reports/src/s3-store.js +295 -0
  214. package/dist/reports/src/scenario.d.ts +14 -4
  215. package/dist/reports/src/scenario.js +5 -4
  216. package/dist/reports/src/sigv4.d.ts +72 -0
  217. package/dist/reports/src/sigv4.js +96 -0
  218. package/dist/reports/src/snapshot-bundle.d.ts +24 -1
  219. package/dist/reports/src/snapshot-bundle.js +27 -2
  220. package/dist/reports/src/snapshot-surface.d.ts +20 -9
  221. package/dist/reports/src/snapshot-surface.js +32 -19
  222. package/dist/reports/src/source-content.d.ts +23 -0
  223. package/dist/reports/src/source-content.js +30 -1
  224. package/dist/reports/src/source-entries.d.ts +13 -0
  225. package/dist/reports/src/source-entries.js +6 -0
  226. package/dist/reports/src/source-part.d.ts +15 -0
  227. package/dist/reports/src/source-part.js +8 -0
  228. package/dist/reports/src/source-reader.d.ts +11 -0
  229. package/dist/reports/src/source-reader.js +5 -0
  230. package/dist/reports/src/spec-evidence.d.ts +12 -2
  231. package/dist/reports/src/spec-evidence.js +6 -2
  232. package/dist/reports/src/spec-inventory.d.ts +15 -1
  233. package/dist/reports/src/spec-inventory.js +22 -1
  234. package/dist/reports/src/spec-link.d.ts +28 -0
  235. package/dist/reports/src/spec-link.js +70 -0
  236. package/dist/reports/src/spec-set.d.ts +63 -0
  237. package/dist/reports/src/spec-set.js +177 -0
  238. package/dist/reports/src/status.d.ts +10 -0
  239. package/dist/reports/src/status.js +5 -0
  240. package/dist/reports/src/store-prefix.d.ts +28 -0
  241. package/dist/reports/src/store-prefix.js +22 -0
  242. package/dist/reports/src/store-url.d.ts +89 -0
  243. package/dist/reports/src/store-url.js +160 -0
  244. package/dist/reports/src/test-catalog-entity.d.ts +11 -0
  245. package/dist/reports/src/test-catalog-entity.js +6 -0
  246. package/dist/reports/src/types.d.ts +6 -1
  247. package/dist/reports/src/use-report-nav.d.ts +35 -0
  248. package/dist/reports/src/use-report-nav.js +12 -0
  249. package/dist/schema/src/api.d.ts +799 -0
  250. package/dist/schema/src/api.js +201 -0
  251. package/dist/schema/src/cli-results.d.ts +210 -0
  252. package/dist/schema/src/cli-results.js +124 -0
  253. package/dist/schema/src/common.d.ts +53 -0
  254. package/dist/schema/src/common.js +42 -0
  255. package/dist/schema/src/index.d.ts +14 -0
  256. package/dist/schema/src/index.js +14 -0
  257. package/dist/schema/src/layer.d.ts +2273 -0
  258. package/dist/schema/src/layer.js +462 -0
  259. package/dist/schema/src/manifest.d.ts +95 -0
  260. package/dist/schema/src/manifest.js +84 -0
  261. package/dist/schema/src/report-sources.d.ts +28 -0
  262. package/dist/schema/src/report-sources.js +25 -0
  263. package/dist/schema/src/runs-index.d.ts +16 -0
  264. package/dist/schema/src/runs-index.js +15 -0
  265. package/dist/schema/src/server-config.d.ts +93 -0
  266. package/dist/schema/src/server-config.js +73 -0
  267. package/dist/schema/src/spec.d.ts +69 -0
  268. package/dist/schema/src/spec.js +58 -0
  269. package/dist/server/src/app.d.ts +62 -0
  270. package/dist/server/src/app.js +419 -0
  271. package/dist/server/src/auth.d.ts +41 -0
  272. package/dist/server/src/auth.js +59 -0
  273. package/dist/server/src/config.d.ts +83 -0
  274. package/dist/server/src/config.js +187 -0
  275. package/dist/server/src/dev.d.ts +12 -0
  276. package/dist/server/src/dev.js +80 -0
  277. package/dist/server/src/events.d.ts +29 -0
  278. package/dist/server/src/events.js +101 -0
  279. package/dist/server/src/host.d.ts +30 -0
  280. package/dist/server/src/host.js +115 -0
  281. package/dist/server/src/index.d.ts +16 -0
  282. package/dist/server/src/index.js +16 -0
  283. package/dist/server/src/llms.d.ts +19 -0
  284. package/dist/server/src/llms.js +63 -0
  285. package/dist/server/src/main.d.ts +30 -0
  286. package/dist/server/src/main.js +112 -0
  287. package/dist/server/src/openapi.d.ts +59 -0
  288. package/dist/server/src/openapi.js +374 -0
  289. package/dist/server/src/problem.d.ts +8 -0
  290. package/dist/server/src/problem.js +39 -0
  291. package/dist/server/src/site.d.ts +13 -0
  292. package/dist/server/src/site.js +43 -0
  293. package/dist/server/src/store-api.d.ts +28 -0
  294. package/dist/server/src/store-api.js +159 -0
  295. package/dist/server/src/validate.d.ts +71 -0
  296. package/dist/server/src/validate.js +197 -0
  297. package/dist/vitest/src/index.d.ts +27 -5
  298. package/dist/vitest/src/index.js +18 -5
  299. package/dist/webdriver/src/driver.d.ts +63 -11
  300. package/dist/webdriver/src/driver.js +57 -6
  301. package/dist/webdriver/src/index.d.ts +8 -0
  302. package/dist/webdriver/src/index.js +1 -0
  303. package/dist/webdriver/src/protocol.d.ts +75 -6
  304. package/dist/webdriver/src/protocol.js +79 -6
  305. package/docs/agents.md +137 -0
  306. package/docs/deploying.md +345 -0
  307. package/docs/dogfooding.md +175 -0
  308. package/docs/extending.md +242 -0
  309. package/docs/integrating.md +485 -0
  310. package/docs/reference/bun.md +141 -0
  311. package/docs/reference/cli.md +146 -0
  312. package/docs/reference/core.md +2426 -0
  313. package/docs/reference/detox.md +150 -0
  314. package/docs/reference/http-api.md +303 -0
  315. package/docs/reference/playwright.md +303 -0
  316. package/docs/reference/reports.md +7098 -0
  317. package/docs/reference/schema.md +1651 -0
  318. package/docs/reference/server.md +685 -0
  319. package/docs/reference/vitest.md +45 -0
  320. package/docs/reference/webdriver.md +330 -0
  321. package/docs/report-backends.md +144 -0
  322. package/docs/report-contract.md +400 -0
  323. package/docs/reporting-issues.md +160 -0
  324. package/docs/server.md +285 -0
  325. package/docs/statecharts.md +641 -0
  326. package/llms-full.txt +3506 -0
  327. package/llms.txt +47 -0
  328. package/package.json +32 -4
  329. package/packages/core/README.md +2 -1
  330. package/packages/reports/README.md +65 -0
  331. package/packages/reports/STORAGE.md +4 -0
  332. package/packages/schema/README.md +14 -0
  333. package/packages/schema/schemas/check-detail.schema.json +1032 -0
  334. package/packages/schema/schemas/checks-result.schema.json +26 -0
  335. package/packages/schema/schemas/command-error.schema.json +35 -0
  336. package/packages/schema/schemas/doctor-result.schema.json +60 -0
  337. package/packages/schema/schemas/effective-delta.schema.json +490 -0
  338. package/packages/schema/schemas/effective-evidence.schema.json +471 -0
  339. package/packages/schema/schemas/evidence-page.schema.json +271 -0
  340. package/packages/schema/schemas/hello-event.schema.json +22 -0
  341. package/packages/schema/schemas/init-plan.schema.json +347 -0
  342. package/packages/schema/schemas/layer-rows.schema.json +199 -0
  343. package/packages/schema/schemas/layer.schema.json +2888 -0
  344. package/packages/schema/schemas/merge-result.schema.json +40 -0
  345. package/packages/schema/schemas/outline.schema.json +62 -0
  346. package/packages/schema/schemas/problem.schema.json +37 -0
  347. package/packages/schema/schemas/publish-checks.schema.json +1413 -0
  348. package/packages/schema/schemas/publish-result.schema.json +168 -0
  349. package/packages/schema/schemas/ready.schema.json +68 -0
  350. package/packages/schema/schemas/report-event.schema.json +100 -0
  351. package/packages/schema/schemas/report-status.schema.json +370 -0
  352. package/packages/schema/schemas/server-config.schema.json +194 -0
  353. package/packages/schema/schemas/server-info.schema.json +114 -0
  354. package/packages/schema/schemas/spec-set.schema.json +103 -0
  355. package/packages/schema/schemas/store-listing.schema.json +29 -0
  356. package/packages/schema/schemas/store-write-result.schema.json +28 -0
  357. package/packages/schema/schemas/stream-result.schema.json +59 -0
  358. package/packages/server/README.md +43 -0
  359. package/packages/server/openapi.json +6699 -0
  360. package/packages/visualizer/README.md +21 -0
  361. package/packages/visualizer/src/app/AppShell.tsx +9 -2
  362. package/packages/visualizer/src/app/report-sources.tsx +6 -4
  363. package/packages/visualizer/src/app/router.tsx +3 -3
  364. package/packages/visualizer/src/components/Header.module.css +35 -0
  365. package/packages/visualizer/src/components/Header.tsx +40 -3
  366. package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
  367. package/packages/visualizer/src/data/adapters/index.ts +10 -1
  368. package/packages/visualizer/src/evidence/api.ts +14 -2
  369. package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
  370. package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
  371. package/packages/visualizer/src/evidence/check-detail.tsx +3 -1
  372. package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
  373. package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
  374. package/packages/visualizer/src/find/Finder.tsx +1 -7
  375. package/packages/visualizer/src/find/open.ts +4 -0
  376. package/packages/visualizer/src/live/live-stream.ts +204 -0
  377. package/packages/visualizer/src/runtime.ts +19 -0
  378. package/packages/visualizer/vite/atlas-server-proxy.ts +36 -0
  379. package/packages/visualizer/vite.config.ts +11 -3
package/docs/server.md ADDED
@@ -0,0 +1,285 @@
1
+ # The Atlas server
2
+
3
+ One process per project that **takes in results from every environment, keeps them, and shows everyone the
4
+ combined report, live**. It is a Bun API that serves a Vite-built single-page viewer: one host, one port, in
5
+ development and in production.
6
+
7
+ ```
8
+ laptop ──────┐ ┌── browsers (the viewer: reads /api, listens on /api/events)
9
+ CI job ──────┼── PUT /api/store/… POST /api/checks ─▶│
10
+ device lab ──┘ atlas report publish | stream │ atlas report status (the gate: asks /api/status)
11
+ │
12
+ backends: ./atlas-data (default), S3, other folders, another Atlas server
13
+ ```
14
+
15
+ - **Clients upload directly to it.** `atlas report publish`, `atlas report stream`, `createReporter` and a plain
16
+ `curl` all end up as layers on the server. Nothing else needs deploying: no bucket access for CI, no artifact
17
+ merging, no static site to rebuild.
18
+ - **Its default backend is its own filesystem** (`./atlas-data`). That folder is the whole state: back it up,
19
+ mount it on a volume, or put the state in a bucket instead ([backends](#backends)). Several backends are one
20
+ report, as ever.
21
+ - **The report is live.** Every viewer holds one server-sent-events connection; when results arrive the server
22
+ says so and each viewer reads only what changed ([streaming](#streaming)).
23
+ - **Layered, never partitioned**, as everywhere in Atlas: the server only stores immutable, content-addressed
24
+ layers and works out the report from them. Replace it, restore it, or run a second one on a copy of the data:
25
+ the same layers give the same report ([the contract](report-contract.md)).
26
+
27
+ - [Run it](#run-it) · [Configuration](#configuration) · [Backends](#backends) · [Tokens and roles](#tokens-and-roles)
28
+ - [Sending results](#sending-results) · [Streaming](#streaming) · [API reference](#api-reference)
29
+ - [What the server refuses](#what-the-server-refuses) · [Performance](#performance) · [Operating it](#operating-it)
30
+
31
+ ## Run it
32
+
33
+ ```sh
34
+ bun add -d @crvouga/atlas
35
+ export ATLAS_TOKEN=$(openssl rand -hex 24) # admin token; see "Tokens and roles"
36
+ bunx atlas serve # http://127.0.0.1:8080, data in ./atlas-data
37
+ ```
38
+
39
+ It runs on Bun and on Node 22.18 or later. The first start builds the viewer once into `~/.cache/atlas/site`
40
+ (or serves `./atlas-site`, or `--site <dir>`); the build is the same for every project, because the server writes
41
+ its own address book into the page (`window.__ATLAS_CONFIG__`).
42
+
43
+ **Working on Atlas or on the viewer:** `atlas dev` runs the server on a port of its own and the viewer's Vite dev
44
+ server in front of it. Vite proxies `/api` (server-sent events included) to the server, so the browser, the
45
+ uploaders and the viewer all use **one host**, exactly as in production, and a change to the viewer shows at once.
46
+
47
+ ```sh
48
+ bunx atlas dev --data .atlas-data # http://127.0.0.1:5180
49
+ ATLAS_STORE=http://127.0.0.1:5180 bunx atlas report publish --producer unit --junit out.xml
50
+ ```
51
+
52
+ For containers, Kubernetes and CI see [running Atlas for a project](deploying.md).
53
+
54
+ ## Configuration
55
+
56
+ Everything has a default, so no configuration is a working server for this machine. Settings come from
57
+ `atlas.server.json` (or `--config <file>` / `ATLAS_CONFIG`), then environment variables, then flags; a later one
58
+ wins. A mistake (an unknown key, a short token, two write targets, listening beyond this machine without tokens)
59
+ is said in words and the server does not start. The file has a published
60
+ [JSON Schema](https://github.com/crvouga/atlas/blob/main/packages/schema/schemas/server-config.schema.json)
61
+ (`@crvouga/atlas/schemas/server-config.schema.json`).
62
+
63
+ ```json
64
+ {
65
+ "$schema": "./node_modules/@crvouga/atlas/packages/schema/schemas/server-config.schema.json",
66
+ "host": "0.0.0.0",
67
+ "port": 8080,
68
+ "data": "./atlas-data",
69
+ "backends": [{ "id": "archive", "url": "s3://my-bucket/atlas/my-project?region=us-east-1", "label": "Archive" }],
70
+ "write": "data",
71
+ "auth": {
72
+ "tokens": [
73
+ { "name": "ci", "role": "publish", "tokenEnv": "ATLAS_CI_TOKEN" },
74
+ { "name": "dashboard", "role": "read", "tokenEnv": "ATLAS_DASHBOARD_TOKEN" }
75
+ ]
76
+ },
77
+ "include": { "branches": ["main"], "ignoreModified": true },
78
+ "refreshSeconds": 10,
79
+ "limits": { "maxObjectMiB": 256, "maxJsonMiB": 32, "maxStreams": 500 }
80
+ }
81
+ ```
82
+
83
+ <!-- generated:server-settings -->
84
+ <!-- /generated:server-settings -->
85
+
86
+ `ATLAS_HOST` and `ATLAS_PORT` win over `HOST` and `PORT`, which other tools on a machine often set for their own
87
+ purposes.
88
+
89
+ ## Backends
90
+
91
+ A backend is anything that implements `ObjectStore`: a folder, an S3-compatible bucket (`s3://`, `r2://`,
92
+ `gs://`), or **another Atlas server** (`https://…`, with `ATLAS_TOKEN` for it). The server reads every backend
93
+ and shows one report; one that is down is named in the viewer and skipped, and the server recovers by itself.
94
+
95
+ - **Uploads go to one backend**: the `data` folder, or the one named by `write` / `ATLAS_WRITE`. Name a bucket
96
+ to make it the system of record, so the server holds nothing a replacement would lose:
97
+
98
+ ```sh
99
+ ATLAS_STORE=project=s3://my-bucket/atlas/my-project ATLAS_WRITE=project atlas serve
100
+ ```
101
+
102
+ - **Layers written to a backend by something else** (CI with bucket access, `atlas report merge`) are found at
103
+ the next refresh. Layers uploaded through the server are in the report at once.
104
+ - **A server can be a backend of another**: a local server reading the project's shared one shows both.
105
+
106
+ ## Tokens and roles
107
+
108
+ | Role | May |
109
+ | --- | --- |
110
+ | `read` | look: the viewer, the report, `/api/status` |
111
+ | `publish` | also upload layers, specs and runs, and remove the scratch objects `atlas report doctor` writes |
112
+ | `admin` | all server operations |
113
+
114
+ Tokens are at least 12 characters (`openssl rand -hex 24`) and different from one another. Give them as
115
+ `ATLAS_TOKEN` (admin), `ATLAS_PUBLISH_TOKEN`, `ATLAS_READ_TOKEN`, or under `auth.tokens` in the file, preferably
116
+ with `tokenEnv` so the file holds no secret.
117
+
118
+ - **Sending a token:** `Authorization: Bearer <token>`. A browser's sign-in box also works for *looking* (any
119
+ user name, the token as the password). **Uploading needs the Bearer header, never the sign-in box**: a browser
120
+ sends those credentials by itself to any page that asks, which would let another site upload with them.
121
+ - **No tokens** means open, and then the server refuses to listen beyond this machine unless told
122
+ `--insecure` (for when a proxy in front does the signing in).
123
+ - `/healthz` and `/readyz` never need a token, so a load balancer can use them.
124
+ - Only this server's own clients need tokens; Atlas does not do accounts. Put it behind what your company already
125
+ uses (Cloudflare Access, an ALB with OIDC, `oauth2-proxy`, Tailscale) and TLS ends there too.
126
+
127
+ ## Sending results
128
+
129
+ Every route ends as the same immutable layers, so use what fits:
130
+
131
+ | From | How |
132
+ | --- | --- |
133
+ | a CI job or laptop, after the run | `ATLAS_STORE=https://atlas.example.com ATLAS_TOKEN=… atlas report publish --producer unit --junit out.xml --specs ./specs` |
134
+ | a suite that can print JSON lines | `my-suite --json \| atlas report stream --producer e2e --environment ios` |
135
+ | code | `createReporter(identity, { store: httpStore({ url, token }) })`, below |
136
+ | anything that can make a request | `POST /api/checks` with a JSON body |
137
+ | a folder of layers you already have | `atlas report merge --store ./layers --into https://atlas.example.com` |
138
+
139
+ `ATLAS_STORE` may name the server instead of a folder or bucket: **every `atlas report` command works against it**
140
+ (`publish`, `stream`, `merge --into`, `doctor`, `status`). Keys never go in the URL; put the token in
141
+ `ATLAS_TOKEN` (or the first line of the file named by `ATLAS_TOKEN_FILE`). **A token belongs to one server.** With
142
+ one server in `ATLAS_STORE`, `ATLAS_TOKEN` is its token; with several, name each (`lab=https://lab.example.com`) and give
143
+ it `ATLAS_TOKEN_LAB`, because `ATLAS_TOKEN` is then sent to none of them. A token is never sent over plain `http://`
144
+ beyond this machine (`ATLAS_TRUST_NETWORK=1` says the network is yours).
145
+
146
+ ### Streaming a run as it happens
147
+
148
+ `atlas report stream` reads one check per line (the fields of a check: `id`, `title`, `status`, and any
149
+ [evidence](extending.md)) and sends what has finished every second as a layer. A check sent first as `running` and
150
+ then as `passed` is two layers; the later, completed one wins, so the report follows the suite while it runs and
151
+ agrees with it when it ends. A layer that cannot be sent is kept and sent again, byte for byte the same, so a
152
+ retry never makes a second layer. It exits 1 if anything could not be sent.
153
+
154
+ ```ts
155
+ import { createReporter } from '@crvouga/atlas/reports/reporter';
156
+ import { httpStore } from '@crvouga/atlas/reports/http-store';
157
+
158
+ const reporter = createReporter(
159
+ { producer: 'web-e2e', environment: 'desktop-web', branch: process.env.GITHUB_REF_NAME ?? null },
160
+ { store: httpStore({ url: 'https://atlas.example.com', token: process.env.ATLAS_TOKEN }), flushMs: 1000 }
161
+ );
162
+ reporter.check({ id: 'checkout > pays', title: 'pays', status: 'running', specPath: 'specs/checkout.feature' });
163
+ // …
164
+ reporter.check({ id: 'checkout > pays', title: 'pays', status: 'passed', specPath: 'specs/checkout.feature', attachments: [{ name: 'end.png', data: png }] });
165
+ const { layers, checks, lost } = await reporter.close(); // never throws into the suite; `lost` counts what never arrived
166
+ ```
167
+
168
+ ### `POST /api/checks`
169
+
170
+ ```sh
171
+ curl -X POST https://atlas.example.com/api/checks -H "authorization: Bearer $ATLAS_TOKEN" -d '{
172
+ "producer": "smoke", "environment": "prod",
173
+ "checks": [{ "id": "login", "title": "can log in", "status": "passed" }]
174
+ }'
175
+ # 201 {"ok":true,"id":"…","checks":1}
176
+ ```
177
+
178
+ The body is [the checks file](integrating.md) (`recordedAtMs` defaults to now). Binary evidence goes in as
179
+ `base64`. Anything wrong is a `422` that says what.
180
+
181
+ ### The store protocol
182
+
183
+ `httpStore` is an `ObjectStore` over `/api/store`, so `publishChecks`, spec sets, run uploads and `copyStore` work
184
+ against a server unchanged. A client uploads **in dependency order**: blobs, then the manifest, then the catalog
185
+ entry that makes it visible. The server checks each step and says no to a catalog entry whose manifest is not
186
+ there (`409`), so nothing half-uploaded is ever shown.
187
+
188
+ ## Streaming
189
+
190
+ `GET /api/events` is a server-sent-events stream:
191
+
192
+ | Event | Data | Meaning |
193
+ | --- | --- | --- |
194
+ | `hello` | `{ version, ready, time }` | Sent first. The report's current version. |
195
+ | `report` | `{ version, totalLayers, added: [{ id, producer, environment, branch, sha, recordedAtMs, counts }], addedTotal }` | Layers arrived. Enough to say who sent what, without asking. |
196
+ | `atlas:change` | `{ scope: "specs" \| "runs", files, runIds }` | New specs or runs. |
197
+ | `ping` | `{}` | Every 15 s, so proxies keep the connection and the viewer can tell a quiet server from a dead connection. |
198
+
199
+ Events say *that* something changed; the state is always fetched. A viewer that missed some (a dropped
200
+ connection, a laptop that slept) loses nothing: it reads again when it reconnects, and the viewer also reads on a
201
+ slow timer behind the stream. Past `limits.maxStreams` viewers the server answers `503` with `Retry-After` and the
202
+ viewer polls instead; a viewer that stops reading is let go rather than held in memory.
203
+
204
+ ### Reading only what changed
205
+
206
+ `GET /api/reports/evidence/effective` is the whole report: only the layers that win a check, each with only the
207
+ rows that win, with an ETag. A viewer that holds a version asks `?since=<version>` and is sent **only the layers
208
+ that changed since, and the ids that no longer win anything** (`"delta": true`). Applying that gives exactly the
209
+ report the server would have sent whole (`applyEffectiveDelta`, tested against every earlier version of random
210
+ runs); a version the server does not remember, or an older server, answers with the whole report, which a viewer
211
+ takes as it always did. A result streamed into a 100 000-check report costs a viewer a few kilobytes.
212
+
213
+ ## API reference
214
+
215
+ <!-- generated:server-operations -->
216
+ <!-- /generated:server-operations -->
217
+
218
+ Errors are always [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents (`application/problem+json`: `type`,
219
+ `title`, `status`, and a `detail` that says what to do; `error` repeats `detail` for older clients) with a status that says
220
+ what to do: `401` send a token, `403` that token may not, `409` upload what this depends on first or you are changing
221
+ something immutable, `413` too large, `422` not valid, `503` still starting (see `Retry-After`).
222
+
223
+ **For agents:** a server describes itself. `GET /api/openapi.json` is the OpenAPI 3.1 description of everything above (also
224
+ shipped as `@crvouga/atlas/openapi.json`) and `GET /llms.txt` is a guide for an agent that was handed only the server's address;
225
+ neither needs a token. See [Atlas for AI agents](agents.md).
226
+
227
+ ## What the server refuses
228
+
229
+ What is checked is what would otherwise corrupt the report for everyone, and it is checked from the bytes in hand:
230
+
231
+ - **Everything is named by its content.** A manifest whose content does not hash to its id (it was edited, or written
232
+ by something that does not follow the format) is refused, as is one that is not a valid layer; so is a blob
233
+ (the digest of what is in it) or a spec file that is not what its name says.
234
+ - **Dependencies come first.** A manifest needs its blobs; a catalog entry needs its manifest; a spec set needs its
235
+ files; a run's catalog entry needs its manifest.
236
+ - **Layers are immutable.** The same bytes again are fine (a retry); other bytes under the same key are a `409`.
237
+ - **A run that did not pass its privacy scan is never shared.**
238
+ - **Only keys Atlas writes**, under `layers/`, without traversal, backslashes or control characters.
239
+ - **Size.** Bodies over `limits.maxObjectMiB` are refused as they arrive, not after they were held.
240
+ - **Attachment bytes are untrusted.** They are served only through one function that forces a safe type and a
241
+ sandbox, whatever the producer claimed.
242
+
243
+ ## Performance
244
+
245
+ `bun packages/server/bench/bench.ts [layers] [checks]` seeds a long history and measures the server on it. On a
246
+ laptop with 2 000 layers of 100 checks (200 000 results, 10 000 slots) and with 1 000 layers of 1 000 checks
247
+ (100 000 slots, a 22 MB report):
248
+
249
+ | | 10 000 slots | 100 000 slots |
250
+ | --- | --- | --- |
251
+ | Start: read and fold every layer | 0.4 s | 1.7 s |
252
+ | A refresh with nothing new | 4 ms | 4 ms |
253
+ | Upload one layer → every viewer told | 4 ms | 5 ms |
254
+ | `GET effective`, cached / `304` | 1 ms / 0.2 ms | 13 ms / 0.5 ms |
255
+ | `GET status`, cached for the version | 12 ms first | 70 ms first |
256
+
257
+ What makes it so, none of it at the cost of the contract:
258
+
259
+ - **A layer is read once.** The report is folded incrementally; only layers that still hold a winner are kept, so
260
+ memory follows the number of checks and not the age of the project.
261
+ - **A change costs one layer.** Each layer remembers the JSON of what it sends, the version is a hash of per-layer
262
+ digests, and a delta lists only layers that changed, so the work to take in a result does not grow with the
263
+ report.
264
+ - **An upload is read once.** A layer's manifest is parsed and verified when it arrives and handed to the report,
265
+ not read again; blob existence is asked 16 at a time.
266
+ - **The newest end of the catalog is all a refresh looks at**, with a full look now and then.
267
+ - **Compressed once.** The viewer's files are brotli-compressed once (gzip for clients that cannot read it), a
268
+ report version is gzipped once however many viewers fetch it, and events are encoded once for every viewer.
269
+ - **A small viewer.** The graph, the Gherkin parser, the recording player and the finder load when used: the
270
+ first page is about 250 KB over the wire.
271
+
272
+ ## Operating it
273
+
274
+ - **One server per project.** It is a singleton by design: local environments and CI all talk to it, and it tells
275
+ every viewer at once. Two servers on one backend both work and show the same report, but a viewer hears about
276
+ an upload only from the server that took it and about the other's at the next refresh, so give a team one.
277
+ - **State is the write backend.** Back up `./atlas-data` (or the bucket). Restoring it, or starting a new server
278
+ on it, shows the same report.
279
+ - **Stopping.** `SIGTERM` stops accepting, lets requests in flight finish, ends the event streams and exits 0.
280
+ - **Logs.** One JSON line per request (method, path, status, ms) to stdout, none for the probes. The first lines
281
+ say what it reads and which backend takes uploads, and warn when there are no tokens.
282
+ - **A backend goes down.** The server keeps serving what it has, names the backend in the viewer and in
283
+ `/readyz`, and recovers by itself. The gate fails closed: unknown is not a pass.
284
+ - **Upgrades.** The layer format is a stable, versioned contract; upgrade the server before the clients that
285
+ publish when a release says the format moved.