forgecad 0.9.14 → 0.9.15

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 (219) hide show
  1. package/LICENSE +6 -4
  2. package/README.md +8 -4
  3. package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CDyGUinA.js} +2 -2
  4. package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-DfPMY_-d.js} +4 -15
  5. package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-kF0fkdJT.js} +2 -2
  6. package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-B954L3YN.js} +9 -3
  7. package/dist/assets/EditorApp-Beb-IZ0y.js +14014 -0
  8. package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-CuDLxKqL.css} +698 -0
  9. package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-C77B-TrF.js} +3 -3
  10. package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-Cr6fXMDj.js} +35 -37
  11. package/dist/assets/LegalPage-BRlScr9A.css +91 -0
  12. package/dist/assets/LegalPage-Dzklqmmg.js +39 -0
  13. package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
  14. package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-zWXkvlwl.js} +19 -19
  15. package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-Bz0of4KQ.js} +2 -2
  16. package/dist/assets/app-CE3sYcV7.css +3890 -0
  17. package/dist/assets/{app-bEww1ic4.js → app-D3kDkggg.js} +2293 -946
  18. package/dist/assets/cli/{render-Cho2uKG_.js → render-DSY3mMQa.js} +337 -7
  19. package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-gpDo-uH2.js} +927 -243
  20. package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-CU0Ke6DP.js} +7800 -4164
  21. package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
  22. package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
  23. package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-COyp8XXA.js} +927 -243
  24. package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
  25. package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
  26. package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
  27. package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
  28. package/dist/assets/{manifold-CG9Fokx-.js → manifold-BRI5prcH.js} +1 -1
  29. package/dist/assets/{manifold-uRzgk5O8.js → manifold-C-3h2M7p.js} +2 -2
  30. package/dist/assets/{manifold-rmfAcdwF.js → manifold-DNkrUWpA.js} +1 -1
  31. package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-CdBz5bNg.js} +7538 -10857
  32. package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-wJF98aY9.js} +6935 -4331
  33. package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-B-9VbLIs.js} +32 -1
  34. package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
  35. package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
  36. package/dist/assets/{targets-D6PWsv6X.js → targets-B9sGB5nB.js} +1 -1
  37. package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
  38. package/dist/cli/render.html +1 -1
  39. package/dist/docs/index.html +2 -2
  40. package/dist/docs-raw/AI/ai-native-cad.md +50 -0
  41. package/dist/docs-raw/AI/usage.md +3 -12
  42. package/dist/docs-raw/CLI.md +30 -10
  43. package/dist/docs-raw/component-model.md +27 -11
  44. package/dist/docs-raw/generated/assembly.md +301 -212
  45. package/dist/docs-raw/generated/concepts.md +235 -237
  46. package/dist/docs-raw/generated/core.md +283 -6
  47. package/dist/docs-raw/generated/curves.md +274 -361
  48. package/dist/docs-raw/generated/lib.md +7 -1
  49. package/dist/docs-raw/generated/output.md +19 -4
  50. package/dist/docs-raw/generated/runtime-names.md +41 -0
  51. package/dist/docs-raw/generated/sdf.md +31 -0
  52. package/dist/docs-raw/generated/sheet-metal.md +9 -0
  53. package/dist/docs-raw/generated/sketch.md +44 -1
  54. package/dist/docs-raw/generated/viewport.md +11 -3
  55. package/dist/docs-raw/guides/coordinate-system.md +20 -16
  56. package/dist/docs-raw/guides/geometry-conventions.md +2 -2
  57. package/dist/docs-raw/guides/inspection-bundles.md +2 -1
  58. package/dist/docs-raw/guides/joint-design.md +24 -0
  59. package/dist/docs-raw/guides/positioning.md +13 -3
  60. package/dist/docs-raw/legal/privacy.md +63 -0
  61. package/dist/docs-raw/legal/software-license.md +55 -0
  62. package/dist/docs-raw/legal/terms.md +87 -0
  63. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
  64. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  65. package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
  66. package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
  67. package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
  68. package/dist/docs-raw/skills/forgecad-lld.md +1 -1
  69. package/dist/docs-raw/skills/forgecad-make-a-model.md +1 -1
  70. package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
  71. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
  72. package/dist/docs-raw/skills/forgecad-project.md +1 -1
  73. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
  74. package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
  75. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  76. package/dist/docs-raw/skills/forgecad.md +4 -3
  77. package/dist/index.html +40 -12
  78. package/dist/llms.txt +8 -0
  79. package/dist/site.webmanifest +1 -1
  80. package/dist/sitemap.xml +49 -13
  81. package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SDX5QIXI.js} +1 -2
  82. package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-EAYEFT77.js} +1 -2
  83. package/dist-cli/{chunk-EXWGNL6K.js → chunk-N4O47JLF.js} +12540 -9046
  84. package/dist-cli/forgecad.js +1786 -679
  85. package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
  86. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  87. package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
  88. package/dist-cli/solver_bg.wasm +0 -0
  89. package/dist-skill/CONTEXT.md +1117 -721
  90. package/dist-skill/SKILL.md +3 -2
  91. package/dist-skill/docs/API/core/concepts.md +64 -1
  92. package/dist-skill/docs/CLI.md +30 -10
  93. package/dist-skill/docs/generated/assembly.md +277 -229
  94. package/dist-skill/docs/generated/core.md +283 -6
  95. package/dist-skill/docs/generated/curves.md +272 -362
  96. package/dist-skill/docs/generated/lib.md +7 -1
  97. package/dist-skill/docs/generated/output.md +19 -4
  98. package/dist-skill/docs/generated/runtime-names.md +41 -0
  99. package/dist-skill/docs/generated/sdf.md +31 -0
  100. package/dist-skill/docs/generated/sheet-metal.md +9 -0
  101. package/dist-skill/docs/generated/sketch.md +44 -2
  102. package/dist-skill/docs/generated/viewport.md +2 -87
  103. package/dist-skill/docs/guides/coordinate-system.md +20 -16
  104. package/dist-skill/docs/guides/geometry-conventions.md +2 -2
  105. package/dist-skill/docs/guides/inspection-bundles.md +2 -1
  106. package/dist-skill/docs/guides/joint-design.md +24 -0
  107. package/dist-skill/docs/guides/positioning.md +13 -3
  108. package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
  109. package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
  110. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
  111. package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
  112. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  113. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  114. package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
  115. package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
  116. package/examples/api/assembly-kinematics-limb.forge.js +116 -0
  117. package/examples/api/connector-frame-rig-chain.forge.js +102 -0
  118. package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
  119. package/examples/api/exact-surface-studio.forge.js +6 -8
  120. package/examples/api/helix-basics.forge.js +6 -6
  121. package/examples/api/lean-foundations/README.md +12 -0
  122. package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
  123. package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
  124. package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
  125. package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
  126. package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
  127. package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
  128. package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
  129. package/examples/api/route3d-elbow.forge.js +68 -0
  130. package/examples/api/transition-curves.forge.js +44 -15
  131. package/examples/api/y-blend-corner-showcase.forge.js +0 -2
  132. package/examples/generative/coral-vase.forge.js +1 -1
  133. package/examples/nurbs-tube.forge.js +1 -1
  134. package/package.json +14 -13
  135. package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
  136. package/dist/assets/app-CsHnaBWt.css +0 -1789
  137. package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
  138. package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
  139. package/dist/docs-raw/API/README.md +0 -16
  140. package/dist/docs-raw/API/core/concepts.md +0 -118
  141. package/dist/docs-raw/INDEX.md +0 -138
  142. package/dist/docs-raw/RELEASING.md +0 -87
  143. package/dist/docs-raw/agent-native-api.md +0 -27
  144. package/dist/docs-raw/beta-deployment.md +0 -304
  145. package/dist/docs-raw/beta-operations.md +0 -325
  146. package/dist/docs-raw/blueprint-first.md +0 -145
  147. package/dist/docs-raw/cli-monetization.md +0 -112
  148. package/dist/docs-raw/coding-best-practices.md +0 -120
  149. package/dist/docs-raw/coding.md +0 -340
  150. package/dist/docs-raw/deployment.md +0 -374
  151. package/dist/docs-raw/guides/skill-maintenance.md +0 -161
  152. package/dist/docs-raw/guides/surface-members.md +0 -82
  153. package/dist/docs-raw/harbor-cli.md +0 -854
  154. package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
  155. package/dist/docs-raw/internals/compiler.md +0 -307
  156. package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
  157. package/dist/docs-raw/internals/constraint-solver.md +0 -176
  158. package/dist/docs-raw/internals/shape-from-slices.md +0 -152
  159. package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
  160. package/dist/docs-raw/platform/admin.md +0 -45
  161. package/dist/docs-raw/platform/architecture.md +0 -82
  162. package/dist/docs-raw/platform/auth.md +0 -139
  163. package/dist/docs-raw/platform/email.md +0 -67
  164. package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
  165. package/dist/docs-raw/platform/observability.md +0 -197
  166. package/dist/docs-raw/platform/projects.md +0 -111
  167. package/dist/docs-raw/platform/sharing.md +0 -90
  168. package/dist/docs-raw/product/README.md +0 -39
  169. package/dist/docs-raw/product/api-as-product-language.md +0 -13
  170. package/dist/docs-raw/product/business-model.md +0 -15
  171. package/dist/docs-raw/product/competitive-positioning.md +0 -17
  172. package/dist/docs-raw/product/creative-manufacturing.md +0 -15
  173. package/dist/docs-raw/product/founder-story.md +0 -11
  174. package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
  175. package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
  176. package/dist/docs-raw/product/product-loop.md +0 -17
  177. package/dist/docs-raw/product/strategic-decisions.md +0 -22
  178. package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
  179. package/dist/docs-raw/product/user-segments.md +0 -15
  180. package/dist/docs-raw/product/vision.md +0 -26
  181. package/dist/docs-raw/rl-environments.md +0 -350
  182. package/dist/docs-raw/runbook.md +0 -611
  183. package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
  184. package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
  185. package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
  186. package/dist-cli/forgecad.js.map +0 -1
  187. package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
  188. package/dist-cli/solver-46FFSK2U.js.map +0 -1
  189. package/dist-skill/SKILL-dev.md +0 -145
  190. package/dist-skill/docs-dev/API/core/concepts.md +0 -118
  191. package/dist-skill/docs-dev/CLI.md +0 -677
  192. package/dist-skill/docs-dev/agent-native-api.md +0 -27
  193. package/dist-skill/docs-dev/blueprint-first.md +0 -145
  194. package/dist-skill/docs-dev/coding-best-practices.md +0 -120
  195. package/dist-skill/docs-dev/coding.md +0 -340
  196. package/dist-skill/docs-dev/component-model.md +0 -164
  197. package/dist-skill/docs-dev/generated/assembly.md +0 -794
  198. package/dist-skill/docs-dev/generated/core.md +0 -2117
  199. package/dist-skill/docs-dev/generated/curves.md +0 -2583
  200. package/dist-skill/docs-dev/generated/lib.md +0 -169
  201. package/dist-skill/docs-dev/generated/output.md +0 -247
  202. package/dist-skill/docs-dev/generated/sdf.md +0 -446
  203. package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
  204. package/dist-skill/docs-dev/generated/sketch.md +0 -1811
  205. package/dist-skill/docs-dev/generated/viewport.md +0 -585
  206. package/dist-skill/docs-dev/generated/wood.md +0 -108
  207. package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
  208. package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
  209. package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
  210. package/dist-skill/docs-dev/guides/joint-design.md +0 -78
  211. package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
  212. package/dist-skill/docs-dev/guides/positioning.md +0 -161
  213. package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
  214. package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
  215. package/dist-skill/docs-dev/internals/compiler.md +0 -307
  216. package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
  217. package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
  218. package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
  219. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
@@ -1,611 +0,0 @@
1
- # ForgeCAD Hosted Service — Runbook
2
-
3
- Operational reference for running, testing, and troubleshooting the forgecad.io hosted service.
4
-
5
- For the full documentation index see [INDEX.md](./INDEX.md).
6
- For environment variables, Docker setup, and deployment details see [deployment.md](deployment.md).
7
- For dashboards, logs, metrics, and observability access see [platform/observability.md](platform/observability.md).
8
- For releasing new versions see [RELEASING.md](./RELEASING.md).
9
- For platform feature docs (auth, projects, sharing, admin, email) see [platform/](./platform/).
10
-
11
- ---
12
-
13
- ## Quick Reference
14
-
15
- | What | Command |
16
- |------|---------|
17
- | Start local DB | `npm run dev:db` |
18
- | Stop local DB | `npm run dev:db:stop` |
19
- | Start API server | `npm run dev:server` |
20
- | Start frontend (with API proxy) | `npm run dev:studio` |
21
- | Run API tests | `bash scripts/test-shares-api.sh` |
22
- | Run unit tests | `npm run test:unit` |
23
- | Run full test suite | `npm test` |
24
- | Health check | `curl http://localhost:5174/api/health` |
25
- | SSH to production | `ssh hetzner` |
26
- | Open observability UI | Connect to Tailscale, then open `http://100.118.68.93:3000` |
27
- | Check observability stack | `bash scripts/prod/observability-status.sh` |
28
- | Roll back bad web deploy | `npm run prod:rollback -- <VERSION>` |
29
- | Verify npm tarball in Docker | `npm run release:docker:all` |
30
-
31
- ---
32
-
33
- ## NPM Release Docker Verification
34
-
35
- Use this runbook before publishing a CLI release, after unpublishing/rolling back a bad npm version, or whenever a bug only appears in a clean global install.
36
-
37
- The check builds the exact npm tarball, recreates a clean `node:22-slim` container, copies the tarball and a model into it, installs the tarball globally, and runs `forgecad run` inside the container. Stop immediately if any step fails.
38
-
39
- ### One-command check
40
-
41
- ```bash
42
- npm run release:docker:all
43
- ```
44
-
45
- Default behavior:
46
-
47
- | Setting | Default |
48
- |---------|---------|
49
- | Container | `forgecad-release-check` |
50
- | Docker image | `node:22-slim` |
51
- | Browser URL | `http://localhost:4173` |
52
- | Studio port mapping | host `4173` -> container `5173` |
53
- | Tarball output | `dist-bundles/npm-release-check/forgecad-<version>.tgz` |
54
- | Smoke model | `examples/api/verification-demo.forge.js` |
55
- | Run timeout | `20s` |
56
-
57
- For a known repro model, override the model path:
58
-
59
- ```bash
60
- FORGECAD_RELEASE_MODEL=/Users/kostard/Downloads/plant-pot-holder-marble.forge.js \
61
- npm run release:docker:all
62
- ```
63
-
64
- To reuse a familiar container name:
65
-
66
- ```bash
67
- FORGECAD_RELEASE_CONTAINER=forgecad-debug \
68
- FORGECAD_RELEASE_MODEL=/Users/kostard/Downloads/plant-pot-holder-marble.forge.js \
69
- npm run release:docker:all
70
- ```
71
-
72
- ### Step-by-step check
73
-
74
- Run the lane manually when you want to inspect a specific stage:
75
-
76
- ```bash
77
- npm run release:pack
78
- npm run release:docker:create
79
- npm run release:docker:copy
80
- npm run release:docker:install
81
- npm run release:docker:run
82
- ```
83
-
84
- What each step does:
85
-
86
- | Step | Purpose |
87
- |------|---------|
88
- | `release:pack` | Runs `npm pack` and writes the tarball under `dist-bundles/npm-release-check/`. |
89
- | `release:docker:create` | Removes any existing release-check container and starts a fresh `node:22-slim` container. |
90
- | `release:docker:copy` | Copies the tarball to `/tmp/forgecad-release.tgz` and the model to `/tmp/<model-name>` in the container. |
91
- | `release:docker:install` | Runs `npm install -g /tmp/forgecad-release.tgz` inside the container and prints `forgecad --version`. |
92
- | `release:docker:run` | Runs `forgecad run /tmp/<model-name>` with update checks disabled and a timeout. |
93
- | `release:docker:studio` | Starts `forgecad studio /tmp --host 0.0.0.0` in the container. Open `http://localhost:4173` on the host. |
94
-
95
- Expected success signal:
96
-
97
- ```text
98
- forgecad --version
99
- # <new version>
100
-
101
- ✓ Objects: ...
102
- ✓ Verifications: ... pass, 0 fail
103
- ✓ Time: ...
104
- ```
105
-
106
- ### Manual checks inside the container
107
-
108
- After `release:docker:install`, open a shell for interactive checks:
109
-
110
- ```bash
111
- docker exec -it forgecad-release-check bash
112
- forgecad --version
113
- forgecad login
114
- forgecad run /tmp/verification-demo.forge.js
115
- ```
116
-
117
- If you used a custom container name, replace `forgecad-release-check`.
118
-
119
- To open Studio in your host browser without changing the internal ForgeCAD port:
120
-
121
- ```bash
122
- npm run release:docker:studio
123
- ```
124
-
125
- Then open:
126
-
127
- ```text
128
- http://localhost:4173
129
- ```
130
-
131
- The helper publishes host port `4173` to the container's normal Studio port `5173`, so `forgecad studio` still runs on its default internal port.
132
-
133
- ### Cleanup
134
-
135
- Remove the release-check container when done:
136
-
137
- ```bash
138
- npm run release:docker:clean
139
- ```
140
-
141
- The tarball output directory is under ignored `dist-bundles/`, but it can still consume local disk. Remove it when space is tight:
142
-
143
- ```bash
144
- rm -rf dist-bundles/npm-release-check
145
- ```
146
-
147
- ### Useful overrides
148
-
149
- | Variable | Use |
150
- |----------|-----|
151
- | `FORGECAD_RELEASE_CONTAINER` | Container name to create/use. |
152
- | `FORGECAD_RELEASE_IMAGE` | Docker image, for example `node:20-slim` or `node:22-slim`. |
153
- | `FORGECAD_RELEASE_HOST_PORT` | Host browser port, default `4173`. |
154
- | `FORGECAD_RELEASE_CONTAINER_PORT` | Container Studio port, default `5173`. |
155
- | `FORGECAD_RELEASE_STUDIO_PROJECT` | Project path inside the container for `release:docker:studio`, default `/tmp`. |
156
- | `FORGECAD_RELEASE_PACK_DIR` | Local tarball output directory. |
157
- | `FORGECAD_RELEASE_TARBALL` | Existing tarball to copy instead of the default output path. |
158
- | `FORGECAD_RELEASE_MODEL` | Model path to copy and run. |
159
- | `FORGECAD_RELEASE_TIMEOUT_SECONDS` | Timeout for `forgecad run` inside the container. |
160
-
161
- ---
162
-
163
- ## Local Development Stack
164
-
165
- ### Minimal (editor only, no auth)
166
-
167
- ```bash
168
- npm run dev
169
- ```
170
-
171
- No database, no login. For geometry, sketches, viewer work.
172
-
173
- ### Full stack (auth, projects, sharing)
174
-
175
- Three terminals:
176
-
177
- ```bash
178
- # 1. Database
179
- npm run dev:db
180
-
181
- # 2. API server (port 5174)
182
- npm run dev:server
183
-
184
- # 3. Frontend (port 5173, proxies /api → 5174)
185
- npm run dev:studio
186
- ```
187
-
188
- Open http://localhost:5173. Dev credentials are hardcoded in `dev:server` — see [deployment.md](deployment.md#local-development).
189
-
190
- ### Verify the stack is healthy
191
-
192
- ```bash
193
- # API health
194
- curl -s http://localhost:5174/api/health | python3 -m json.tool
195
-
196
- # Database
197
- docker exec forgecad-postgresql-1 psql -U forgecad -d forgecad -c "SELECT count(*) FROM users;"
198
-
199
- # Migration state
200
- docker exec forgecad-postgresql-1 psql -U forgecad -d forgecad -c "SELECT * FROM _migrations ORDER BY id;"
201
- ```
202
-
203
- ---
204
-
205
- ## Testing
206
-
207
- ### API tests (end-to-end, from terminal)
208
-
209
- These scripts exercise the real server — no mocks, real HTTP, real database.
210
-
211
- | Script | What it tests | Prerequisites |
212
- |--------|--------------|---------------|
213
- | `scripts/test-shares-api.sh` | Publish, fetch, update, unpublish, auth enforcement | `dev:db` + `dev:server` |
214
-
215
- Run:
216
-
217
- ```bash
218
- bash scripts/test-shares-api.sh
219
- ```
220
-
221
- The script registers a fresh test user, runs through the full lifecycle, and cleans up. Each run uses a unique email so it's safe to re-run.
222
-
223
- ### Adding new API test scripts
224
-
225
- Follow the pattern in `test-shares-api.sh`:
226
-
227
- 1. Name: `scripts/test-<feature>-api.sh`
228
- 2. Check server health first (`/api/health`)
229
- 3. Register a unique test user per run
230
- 4. Use `acurl()` helper for authenticated requests
231
- 5. Assert HTTP status codes and response fields
232
- 6. Clean up (delete created resources)
233
- 7. Print colored pass/fail per step
234
-
235
- ### Unit tests
236
-
237
- ```bash
238
- npm run test:unit # vitest
239
- npm test # smoke invariant suite
240
- npm run test:smoke # same as npm test
241
- npm run test:full # broader regression sweep
242
- npm run check:suite # repo invariant suite
243
- npm run check:suite -- --profile smoke
244
- npm run test:examples # maintained fast example smoke lane
245
- npm run test:examples:full # full example catalog sweep
246
- ```
247
-
248
- ### Invariant checks (CLI)
249
-
250
- ```bash
251
- node dist-cli/forgecad.js check examples
252
- node dist-cli/forgecad.js check examples --profile smoke
253
- node dist-cli/forgecad.js check examples --profile full
254
- ```
255
-
256
- Use the smoke profiles for everyday merge checks; use the full profiles when you want the broader, currently noisier regression sweep.
257
-
258
- ---
259
-
260
- ## Database
261
-
262
- ### Migrations
263
-
264
- Migrations run automatically on server startup (`server/db/migrate.ts`). The system:
265
-
266
- 1. Maintains a `_migrations` table tracking applied migrations by sequential ID
267
- 2. Runs unapplied migrations in order on boot
268
- 3. Each migration is idempotent SQL (`CREATE TABLE IF NOT EXISTS`, `ALTER ... ADD COLUMN IF NOT EXISTS`)
269
-
270
- **To add a new migration:** append to the `MIGRATIONS` array in `server/db/migrate.ts`.
271
-
272
- ### Inspect the database locally
273
-
274
- ```bash
275
- docker exec -it forgecad-postgresql-1 psql -U forgecad -d forgecad
276
- ```
277
-
278
- Common queries:
279
-
280
- ```sql
281
- -- List tables
282
- \dt
283
-
284
- -- Users
285
- SELECT id, email, name, role, created_at FROM users;
286
-
287
- -- Projects
288
- SELECT id, slug, name, visibility, owner_id FROM projects;
289
-
290
- -- Shared/published models
291
- SELECT share_id, filename, title, owner_id, updated_at FROM shared_files;
292
-
293
- -- Audit log (recent)
294
- SELECT action, details, created_at FROM audit_log ORDER BY created_at DESC LIMIT 20;
295
-
296
- -- Migration state
297
- SELECT * FROM _migrations ORDER BY id;
298
- ```
299
-
300
- ### Backup production database
301
-
302
- Stream a compressed dump directly to your Mac:
303
-
304
- ```bash
305
- ssh hetzner "docker exec \$(docker ps --filter name=postgres -q) pg_dump -Fc -U JR3CsoTkOQypFSdD forgecad" > ~/forgecad-backup-<label>.dump
306
- ```
307
-
308
- Use a descriptive label (e.g., `pre-fileids-20260409`). Always take a backup before deploying migrations that modify existing data.
309
-
310
- ### Restore production database
311
-
312
- ```bash
313
- cat ~/forgecad-backup-<label>.dump | ssh hetzner "docker exec -i \$(docker ps --filter name=postgres -q) pg_restore -U JR3CsoTkOQypFSdD -d forgecad --clean"
314
- ```
315
-
316
- This drops and recreates all tables from the dump. The app container will need a restart afterward to reconnect.
317
-
318
- ### Reset local database
319
-
320
- ```bash
321
- npm run dev:db:stop
322
- docker volume rm forgecad_postgresql-dev-data # or the worktree-specific volume name
323
- npm run dev:db
324
- # Server will re-run all migrations on next start
325
- ```
326
-
327
- ---
328
-
329
- ## Production (Hetzner / Kamal)
330
-
331
- ### Access
332
-
333
- ```bash
334
- ssh hetzner # see ~/.ssh/config for host alias
335
- ```
336
-
337
- ### Observability-first incident workflow
338
-
339
- Use the observability stack as the primary path for production incidents. Fall back to raw `docker logs` only if the stack itself is degraded.
340
-
341
- 1. Make sure your laptop is connected to Tailscale.
342
-
343
- 2. Check external symptoms first in Uptime Kuma:
344
-
345
- - `http://100.118.68.93:3001`
346
- - Confirm whether `https://forgecad.io/` and `https://forgecad.io/api/health` are down, slow, or flapping.
347
-
348
- 3. Open Grafana for quick triage:
349
-
350
- - `http://100.118.68.93:3000`
351
- - Use the `ForgeCAD Host Overview` dashboard first to check host pressure, container pressure, and whether observability targets are up.
352
- - Then switch to `ForgeCAD Web Requests` if the problem looks like throughput collapse, rising 4xx/5xxs, or slow request handling in the web app.
353
- - Then switch to `ForgeCAD DB Queries` if the issue looks like latency, auth/session churn, admin slowness, or anything else that may be query-bound.
354
-
355
- 4. Use Grafana Explore for logs and metrics:
356
-
357
- - Select `Loki` for logs.
358
- - Select `Prometheus` for metrics.
359
-
360
- Useful Loki queries:
361
-
362
- ```text
363
- {container=~"forgecad-web-.*|forgecad-backend-web-.*"}
364
- {container=~"forgecad-web-.*|forgecad-backend-web-.*"} |= "error"
365
- {container=~"forgecad-web-.*|forgecad-beta-web-.*"} |= "DB slow query"
366
- {container=~"forgecad-web-.*|forgecad-beta-web-.*"} | json | event="db.query"
367
- {container=~"forgecad-beta-.*"}
368
- {container="kamal-proxy"}
369
- ```
370
-
371
- ### Roll back a bad prod web deploy
372
-
373
- If the latest web deploy is healthy enough for `/api/health` but breaking real
374
- user flows, roll back to the previous healthy web version immediately:
375
-
376
- ```bash
377
- ssh hetzner 'docker ps -a --format "{{.Names}}\t{{.Status}}" | grep "^forgecad-web-" | sed -E "s/^forgecad-web-([a-f0-9]+)(_replaced_[^[:space:]]+)?\t/\1\t/"'
378
- npm run prod:rollback -- <VERSION>
379
- ```
380
-
381
- Example:
382
-
383
- ```bash
384
- npm run prod:rollback -- db9c49b058aaa379d9d622456cb249f9b0ddc335
385
- ```
386
-
387
- After rollback, verify a real user-facing path, not just `/api/health`. For
388
- auth incidents, a dummy login should return `401 Invalid credentials`, not
389
- `500`.
390
-
391
- Useful Prometheus queries:
392
-
393
- ```text
394
- up
395
- node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
396
- 100 * (1 - node_filesystem_avail_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{mountpoint="/",fstype!~"tmpfs|overlay"})
397
- sum by (container) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
398
- sum(rate(forge_http_request_total[$__rate_interval]))
399
- sum(rate(forge_http_request_total{surface="api",status_class="5xx"}[$__rate_interval]))
400
- histogram_quantile(0.95, sum by (le) (rate(forge_http_request_duration_seconds_bucket[$__rate_interval])))
401
- topk(10, sum by (forge_env, method, route) (rate(forge_http_request_total{surface="api"}[$__rate_interval])))
402
- ```
403
-
404
- 5. Check the app health endpoint after any intervention:
405
-
406
- ```bash
407
- curl -s https://forgecad.io/api/health | python3 -m json.tool
408
- ```
409
-
410
- 6. Use raw Docker inspection only as fallback or to verify what Grafana already suggested:
411
-
412
- - `docker logs --tail 200 <forgecad-container>`
413
- - `docker logs --tail 200 <forgecad-backend-container>`
414
- - `docker ps --format "{{.Names}}\t{{.Status}}"`
415
- - `df -h / && free -h`
416
-
417
- Grafana admin credentials live on Hetzner in `/home/kostard/forgecad-observability/.env`.
418
-
419
- ### Find containers
420
-
421
- ```bash
422
- docker ps --format "{{.Names}}" | grep forge
423
- ```
424
-
425
- ### Logs
426
-
427
- ```bash
428
- # App logs (recent)
429
- docker logs --tail 200 <forgecad-container>
430
-
431
- # Follow live
432
- docker logs -f <forgecad-container>
433
-
434
- # Filter for errors
435
- docker logs <forgecad-container> 2>&1 | grep -i error | tail -20
436
- ```
437
-
438
- If the observability stack is healthy, prefer Grafana Explore over direct `docker logs` because it gives you cross-container history and labels in one place.
439
-
440
- ### Database access in production
441
-
442
- ```bash
443
- # Get credentials from the app container's environment
444
- docker exec <forgecad-container> env | grep POSTGRES
445
-
446
- # Connect
447
- docker exec -it <postgres-container> psql -U <USER> -d forgecad
448
- ```
449
-
450
- ### Health check
451
-
452
- ```bash
453
- curl -s https://forgecad.io/api/health | python3 -m json.tool
454
- ```
455
-
456
- ### Verify a deployment landed
457
-
458
- GitHub Actions deploys production on push to `mainline` by running Kamal against Hetzner. This usually takes 2–5 minutes, depending on image build and pull time.
459
-
460
- To confirm:
461
-
462
- ```bash
463
- # 1. Check container was recently recreated (look at the "Up X minutes" status and CreatedAt)
464
- ssh hetzner 'docker ps --format "{{.Names}}\t{{.CreatedAt}}\t{{.Status}}" | grep -E "^forgecad-" | grep -v postgres'
465
-
466
- # 2. Check for healthy status
467
- ssh hetzner 'docker ps --format "{{.Names}}\t{{.Status}}" | grep -E "^forgecad-" | grep -v postgres | grep healthy'
468
-
469
- # 3. Check startup logs for errors
470
- ssh hetzner 'docker logs --tail 30 $(docker ps --format "{{.Names}}" | grep -E "^forgecad-" | grep -v postgres) 2>&1'
471
-
472
- # 4. Hit the health endpoint
473
- curl -s https://forgecad.io/api/health | python3 -m json.tool
474
-
475
- # 5. Verify specific code landed (check compiled output, not source)
476
- ssh hetzner 'docker exec $(docker ps --format "{{.Names}}" | grep -E "^forgecad-" | grep -v postgres) grep -c "YOUR_KEYWORD" /app/dist-server/routes/files.js'
477
- ```
478
-
479
- Container names change on each deploy — always discover dynamically with `docker ps | grep forgecad`. The app dir is `/app/` with compiled output in `dist/`, `dist-server/`, `dist-cli/`.
480
-
481
- ### Verify share links work after deploy
482
-
483
- Share links are a critical user-facing feature. After any deploy that touches shares, files, or database migrations:
484
-
485
- ```bash
486
- # Hit the share API directly — should return JSON with shareId, code, author
487
- curl -s https://forgecad.io/api/shares/gUGt8bdvKg | python3 -c "
488
- import sys, json
489
- d = json.load(sys.stdin)
490
- if 'shareId' in d and 'code' in d:
491
- print(f'OK: share {d[\"shareId\"]} resolves ({len(d[\"code\"])} bytes)')
492
- else:
493
- print(f'BROKEN: {d}')
494
- sys.exit(1)
495
- "
496
- ```
497
-
498
- If it prints `BROKEN`, restore the database backup and roll back the deploy.
499
-
500
- ### Restart
501
-
502
- Kamal normally handles deploys and restarts on push to `mainline`. For a manual restart:
503
-
504
- ```bash
505
- docker restart $(docker ps --format "{{.Names}}" | grep -E "^forgecad-" | grep -v postgres)
506
- ```
507
-
508
- ### System resources
509
-
510
- ```bash
511
- df -h # disk
512
- free -h # memory
513
- docker stats --no-stream # per-container CPU/memory
514
- ```
515
-
516
- ---
517
-
518
- ## API Endpoints Reference
519
-
520
- ### Public (no auth)
521
-
522
- | Method | Route | Purpose |
523
- |--------|-------|---------|
524
- | GET | `/api/health` | Server health + uptime |
525
- | GET | `/api/shares/:shareId` | Fetch a published model |
526
-
527
- ### Auth
528
-
529
- | Method | Route | Purpose |
530
- |--------|-------|---------|
531
- | POST | `/api/auth/register` | Create account |
532
- | POST | `/api/auth/login` | Login (returns cookies) |
533
- | POST | `/api/auth/refresh` | Refresh access token |
534
- | POST | `/api/auth/logout` | Clear session |
535
- | GET | `/api/auth/session` | Current user info |
536
- | GET | `/api/auth/providers` | OAuth provider URLs |
537
- | POST | `/api/auth/callback/:provider` | OAuth callback |
538
- | POST | `/api/auth/forgot-password` | Request password reset |
539
- | POST | `/api/auth/reset-password` | Apply password reset |
540
- | GET | `/api/auth/verify-email` | Confirm email |
541
- | POST | `/api/auth/resend-verification` | Resend verification email |
542
-
543
- ### Projects (all require auth)
544
-
545
- | Method | Route | Role | Purpose |
546
- |--------|-------|------|---------|
547
- | GET | `/api/projects` | — | List user's projects |
548
- | POST | `/api/projects` | — | Create project |
549
- | GET | `/api/projects/:id` | viewer | Project details |
550
- | PATCH | `/api/projects/:id` | owner | Update project |
551
- | DELETE | `/api/projects/:id` | owner | Delete project |
552
- | GET | `/api/projects/:id/members` | viewer | List members |
553
- | POST | `/api/projects/:id/members` | owner | Add member |
554
- | PATCH | `/api/projects/:id/members/:userId` | owner | Update member role |
555
- | DELETE | `/api/projects/:id/members/:userId` | owner | Remove member |
556
-
557
- ### Files (all require auth + project role)
558
-
559
- | Method | Route | Role | Purpose |
560
- |--------|-------|------|---------|
561
- | GET | `/api/projects/:id/watch` | viewer | SSE file watcher |
562
- | GET | `/api/projects/:id/file-ids` | viewer | File ID map (path → fileId) |
563
- | POST | `/api/projects/:id/save` | editor | Save file (returns fileId) |
564
- | POST | `/api/projects/:id/delete` | editor | Delete file |
565
- | POST | `/api/projects/:id/move` | editor | Move/rename file (preserves share links) |
566
- | POST | `/api/projects/:id/mkdir` | editor | Create directory |
567
- | GET | `/api/projects/:id/read-binary` | viewer | Download mesh file |
568
-
569
- ### Shares (published models)
570
-
571
- | Method | Route | Auth | Purpose |
572
- |--------|-------|------|---------|
573
- | GET | `/api/shares/:shareId` | none | Fetch published model (public) |
574
- | GET | `/api/shares` | required | List user's published models |
575
- | POST | `/api/shares` | required | Publish or update a model |
576
- | DELETE | `/api/shares/:shareId` | required (owner) | Unpublish |
577
-
578
- ---
579
-
580
- ## Troubleshooting
581
-
582
- ### Server won't start
583
-
584
- | Symptom | Cause | Fix |
585
- |---------|-------|-----|
586
- | `pino-pretty` transport error | Missing dev dependency in worktree | Use `NODE_ENV=production` or install pino-pretty |
587
- | Port 5174 already in use | Another server instance running | `lsof -i :5174` then kill the PID |
588
- | `FORGE_DB_URL` missing | Env var not set | Use `npm run dev:server` which sets it automatically |
589
- | Connection refused on :5432 | Postgres not running | `npm run dev:db` |
590
-
591
- ### Auth issues
592
-
593
- | Symptom | Cause | Fix |
594
- |---------|-------|-----|
595
- | 500 on login/register | Missing `FORGE_JWT_SECRET` | Set it (any string for dev, `openssl rand -base64 32` for prod) |
596
- | 401 on authenticated endpoints | Expired access token (15 min) | Client should auto-refresh; check refresh token cookie |
597
- | CORS errors in browser | `FORGE_ALLOWED_ORIGINS` mismatch | Must match the frontend's origin exactly |
598
-
599
- ### Email issues
600
-
601
- | Symptom | Cause | Fix |
602
- |---------|-------|-----|
603
- | No verification/reset email received; logs show `[email] To: user@...` | `RESEND_API_KEY` is empty — falling back to console log | Set `RESEND_API_KEY` in `.kamal/secrets` or production secrets, then redeploy. See [platform/email.md](platform/email.md) |
604
-
605
- ### Database issues
606
-
607
- | Symptom | Cause | Fix |
608
- |---------|-------|-----|
609
- | Migration fails | Schema conflict from manual changes | Reset local DB (see above) |
610
- | `relation does not exist` | Migrations didn't run | Check `_migrations` table; restart server |
611
- | Quota errors on file save | User's `storage_used_bytes` exceeded 50MB | Check with `SELECT storage_used_bytes FROM users WHERE ...` |