forgecad 0.9.14 → 0.9.16
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.
- package/LICENSE +6 -4
- package/README.md +8 -4
- package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CXvls4-J.js} +2 -2
- package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-B27zk8xL.js} +4 -15
- package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-CMAVvgQL.js} +2 -2
- package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-knf4I4h7.js} +9 -3
- package/dist/assets/EditorApp-BHMQlJ-D.js +14686 -0
- package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-BpjZgzk0.css} +846 -0
- package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-D7ZGlFjx.js} +3 -3
- package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-CnevhTE8.js} +36 -38
- package/dist/assets/LegalPage-BPTUmqeg.js +39 -0
- package/dist/assets/LegalPage-BRlScr9A.css +91 -0
- package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-B0D4goG_.js} +19 -19
- package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
- package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-CFF-UgjI.js} +2 -2
- package/dist/assets/app-CE3sYcV7.css +3890 -0
- package/dist/assets/{app-bEww1ic4.js → app-T0pDcSX4.js} +3382 -1069
- package/dist/assets/cli/{render-Cho2uKG_.js → render-C5pcIISc.js} +477 -29
- package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-Ba2Hm58b.js} +928 -243
- package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-vkx310U2.js} +8883 -6040
- package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
- package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
- package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-BuTJDVX6.js} +1179 -273
- package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
- package/dist/assets/{targets-D6PWsv6X.js → jointPose-B_Cgedn9.js} +71 -3
- package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
- package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
- package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
- package/dist/assets/{manifold-rmfAcdwF.js → manifold-BWgsjmAM.js} +1 -1
- package/dist/assets/{manifold-uRzgk5O8.js → manifold-D6IFSkhH.js} +2 -2
- package/dist/assets/{manifold-CG9Fokx-.js → manifold-rZexZI0G.js} +1 -1
- package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-0AGij1Ru.js} +8659 -12771
- package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-J5cuzxT1.js} +8050 -6203
- package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-Vl4Wxa1y.js} +50 -6
- package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
- package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
- package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/ai-native-cad.md +50 -0
- package/dist/docs-raw/AI/usage.md +5 -12
- package/dist/docs-raw/CLI.md +34 -10
- package/dist/docs-raw/component-model.md +27 -11
- package/dist/docs-raw/generated/assembly.md +374 -187
- package/dist/docs-raw/generated/concepts.md +245 -237
- package/dist/docs-raw/generated/core.md +283 -6
- package/dist/docs-raw/generated/curves.md +274 -361
- package/dist/docs-raw/generated/lib.md +9 -19
- package/dist/docs-raw/generated/output.md +29 -4
- package/dist/docs-raw/generated/runtime-names.md +49 -0
- package/dist/docs-raw/generated/sdf.md +31 -0
- package/dist/docs-raw/generated/sheet-metal.md +9 -0
- package/dist/docs-raw/generated/sketch.md +44 -1
- package/dist/docs-raw/generated/viewport.md +11 -3
- package/dist/docs-raw/guides/coordinate-system.md +20 -16
- package/dist/docs-raw/guides/geometry-conventions.md +2 -2
- package/dist/docs-raw/guides/inspection-bundles.md +2 -1
- package/dist/docs-raw/guides/joint-design.md +24 -0
- package/dist/docs-raw/guides/positioning.md +13 -3
- package/dist/docs-raw/legal/privacy.md +63 -0
- package/dist/docs-raw/legal/software-license.md +55 -0
- package/dist/docs-raw/legal/terms.md +87 -0
- package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
- package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
- package/dist/docs-raw/skills/forgecad-lld.md +1 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +40 -39
- package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
- package/dist/docs-raw/skills/forgecad-project.md +3 -1
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
- package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
- package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad.md +4 -3
- package/dist/docs-raw/welcome.md +2 -0
- package/dist/index.html +40 -12
- package/dist/llms.txt +8 -0
- package/dist/site.webmanifest +1 -1
- package/dist/sitemap.xml +49 -13
- package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SYQ2PWOB.js} +1 -2
- package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-HIAGV62W.js} +1 -2
- package/dist-cli/{chunk-EXWGNL6K.js → chunk-SPZE3DUY.js} +20659 -17930
- package/dist-cli/forgecad.js +3568 -1250
- package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
- package/dist-cli/solver_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +1192 -725
- package/dist-skill/SKILL.md +3 -2
- package/dist-skill/docs/API/core/concepts.md +64 -1
- package/dist-skill/docs/CLI.md +34 -10
- package/dist-skill/docs/generated/assembly.md +339 -213
- package/dist-skill/docs/generated/core.md +283 -6
- package/dist-skill/docs/generated/curves.md +272 -362
- package/dist-skill/docs/generated/lib.md +9 -19
- package/dist-skill/docs/generated/output.md +29 -4
- package/dist-skill/docs/generated/runtime-names.md +40 -0
- package/dist-skill/docs/generated/sdf.md +31 -0
- package/dist-skill/docs/generated/sheet-metal.md +9 -0
- package/dist-skill/docs/generated/sketch.md +44 -2
- package/dist-skill/docs/generated/viewport.md +2 -87
- package/dist-skill/docs/guides/coordinate-system.md +20 -16
- package/dist-skill/docs/guides/geometry-conventions.md +2 -2
- package/dist-skill/docs/guides/inspection-bundles.md +2 -1
- package/dist-skill/docs/guides/joint-design.md +24 -0
- package/dist-skill/docs/guides/positioning.md +13 -3
- package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
- package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +39 -38
- package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
- package/dist-skill/library/forgecad-project/SKILL.md +2 -0
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
- package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
- package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
- package/examples/api/assembly-kinematics-limb.forge.js +116 -0
- package/examples/api/connector-frame-rig-chain.forge.js +102 -0
- package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
- package/examples/api/exact-surface-studio.forge.js +6 -8
- package/examples/api/helix-basics.forge.js +8 -8
- package/examples/api/lean-foundations/README.md +12 -0
- package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
- package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
- package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
- package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
- package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
- package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
- package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
- package/examples/api/route3d-elbow.forge.js +71 -0
- package/examples/api/transition-curves.forge.js +44 -15
- package/examples/api/variable-sweep-test.forge.js +3 -1
- package/examples/api/y-blend-corner-showcase.forge.js +0 -2
- package/examples/generative/coral-vase.forge.js +1 -1
- package/examples/nurbs-tube.forge.js +1 -1
- package/package.json +17 -13
- package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
- package/dist/assets/app-CsHnaBWt.css +0 -1789
- package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
- package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
- package/dist/docs-raw/API/README.md +0 -16
- package/dist/docs-raw/API/core/concepts.md +0 -118
- package/dist/docs-raw/INDEX.md +0 -138
- package/dist/docs-raw/RELEASING.md +0 -87
- package/dist/docs-raw/agent-native-api.md +0 -27
- package/dist/docs-raw/beta-deployment.md +0 -304
- package/dist/docs-raw/beta-operations.md +0 -325
- package/dist/docs-raw/blueprint-first.md +0 -145
- package/dist/docs-raw/cli-monetization.md +0 -112
- package/dist/docs-raw/coding-best-practices.md +0 -120
- package/dist/docs-raw/coding.md +0 -340
- package/dist/docs-raw/deployment.md +0 -374
- package/dist/docs-raw/guides/skill-maintenance.md +0 -161
- package/dist/docs-raw/guides/surface-members.md +0 -82
- package/dist/docs-raw/harbor-cli.md +0 -854
- package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
- package/dist/docs-raw/internals/compiler.md +0 -307
- package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
- package/dist/docs-raw/internals/constraint-solver.md +0 -176
- package/dist/docs-raw/internals/shape-from-slices.md +0 -152
- package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
- package/dist/docs-raw/platform/admin.md +0 -45
- package/dist/docs-raw/platform/architecture.md +0 -82
- package/dist/docs-raw/platform/auth.md +0 -139
- package/dist/docs-raw/platform/email.md +0 -67
- package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
- package/dist/docs-raw/platform/observability.md +0 -197
- package/dist/docs-raw/platform/projects.md +0 -111
- package/dist/docs-raw/platform/sharing.md +0 -90
- package/dist/docs-raw/product/README.md +0 -39
- package/dist/docs-raw/product/api-as-product-language.md +0 -13
- package/dist/docs-raw/product/business-model.md +0 -15
- package/dist/docs-raw/product/competitive-positioning.md +0 -17
- package/dist/docs-raw/product/creative-manufacturing.md +0 -15
- package/dist/docs-raw/product/founder-story.md +0 -11
- package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
- package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
- package/dist/docs-raw/product/product-loop.md +0 -17
- package/dist/docs-raw/product/strategic-decisions.md +0 -22
- package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
- package/dist/docs-raw/product/user-segments.md +0 -15
- package/dist/docs-raw/product/vision.md +0 -26
- package/dist/docs-raw/rl-environments.md +0 -350
- package/dist/docs-raw/runbook.md +0 -611
- package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
- package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
- package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
- package/dist-cli/forgecad.js.map +0 -1
- package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
- package/dist-cli/solver-46FFSK2U.js.map +0 -1
- package/dist-skill/SKILL-dev.md +0 -145
- package/dist-skill/docs-dev/API/core/concepts.md +0 -118
- package/dist-skill/docs-dev/CLI.md +0 -677
- package/dist-skill/docs-dev/agent-native-api.md +0 -27
- package/dist-skill/docs-dev/blueprint-first.md +0 -145
- package/dist-skill/docs-dev/coding-best-practices.md +0 -120
- package/dist-skill/docs-dev/coding.md +0 -340
- package/dist-skill/docs-dev/component-model.md +0 -164
- package/dist-skill/docs-dev/generated/assembly.md +0 -794
- package/dist-skill/docs-dev/generated/core.md +0 -2117
- package/dist-skill/docs-dev/generated/curves.md +0 -2583
- package/dist-skill/docs-dev/generated/lib.md +0 -169
- package/dist-skill/docs-dev/generated/output.md +0 -247
- package/dist-skill/docs-dev/generated/sdf.md +0 -446
- package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
- package/dist-skill/docs-dev/generated/sketch.md +0 -1811
- package/dist-skill/docs-dev/generated/viewport.md +0 -585
- package/dist-skill/docs-dev/generated/wood.md +0 -108
- package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
- package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
- package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
- package/dist-skill/docs-dev/guides/joint-design.md +0 -78
- package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
- package/dist-skill/docs-dev/guides/positioning.md +0 -161
- package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
- package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
- package/dist-skill/docs-dev/internals/compiler.md +0 -307
- package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
- package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
- package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
- package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
- package/examples/api/bolted-service-cover.forge.js +0 -17
- package/examples/api/cable-gland-anchor.forge.js +0 -14
- package/examples/api/captured-cartridge-guide.forge.js +0 -14
- package/examples/api/captured-linear-slide.forge.js +0 -13
- package/examples/api/clevis-pin-joint.forge.js +0 -13
- package/examples/api/datum-enclosure.forge.js +0 -16
- package/examples/api/hose-barb-port.forge.js +0 -14
- package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
- package/examples/api/living-hinge-cover.forge.js +0 -14
- package/examples/api/pcb-terminal-block.forge.js +0 -22
- package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
- package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
- package/examples/api/routed-tube-clip.forge.js +0 -15
- package/examples/api/seated-bearing-stack.forge.js +0 -30
- package/examples/api/snap-latch-cover.forge.js +0 -14
- package/examples/api/thumb-screw-clamp.forge.js +0 -15
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
# Authentication
|
|
2
|
-
|
|
3
|
-
ForgeCAD uses JWT-based authentication with refresh token rotation. Tokens are delivered via `httpOnly` cookies -- never exposed to client-side JavaScript.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Token Architecture
|
|
8
|
-
|
|
9
|
-
- **Access token**: JWT signed with HMAC (HS256). Default lifetime: 15 minutes.
|
|
10
|
-
- **Refresh token**: Opaque token stored in the database. Default lifetime: 7 days.
|
|
11
|
-
- **Rotation**: Each refresh request issues a new refresh token and invalidates the old one. If a previously-used refresh token is replayed, all sessions for that user are revoked (replay detection).
|
|
12
|
-
|
|
13
|
-
Both tokens are set as `httpOnly`, `Secure`, `SameSite=Strict` cookies.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## User Roles
|
|
18
|
-
|
|
19
|
-
| Role | Access |
|
|
20
|
-
|-------|--------|
|
|
21
|
-
| `user` | Standard account, can use `/app` and all editor features |
|
|
22
|
-
| `admin` | Full access including `/admin` dashboard |
|
|
23
|
-
|
|
24
|
-
Middleware:
|
|
25
|
-
|
|
26
|
-
- `requireAuth` -- extracts the user from the JWT access token. Returns 401 if the token is missing or expired.
|
|
27
|
-
- `requireAdmin` -- calls `requireAuth`, then checks `role === 'admin'`. Returns 403 otherwise.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Registration and Login
|
|
32
|
-
|
|
33
|
-
1. **Register** (`POST /api/auth/register`) -- accepts email and password. Creates the account and sends a verification email. See [email delivery](email.md) for delivery configuration.
|
|
34
|
-
2. **Verify email** (`GET /api/auth/verify-email`) -- confirms the address using the token from the email link.
|
|
35
|
-
3. **Login** (`POST /api/auth/login`) -- validates credentials, sets access and refresh token cookies.
|
|
36
|
-
|
|
37
|
-
Password reset:
|
|
38
|
-
|
|
39
|
-
1. `POST /api/auth/forgot-password` -- sends a reset email with a single-use token (1-hour expiry).
|
|
40
|
-
2. `POST /api/auth/reset-password` -- applies the new password using the reset token.
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## OAuth
|
|
45
|
-
|
|
46
|
-
GitHub and Google are supported as optional OAuth providers. They are enabled by setting the corresponding environment variables (see below). When not configured, the provider is omitted from the login UI.
|
|
47
|
-
|
|
48
|
-
- `GET /api/auth/providers` -- returns the list of configured OAuth providers and their authorization URLs.
|
|
49
|
-
- `POST /api/auth/callback/:provider` -- handles the OAuth redirect, creates or links the account, and sets token cookies.
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## CLI Auth for OAuth Accounts
|
|
54
|
-
|
|
55
|
-
The web app and CLI use different sign-in mechanics:
|
|
56
|
-
|
|
57
|
-
- Browser sign-in uses cookies. Email/password login and OAuth login both end with `httpOnly` cookies that the browser sends back to ForgeCAD.
|
|
58
|
-
- CLI sign-in uses bearer tokens. The CLI cannot reuse a browser-only OAuth cookie, and a Google OAuth redirect flow is awkward for a terminal.
|
|
59
|
-
|
|
60
|
-
That difference is why a Google-only user needs an API token in the CLI: there is no terminal password to submit for a browser-only OAuth account.
|
|
61
|
-
|
|
62
|
-
The normal CLI path is now one interactive command:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
forgecad login
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Choose **API token** when prompted, then paste a token created from the signed-in web app at **Settings > API Tokens**. The CLI validates the token with `GET /api/auth/session`, stores it in `~/.forgecad/auth.json` as `authType: "token"`, and sends it as `Authorization: Bearer ...` on project, publish, license, and token-management requests.
|
|
69
|
-
|
|
70
|
-
For CI/CD and one-off automation, skip local storage and set:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
FORGECAD_TOKEN=fc_pat_... forgecad project push
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Passing the token directly as `forgecad login --token fc_pat_...` is accepted for non-interactive setup, but the interactive prompt is preferred so tokens do not land in shell history.
|
|
77
|
-
|
|
78
|
-
For local development or self-hosted experiments, `forgecad login --server <url>` accepts a clean server origin only. Use HTTPS for remote servers; plain HTTP is accepted only for localhost. License activation remains tied to forgecad.io accounts.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## API Endpoints
|
|
83
|
-
|
|
84
|
-
| Method | Route | Purpose |
|
|
85
|
-
|--------|-------|---------|
|
|
86
|
-
| `POST` | `/api/auth/register` | Create account |
|
|
87
|
-
| `POST` | `/api/auth/login` | Login (sets cookies) |
|
|
88
|
-
| `POST` | `/api/auth/refresh` | Rotate refresh token, issue new access token |
|
|
89
|
-
| `POST` | `/api/auth/logout` | Clear session cookies, invalidate refresh token |
|
|
90
|
-
| `GET` | `/api/auth/session` | Return current user info (requires valid access token) |
|
|
91
|
-
| `GET` | `/api/auth/providers` | List available OAuth providers |
|
|
92
|
-
| `POST` | `/api/auth/callback/:provider` | OAuth callback |
|
|
93
|
-
| `POST` | `/api/auth/forgot-password` | Request password reset email |
|
|
94
|
-
| `POST` | `/api/auth/reset-password` | Apply password reset |
|
|
95
|
-
| `GET` | `/api/auth/verify-email` | Confirm email address |
|
|
96
|
-
| `POST` | `/api/auth/resend-verification` | Resend verification email |
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## Rate Limiting
|
|
101
|
-
|
|
102
|
-
Auth endpoints are rate-limited to **10 requests per 15 minutes per IP**. This applies to login, registration, password reset, and token refresh. Exceeding the limit returns HTTP 429.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Environment Variables
|
|
107
|
-
|
|
108
|
-
| Variable | Required | Default | Purpose |
|
|
109
|
-
|----------|----------|---------|---------|
|
|
110
|
-
| `FORGE_JWT_SECRET` | Yes | -- | HMAC signing key for JWT access tokens |
|
|
111
|
-
| `FORGE_JWT_ACCESS_EXPIRES` | No | `15m` | Access token lifetime (e.g. `15m`, `1h`) |
|
|
112
|
-
| `FORGE_JWT_REFRESH_EXPIRES` | No | `7d` | Refresh token lifetime |
|
|
113
|
-
| `GITHUB_CLIENT_ID` | No | -- | GitHub OAuth application ID |
|
|
114
|
-
| `GITHUB_CLIENT_SECRET` | No | -- | GitHub OAuth secret |
|
|
115
|
-
| `GOOGLE_CLIENT_ID` | No | -- | Google OAuth client ID |
|
|
116
|
-
| `GOOGLE_CLIENT_SECRET` | No | -- | Google OAuth secret |
|
|
117
|
-
| `FORGE_ALLOWED_ORIGINS` | No | `localhost` | Comma-separated list of allowed CORS origins |
|
|
118
|
-
|
|
119
|
-
See [deployment.md](../deployment.md) for the full environment variable reference.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Troubleshooting
|
|
124
|
-
|
|
125
|
-
**500 on login** -- `FORGE_JWT_SECRET` is not set. The server cannot sign tokens without it.
|
|
126
|
-
|
|
127
|
-
**401 on authenticated endpoints** -- the access token has expired. The client should call `POST /api/auth/refresh` to obtain a new one. If refresh also returns 401, the session has expired entirely and the user must log in again.
|
|
128
|
-
|
|
129
|
-
**CORS errors** -- `FORGE_ALLOWED_ORIGINS` does not include the frontend's origin. Add the correct domain (e.g. `https://forgecad.io`).
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## Source Files
|
|
134
|
-
|
|
135
|
-
| File | Purpose |
|
|
136
|
-
|------|---------|
|
|
137
|
-
| `server/routes/auth.ts` | All auth route handlers |
|
|
138
|
-
| `server/middleware/auth.ts` | `requireAuth` and `requireAdmin` middleware |
|
|
139
|
-
| `server/db/schema.ts` | User and refresh token table definitions |
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Email Delivery
|
|
2
|
-
|
|
3
|
-
ForgeCAD uses [Resend](https://resend.com) for transactional email. The free tier covers 3,000 emails/month and 100/day — more than enough for early production.
|
|
4
|
-
|
|
5
|
-
Emails are sent for:
|
|
6
|
-
- Email address verification (on register + resend)
|
|
7
|
-
- Password reset links
|
|
8
|
-
|
|
9
|
-
See [auth.md](auth.md) for the authentication flows that trigger these emails.
|
|
10
|
-
|
|
11
|
-
## Environment Variables
|
|
12
|
-
|
|
13
|
-
| Variable | Required | Default | Notes |
|
|
14
|
-
|---|---|---|---|
|
|
15
|
-
| `RESEND_API_KEY` | Yes (in prod) | — | From Resend dashboard |
|
|
16
|
-
| `FORGE_EMAIL_FROM` | No | `ForgeCAD <noreply@forgecad.io>` | Must match a verified domain |
|
|
17
|
-
| `FORGE_APP_URL` | No | `http://localhost:5173` | Base URL used in email links |
|
|
18
|
-
|
|
19
|
-
Without `RESEND_API_KEY`, emails fall back to `console.log` — fine for local dev.
|
|
20
|
-
|
|
21
|
-
See [../deployment.md](../deployment.md) for the full environment variable reference.
|
|
22
|
-
|
|
23
|
-
## First-Time Setup
|
|
24
|
-
|
|
25
|
-
### 1. Create a Resend account
|
|
26
|
-
|
|
27
|
-
Sign up at [resend.com](https://resend.com). No credit card required for the free tier.
|
|
28
|
-
|
|
29
|
-
### 2. Verify your sending domain
|
|
30
|
-
|
|
31
|
-
In the Resend dashboard: **Domains** > **Add Domain** > enter `forgecad.io`.
|
|
32
|
-
|
|
33
|
-
Resend will show you DNS records to add (typically a few TXT/MX entries). If your DNS is on **Cloudflare**, Resend can auto-configure the records — just connect your Cloudflare account in the Resend dashboard. It takes a few minutes to propagate.
|
|
34
|
-
|
|
35
|
-
### 3. Create an API key
|
|
36
|
-
|
|
37
|
-
In the Resend dashboard: **API Keys** > **Create API Key**.
|
|
38
|
-
|
|
39
|
-
Use **Send access only** — no need for full access on the server.
|
|
40
|
-
|
|
41
|
-
### 4. Set env vars
|
|
42
|
-
|
|
43
|
-
In production secrets (`.kamal/secrets`) or your local `.env`:
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
RESEND_API_KEY=re_...
|
|
47
|
-
FORGE_EMAIL_FROM=ForgeCAD <noreply@forgecad.io>
|
|
48
|
-
FORGE_APP_URL=https://forgecad.io
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Local Development
|
|
52
|
-
|
|
53
|
-
Leave `RESEND_API_KEY` unset. The `send()` function detects the missing key and logs the email content and token to the console instead of sending. Token links are printed so you can copy them directly into the browser.
|
|
54
|
-
|
|
55
|
-
## Code Layout
|
|
56
|
-
|
|
57
|
-
| File | Purpose |
|
|
58
|
-
|---|---|
|
|
59
|
-
| `server/email/send.ts` | Resend client + `sendVerificationEmail` / `sendPasswordResetEmail` helpers |
|
|
60
|
-
| `server/env.ts` | `resendApiKey`, `emailFrom`, `appUrl` env vars |
|
|
61
|
-
| `server/routes/auth.ts` | Calls into `send.ts` from register, forgot-password, resend-verification routes |
|
|
62
|
-
|
|
63
|
-
## Adding a New Email Type
|
|
64
|
-
|
|
65
|
-
1. Add a new `send*Email(to, ...)` export in `server/email/send.ts`
|
|
66
|
-
2. Call it from the relevant route in `server/routes/auth.ts` (or wherever)
|
|
67
|
-
3. No new infrastructure needed — same Resend client and `send()` helper
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# Google OAuth Setup Guide
|
|
2
|
-
|
|
3
|
-
Step-by-step guide to configure Google Sign-In for ForgeCAD.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
- A Google account
|
|
8
|
-
- Access to [Google Cloud Console](https://console.cloud.google.com)
|
|
9
|
-
|
|
10
|
-
## 1. Create a Google Cloud Project
|
|
11
|
-
|
|
12
|
-
1. Go to [console.cloud.google.com](https://console.cloud.google.com)
|
|
13
|
-
2. Click the project dropdown at the top left (might say "Select a project")
|
|
14
|
-
3. Click **New Project**
|
|
15
|
-
4. Name it **ForgeCAD**, click **Create**
|
|
16
|
-
5. Make sure it's selected in the project dropdown
|
|
17
|
-
|
|
18
|
-
## 2. Configure the OAuth Consent Screen
|
|
19
|
-
|
|
20
|
-
This is what users see when they click "Sign in with Google."
|
|
21
|
-
|
|
22
|
-
1. In the left sidebar: **APIs & Services → OAuth consent screen**
|
|
23
|
-
2. Click **Get started** (or **Configure consent screen**)
|
|
24
|
-
3. Choose **External** (anyone with a Google account can sign in) → **Create**
|
|
25
|
-
4. Fill in:
|
|
26
|
-
- **App name**: `ForgeCAD`
|
|
27
|
-
- **User support email**: your email
|
|
28
|
-
- **Developer contact email**: your email
|
|
29
|
-
- Logo is optional (can add later)
|
|
30
|
-
5. Click **Save and Continue**
|
|
31
|
-
6. **Scopes** screen → click **Add or Remove Scopes**
|
|
32
|
-
- Add these three:
|
|
33
|
-
- `openid`
|
|
34
|
-
- `email` (or `.../auth/userinfo.email`)
|
|
35
|
-
- `profile` (or `.../auth/userinfo.profile`)
|
|
36
|
-
- Click **Update** → **Save and Continue**
|
|
37
|
-
7. **Test users** → skip for now (only matters while in "Testing" status) → **Save and Continue**
|
|
38
|
-
8. Review and click **Back to Dashboard**
|
|
39
|
-
|
|
40
|
-
## 3. Create OAuth Credentials
|
|
41
|
-
|
|
42
|
-
Use the Google Auth Platform / Credentials page for ForgeCAD web sign-in.
|
|
43
|
-
The `gcloud iam oauth-clients` command is for IAM/IAP OAuth application
|
|
44
|
-
resources and is not the right client type for this app's Google Sign-In flow.
|
|
45
|
-
|
|
46
|
-
1. In the left sidebar: **APIs & Services → Credentials**
|
|
47
|
-
2. Click **+ Create Credentials → OAuth client ID**
|
|
48
|
-
3. **Application type**: Web application
|
|
49
|
-
4. **Name**: `ForgeCAD Web` (just a label for you)
|
|
50
|
-
5. **Authorized JavaScript origins** — add your domain(s):
|
|
51
|
-
- `https://forgecad.io` (production)
|
|
52
|
-
- `http://localhost:5173` (local dev)
|
|
53
|
-
6. **Authorized redirect URIs** — must match exactly:
|
|
54
|
-
- `https://forgecad.io/auth/callback/google`
|
|
55
|
-
- `http://localhost:5173/auth/callback/google` (local dev)
|
|
56
|
-
7. Click **Create**
|
|
57
|
-
8. Copy the **Client ID** and **Client Secret** from the dialog
|
|
58
|
-
|
|
59
|
-
## 4. Add Credentials to `.env`
|
|
60
|
-
|
|
61
|
-
On your server (or local `.env`):
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
GOOGLE_CLIENT_ID=123456789-xxxxxxx.apps.googleusercontent.com
|
|
65
|
-
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxx
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## 5. Publish the App
|
|
69
|
-
|
|
70
|
-
While the consent screen is in **Testing** mode, only explicitly added test users can sign in.
|
|
71
|
-
|
|
72
|
-
1. Go to **OAuth consent screen**
|
|
73
|
-
2. Click **Publish App**
|
|
74
|
-
3. For apps requesting only `email`, `profile`, and `openid` scopes, Google does not require a full verification review — just confirm the prompt.
|
|
75
|
-
|
|
76
|
-
## 6. Restart and Test
|
|
77
|
-
|
|
78
|
-
Restart the server so it picks up the new env vars, then click the Google button on the login page.
|
|
79
|
-
|
|
80
|
-
## Troubleshooting
|
|
81
|
-
|
|
82
|
-
| Problem | Cause | Fix |
|
|
83
|
-
|---------|-------|-----|
|
|
84
|
-
| `redirect_uri_mismatch` error | Redirect URI in Google Console doesn't exactly match the one your app sends | Check scheme (`http` vs `https`), domain, port, path, and trailing slashes |
|
|
85
|
-
| "Access blocked: This app's request is invalid" | Consent screen not configured or missing scopes | Complete the OAuth consent screen setup (step 2) |
|
|
86
|
-
| "This app isn't verified" warning | App is published but not verified by Google | Normal for `openid`/`email`/`profile` scopes — users can click "Advanced → Go to ForgeCAD" to proceed |
|
|
87
|
-
| Google button doesn't appear on login page | `GOOGLE_CLIENT_ID` env var not set | Add it to `.env` and restart the server |
|
|
88
|
-
| Sign-in works but no email returned | Missing `email` scope | Re-check scopes in consent screen (step 2.6) |
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
# Observability
|
|
2
|
-
|
|
3
|
-
ForgeCAD uses a self-hosted observability stack on Hetzner:
|
|
4
|
-
|
|
5
|
-
- Grafana for dashboards and Explore
|
|
6
|
-
- Prometheus for metrics
|
|
7
|
-
- Loki for logs
|
|
8
|
-
- Alloy for Docker log collection
|
|
9
|
-
- node-exporter and cAdvisor for host and container metrics
|
|
10
|
-
- Uptime Kuma for black-box uptime checks
|
|
11
|
-
|
|
12
|
-
## Why this setup
|
|
13
|
-
|
|
14
|
-
We explicitly do **not** use the app admin page as the primary operational surface.
|
|
15
|
-
|
|
16
|
-
The goals are:
|
|
17
|
-
|
|
18
|
-
- Tailscale-first private operator workflow
|
|
19
|
-
- real dashboards and query UIs
|
|
20
|
-
- logs and metrics on the same box
|
|
21
|
-
- no paid SaaS requirement
|
|
22
|
-
- minimal public exposure
|
|
23
|
-
|
|
24
|
-
## Access model
|
|
25
|
-
|
|
26
|
-
Operator-facing observability ports bind only to the Hetzner host's Tailscale address, defaulting to `100.118.68.93`.
|
|
27
|
-
Reach them directly from any authorized device on the tailnet:
|
|
28
|
-
|
|
29
|
-
- Grafana: `http://100.118.68.93:3000`
|
|
30
|
-
- Uptime Kuma: `http://100.118.68.93:3001`
|
|
31
|
-
- Prometheus: `http://100.118.68.93:9090`
|
|
32
|
-
- Loki: `http://100.118.68.93:3100`
|
|
33
|
-
- Alloy UI: `http://100.118.68.93:12345`
|
|
34
|
-
|
|
35
|
-
This keeps the stack private without adding a public reverse proxy or requiring an SSH tunnel for routine access.
|
|
36
|
-
If the host's Tailscale IP changes, set `OBSERVABILITY_TAILSCALE_IP` in `/home/kostard/forgecad-observability/.env` before redeploying.
|
|
37
|
-
The Compose file uses pinned image digests rather than floating `latest` tags so repeat deploys stay predictable.
|
|
38
|
-
|
|
39
|
-
## Deploy
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
bash scripts/prod/observability-deploy.sh
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
The deploy script:
|
|
46
|
-
|
|
47
|
-
1. syncs `ops/observability/` to `/home/kostard/forgecad-observability` on Hetzner
|
|
48
|
-
2. creates `/home/kostard/forgecad-observability/.env` if it does not already exist
|
|
49
|
-
3. pulls fresh images
|
|
50
|
-
4. starts the stack with `docker compose up -d`
|
|
51
|
-
|
|
52
|
-
## Check status
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
bash scripts/prod/observability-status.sh
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Incident Workflow
|
|
59
|
-
|
|
60
|
-
This stack is the primary way we debug production incidents.
|
|
61
|
-
|
|
62
|
-
1. Make sure your laptop is connected to Tailscale.
|
|
63
|
-
|
|
64
|
-
2. Check Uptime Kuma first:
|
|
65
|
-
|
|
66
|
-
- `http://100.118.68.93:3001`
|
|
67
|
-
- Use it to answer "is the site/API reachable from outside?" before diving into internals.
|
|
68
|
-
|
|
69
|
-
3. Check Grafana next:
|
|
70
|
-
|
|
71
|
-
- `http://100.118.68.93:3000`
|
|
72
|
-
- Start with the `ForgeCAD Host Overview` dashboard.
|
|
73
|
-
- Then switch to `ForgeCAD Web Requests` for throughput, 4xx/5xx rate, latency, and top API routes.
|
|
74
|
-
- Then switch to `Explore` for live investigation.
|
|
75
|
-
|
|
76
|
-
4. In Grafana Explore, use:
|
|
77
|
-
|
|
78
|
-
- `Loki` when you want logs
|
|
79
|
-
- `Prometheus` when you want metrics
|
|
80
|
-
|
|
81
|
-
Useful Loki queries:
|
|
82
|
-
|
|
83
|
-
```text
|
|
84
|
-
{container=~"forgecad-web-.*|forgecad-backend-web-.*"}
|
|
85
|
-
{container=~"forgecad-web-.*|forgecad-backend-web-.*"} |= "error"
|
|
86
|
-
{container=~"forgecad-web-.*|forgecad-beta-web-.*"} |= "DB slow query"
|
|
87
|
-
{container=~"forgecad-web-.*|forgecad-beta-web-.*"} | json | event="db.query"
|
|
88
|
-
{container=~"forgecad-beta-.*"}
|
|
89
|
-
{container="kamal-proxy"}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Useful Prometheus queries:
|
|
93
|
-
|
|
94
|
-
```text
|
|
95
|
-
up
|
|
96
|
-
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
|
|
97
|
-
100 * (1 - node_filesystem_avail_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{mountpoint="/",fstype!~"tmpfs|overlay"})
|
|
98
|
-
sum by (container) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
|
|
99
|
-
sum(rate(forge_http_request_total[$__rate_interval]))
|
|
100
|
-
sum(rate(forge_http_request_total{surface="api",status_class="5xx"}[$__rate_interval]))
|
|
101
|
-
histogram_quantile(0.95, sum by (le) (rate(forge_http_request_duration_seconds_bucket[$__rate_interval])))
|
|
102
|
-
topk(10, sum by (forge_env, method, route) (rate(forge_http_request_total{surface="api"}[$__rate_interval])))
|
|
103
|
-
sum by (forge_env, operation) (rate(forge_db_query_total[$__rate_interval]))
|
|
104
|
-
histogram_quantile(0.95, sum by (forge_env, operation, le) (rate(forge_db_query_duration_seconds_bucket[$__rate_interval])))
|
|
105
|
-
sum by (forge_env, relation) (rate(forge_db_slow_query_total[$__rate_interval]))
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
5. Use the `ForgeCAD Web Requests` dashboard when the issue smells app-level traffic, request failures, or latency:
|
|
109
|
-
|
|
110
|
-
- overall request rate
|
|
111
|
-
- API-only request rate
|
|
112
|
-
- 4xx and 5xx rates
|
|
113
|
-
- P95 request latency
|
|
114
|
-
- request concurrency
|
|
115
|
-
- top API routes by throughput, 4xx rate, 5xx rate, and latency
|
|
116
|
-
|
|
117
|
-
Health probes to `/api/health` are intentionally excluded so low-traffic charts stay focused on real user traffic instead of uptime polling.
|
|
118
|
-
|
|
119
|
-
6. Use the `ForgeCAD DB Queries` dashboard when the issue smells database-related:
|
|
120
|
-
|
|
121
|
-
- Query rate by operation
|
|
122
|
-
- P95 query latency by operation
|
|
123
|
-
- Slow-query rate
|
|
124
|
-
- Error-query rate
|
|
125
|
-
- Top relations by average latency
|
|
126
|
-
- Queries currently in flight
|
|
127
|
-
|
|
128
|
-
The dashboard is backed by app-native metrics from the ForgeCAD web service, not host-level guesses.
|
|
129
|
-
|
|
130
|
-
6. Re-check prod explicitly after any restart or deploy:
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
curl -fsS https://forgecad.io/api/health
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
7. If observability itself is degraded, fall back to SSH and direct Docker inspection:
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
ssh hetzner
|
|
140
|
-
docker ps --format "{{.Names}}\t{{.Status}}"
|
|
141
|
-
docker logs --tail 200 <container>
|
|
142
|
-
df -h /
|
|
143
|
-
free -h
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Tool Roles
|
|
147
|
-
|
|
148
|
-
- `Grafana`: main operator UI for dashboards, Explore, and datasource-backed queries
|
|
149
|
-
- `Loki`: log backend queried through Grafana Explore
|
|
150
|
-
- `Prometheus`: metrics backend and alert rule evaluation
|
|
151
|
-
- `Uptime Kuma`: external uptime/status checks
|
|
152
|
-
|
|
153
|
-
## Credentials
|
|
154
|
-
|
|
155
|
-
Grafana admin credentials live on Hetzner in `/home/kostard/forgecad-observability/.env`.
|
|
156
|
-
|
|
157
|
-
## What phase 1 covers
|
|
158
|
-
|
|
159
|
-
- host CPU, memory, and disk metrics
|
|
160
|
-
- container CPU and memory metrics
|
|
161
|
-
- Docker stdout/stderr logs in Loki
|
|
162
|
-
- app-native HTTP request metrics from the ForgeCAD web app
|
|
163
|
-
- app-native DB query metrics from the ForgeCAD web app
|
|
164
|
-
- Grafana data sources and a starter host dashboard
|
|
165
|
-
- a provisioned `ForgeCAD Web Requests` dashboard
|
|
166
|
-
- a provisioned `ForgeCAD DB Queries` dashboard
|
|
167
|
-
- starter Prometheus alert rules for target health, memory, disk, and observability restarts
|
|
168
|
-
- Uptime Kuma for health checks and notifications
|
|
169
|
-
|
|
170
|
-
Prometheus reaches the web app metrics listener over the private `kamal` Docker network. The app exposes `/metrics` on an internal metrics port, not through the public Cloudflare route.
|
|
171
|
-
|
|
172
|
-
## What phase 1 does not cover
|
|
173
|
-
|
|
174
|
-
- distributed Loki storage
|
|
175
|
-
- Postgres-native query stats such as `pg_stat_statements`
|
|
176
|
-
- tracing / Tempo
|
|
177
|
-
- public Grafana hostnames
|
|
178
|
-
- automatic provisioning of Uptime Kuma monitors
|
|
179
|
-
|
|
180
|
-
Those can be added later once the basic operator loop is working.
|
|
181
|
-
|
|
182
|
-
## First Alerts
|
|
183
|
-
|
|
184
|
-
Phase 1 ships starter Prometheus rules for:
|
|
185
|
-
|
|
186
|
-
- observability targets going down
|
|
187
|
-
- host memory staying below 10% available
|
|
188
|
-
- root disk staying below 15% free
|
|
189
|
-
- observability containers restarting
|
|
190
|
-
|
|
191
|
-
These are intentionally conservative. Let them bake before wiring noisy notifications.
|
|
192
|
-
|
|
193
|
-
## Rollback and Safety
|
|
194
|
-
|
|
195
|
-
- Stop the stack with `cd /home/kostard/forgecad-observability && docker compose down` if it causes unexpected pressure.
|
|
196
|
-
- The observability services bind only to the host's Tailscale address, so they stay off the public internet and do not compete with Kamal for `80/443`.
|
|
197
|
-
- Prod health should always be rechecked with `curl -fsS https://forgecad.io/api/health` after any observability deploy or restart.
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Projects and File Management
|
|
2
|
-
|
|
3
|
-
Projects are the top-level container for user work in ForgeCAD. Each project groups related scripts and output files under a single name with access controls.
|
|
4
|
-
|
|
5
|
-
## Project Model
|
|
6
|
-
|
|
7
|
-
Each project has:
|
|
8
|
-
|
|
9
|
-
| Field | Type | Description |
|
|
10
|
-
|-------|------|-------------|
|
|
11
|
-
| `id` | UUID | Primary key |
|
|
12
|
-
| `slug` | string | URL-safe identifier, unique per owner |
|
|
13
|
-
| `name` | string | Display name |
|
|
14
|
-
| `visibility` | `private` \| `public` | Controls unauthenticated access |
|
|
15
|
-
| `ownerId` | UUID | Foreign key to the user who created the project |
|
|
16
|
-
|
|
17
|
-
Source: `server/db/schema.ts`
|
|
18
|
-
|
|
19
|
-
## Member Roles
|
|
20
|
-
|
|
21
|
-
Projects support collaborative access through membership:
|
|
22
|
-
|
|
23
|
-
| Role | Capabilities |
|
|
24
|
-
|------|-------------|
|
|
25
|
-
| **owner** | Full control: rename, delete, manage members, read/write files |
|
|
26
|
-
| **editor** | Read and write files (save, delete, mkdir) |
|
|
27
|
-
| **viewer** | Read files, watch for changes |
|
|
28
|
-
|
|
29
|
-
The owner is always the user who created the project. Ownership cannot be transferred through the membership API.
|
|
30
|
-
|
|
31
|
-
## File Storage
|
|
32
|
-
|
|
33
|
-
Project files are stored on disk, not in the database. The storage root is configured by the `FORGE_STORAGE_ROOT` environment variable (default: `/data/projects` in Docker). Each project gets an isolated directory under this root.
|
|
34
|
-
|
|
35
|
-
See [deployment.md](../deployment.md) for Docker volume and storage configuration.
|
|
36
|
-
|
|
37
|
-
### Supported File Types
|
|
38
|
-
|
|
39
|
-
| Extension | Purpose |
|
|
40
|
-
|-----------|---------|
|
|
41
|
-
| `.forge.js` | ForgeCAD model scripts |
|
|
42
|
-
| `.js` | Utility modules (imported by scripts) |
|
|
43
|
-
| `.svg` | Vector assets |
|
|
44
|
-
| `.dxf` | 2D CAD profile assets |
|
|
45
|
-
| `.stl`, `.3mf` | Exported mesh files |
|
|
46
|
-
|
|
47
|
-
### Path Traversal Protection
|
|
48
|
-
|
|
49
|
-
All file operations validate resolved paths against the project's storage directory. Any path that escapes the project root (via `..` segments or symlinks) is rejected. This check runs in `server/storage.ts` before any read or write reaches the filesystem.
|
|
50
|
-
|
|
51
|
-
### Storage Quotas
|
|
52
|
-
|
|
53
|
-
Each user has a storage quota tracked via `storage_used_bytes` on the user record.
|
|
54
|
-
|
|
55
|
-
| Limit | Value |
|
|
56
|
-
|-------|-------|
|
|
57
|
-
| Storage per user | 50 MB |
|
|
58
|
-
| Projects per user | 20 |
|
|
59
|
-
|
|
60
|
-
The quota is checked before every file write. Storage counters are adjusted atomically and clamped to zero to prevent negative drift from race conditions or deleted files.
|
|
61
|
-
|
|
62
|
-
Source: `server/quotas.ts`
|
|
63
|
-
|
|
64
|
-
## Real-Time File Watching
|
|
65
|
-
|
|
66
|
-
Clients can subscribe to file changes via Server-Sent Events:
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
GET /api/projects/:id/watch
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
This endpoint streams events whenever files in the project are created, modified, or deleted. The editor uses this to stay in sync when multiple clients have the same project open.
|
|
73
|
-
|
|
74
|
-
## API Endpoints
|
|
75
|
-
|
|
76
|
-
### Projects
|
|
77
|
-
|
|
78
|
-
| Method | Route | Min Role | Purpose |
|
|
79
|
-
|--------|-------|----------|---------|
|
|
80
|
-
| GET | `/api/projects` | -- | List the authenticated user's projects |
|
|
81
|
-
| POST | `/api/projects` | -- | Create a new project |
|
|
82
|
-
| GET | `/api/projects/:id` | viewer | Get project details |
|
|
83
|
-
| PATCH | `/api/projects/:id` | owner | Update project name, slug, or visibility |
|
|
84
|
-
| DELETE | `/api/projects/:id` | owner | Delete project and all its files |
|
|
85
|
-
|
|
86
|
-
### Members
|
|
87
|
-
|
|
88
|
-
| Method | Route | Min Role | Purpose |
|
|
89
|
-
|--------|-------|----------|---------|
|
|
90
|
-
| GET | `/api/projects/:id/members` | viewer | List project members |
|
|
91
|
-
| POST | `/api/projects/:id/members` | owner | Add a member by user ID |
|
|
92
|
-
| PATCH | `/api/projects/:id/members/:userId` | owner | Change a member's role |
|
|
93
|
-
| DELETE | `/api/projects/:id/members/:userId` | owner | Remove a member |
|
|
94
|
-
|
|
95
|
-
### Files
|
|
96
|
-
|
|
97
|
-
| Method | Route | Min Role | Purpose |
|
|
98
|
-
|--------|-------|----------|---------|
|
|
99
|
-
| GET | `/api/projects/:id/watch` | viewer | SSE stream of file changes |
|
|
100
|
-
| POST | `/api/projects/:id/save` | editor | Write file contents to disk |
|
|
101
|
-
| POST | `/api/projects/:id/delete` | editor | Delete a file |
|
|
102
|
-
| POST | `/api/projects/:id/mkdir` | editor | Create a directory |
|
|
103
|
-
| GET | `/api/projects/:id/read-binary` | viewer | Download a binary file (meshes, assets) |
|
|
104
|
-
|
|
105
|
-
Source: `server/routes/projects.ts`, `server/routes/files.ts`
|
|
106
|
-
|
|
107
|
-
## Related
|
|
108
|
-
|
|
109
|
-
- [Architecture](architecture.md) -- system overview and deployment topology
|
|
110
|
-
- [Sharing](sharing.md) -- model publishing and public URLs
|
|
111
|
-
- [Deployment](../deployment.md) -- storage volume configuration, Docker setup
|