nerdsystem 0.0.0-stage → 0.0.2
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 +21 -0
- package/README.md +12 -2
- package/dist/api.d.ts +58 -0
- package/dist/api.js +59 -0
- package/dist/commands/agents.d.ts +9 -0
- package/dist/commands/agents.js +45 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/auth.js +25 -0
- package/dist/commands/billing.d.ts +14 -0
- package/dist/commands/billing.js +42 -0
- package/dist/commands/builds.d.ts +12 -0
- package/dist/commands/builds.js +40 -0
- package/dist/commands/check.d.ts +20 -0
- package/dist/commands/check.js +145 -0
- package/dist/commands/db.d.ts +9 -0
- package/dist/commands/db.js +32 -0
- package/dist/commands/deploy.d.ts +27 -0
- package/dist/commands/deploy.js +119 -0
- package/dist/commands/dev.d.ts +3 -0
- package/dist/commands/dev.js +38 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +42 -0
- package/dist/commands/github.d.ts +17 -0
- package/dist/commands/github.js +49 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +107 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.js +21 -0
- package/dist/commands/ops.d.ts +29 -0
- package/dist/commands/ops.js +113 -0
- package/dist/commands/org.d.ts +47 -0
- package/dist/commands/org.js +121 -0
- package/dist/commands/verify.d.ts +24 -0
- package/dist/commands/verify.js +50 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +22 -0
- package/dist/gates/attempts.d.ts +18 -0
- package/dist/gates/attempts.js +38 -0
- package/dist/gates/code.d.ts +15 -0
- package/dist/gates/code.js +179 -0
- package/dist/gates/fs.d.ts +7 -0
- package/dist/gates/fs.js +35 -0
- package/dist/gates/index.d.ts +26 -0
- package/dist/gates/index.js +68 -0
- package/dist/gates/rules.d.ts +19 -0
- package/dist/gates/rules.js +58 -0
- package/dist/gates/secrets.d.ts +11 -0
- package/dist/gates/secrets.js +98 -0
- package/dist/gates/suppressions.d.ts +2 -0
- package/dist/gates/suppressions.js +57 -0
- package/dist/gates/text.d.ts +31 -0
- package/dist/gates/text.js +72 -0
- package/dist/gates/tools.d.ts +5 -0
- package/dist/gates/tools.js +69 -0
- package/dist/gates/types.d.ts +72 -0
- package/dist/gates/types.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +268 -0
- package/dist/project.d.ts +11 -0
- package/dist/project.js +51 -0
- package/dist/util.d.ts +21 -0
- package/dist/util.js +39 -0
- package/package.json +55 -3
- package/rules/semgrep.yml +77 -0
- package/template/.claude/settings.json +12 -0
- package/template/.claude/skills/nerdsystem/SKILL.md +123 -0
- package/template/.cursor/mcp.json +8 -0
- package/template/.cursor/rules/nerdsystem.mdc +124 -0
- package/template/.gemini/settings.json +8 -0
- package/template/.github/copilot-instructions.md +120 -0
- package/template/.github/dependabot.yml +14 -0
- package/template/.mcp.json +8 -0
- package/template/.vscode/mcp.json +9 -0
- package/template/AGENTS.md +120 -0
- package/template/CLAUDE.md +32 -0
- package/template/GEMINI.md +120 -0
- package/template/drizzle.config.ts +7 -0
- package/template/gitignore +7 -0
- package/template/index.html +12 -0
- package/template/migrations/0000_init.sql +7 -0
- package/template/nerd.json +5 -0
- package/template/package.json +33 -0
- package/template/scripts/sync-agents.mjs +28 -0
- package/template/src/client/App.tsx +182 -0
- package/template/src/client/api.ts +42 -0
- package/template/src/client/main.tsx +10 -0
- package/template/src/client/styles.css +16 -0
- package/template/src/client/vite-env.d.ts +1 -0
- package/template/src/server/db/index.ts +7 -0
- package/template/src/server/db/schema.ts +14 -0
- package/template/src/server/index.ts +23 -0
- package/template/src/server/routes/todos.ts +57 -0
- package/template/tsconfig.client.json +18 -0
- package/template/tsconfig.json +4 -0
- package/template/tsconfig.worker.json +18 -0
- package/template/vite.config.ts +7 -0
- package/template/wrangler.jsonc +18 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nerdsystem
|
|
3
|
+
description: How to build, run, and deploy this app on nerdsystem. Use whenever the task touches the API (src/server), the database schema or migrations, auth, roles, deploying, or debugging the deployed app. Covers the nerd CLI, the @nerdsystem/sdk runtime, and the platform rules that `nerd check` enforces.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Building on nerdsystem
|
|
7
|
+
|
|
8
|
+
This app is deployed by nerdsystem, which also provides authentication, authorization, a D1 database, and an R2 bucket. You never touch Cloudflare directly. Everything goes through the `nerd` CLI, which wraps the platform API. Every `nerd` command accepts `--json`; prefer it when you need to parse output.
|
|
9
|
+
|
|
10
|
+
## First session with a new project
|
|
11
|
+
|
|
12
|
+
If this project still has the template's todo app (the `todos` table and `src/server/routes/todos.ts`), treat the person as new and offer a short walkthrough before building anything:
|
|
13
|
+
|
|
14
|
+
1. Ask one question: what should the app do, in a sentence or two. Do not ask about stacks, databases, or hosting; those are decided.
|
|
15
|
+
2. Say what you will build first, in plain words: the one table and the one screen that make the idea real. Keep the first feature small enough to deploy within the session.
|
|
16
|
+
3. Replace the todo example with that feature: schema, migration (`pnpm db:generate`), a router under `/api/...`, and the client screen. Keep signup and login as they are; they already work.
|
|
17
|
+
4. Run `nerd check`, then `nerd dev`, and tell the person to open the local URL, create an account, and try it. Fix what they report.
|
|
18
|
+
5. Run `nerd deploy` and give them the live URL. Explain in two lines that every deploy is checked for leaked secrets and vulnerable dependencies, and that they can run `nerd verify` any time.
|
|
19
|
+
6. Offer the next steps once, briefly: link the GitHub repo for previews on pull requests (`nerd github connect`), add a custom domain, or invite a teammate from the dashboard.
|
|
20
|
+
|
|
21
|
+
Throughout, show commands rather than describing them, and never ask the person to edit config files; the platform owns `wrangler.jsonc` and `nerd.json`.
|
|
22
|
+
|
|
23
|
+
## The loop
|
|
24
|
+
|
|
25
|
+
1. Make the change.
|
|
26
|
+
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
27
|
+
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
28
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and prints the URL.
|
|
29
|
+
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
30
|
+
|
|
31
|
+
Run `nerd check --json` when you want a machine-readable list of problems.
|
|
32
|
+
|
|
33
|
+
## Deploying from GitHub
|
|
34
|
+
|
|
35
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Previews share the production database and do not run migrations; a PR that changes the schema will only work in full after it merges.
|
|
36
|
+
|
|
37
|
+
## Where things go
|
|
38
|
+
|
|
39
|
+
| Concern | Location |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| API routes | `src/server/index.ts` mounts routers under `/api/...`. Put each resource in `src/server/routes/<name>.ts`. |
|
|
42
|
+
| Database schema | `src/server/db/schema.ts` (Drizzle). Then `pnpm db:generate` to produce a migration in `migrations/`. |
|
|
43
|
+
| Queries | `db(c.env.DB)` from `src/server/db/index.ts`. Drizzle query builder, D1 dialect (SQLite). |
|
|
44
|
+
| Files | `c.env.STORAGE` is an R2 bucket. `put`, `get`, `delete`, `list`. Key files by user id. |
|
|
45
|
+
| Frontend | `src/client/` React, built by Vite. `src/client/api.ts` has a fetch wrapper that sends the session cookie. |
|
|
46
|
+
| Roles and signup policy | `nerd auth config`. Not in code. |
|
|
47
|
+
|
|
48
|
+
## Auth and authorization
|
|
49
|
+
|
|
50
|
+
The SDK handles sessions. There is no login code to write.
|
|
51
|
+
|
|
52
|
+
- `c.var.user` is `{ id, email, name, roles }` or `null` on every request.
|
|
53
|
+
- `requireAuth()` middleware returns 401 when signed out.
|
|
54
|
+
- `requireRole("admin")` returns 403 unless the user has one of the given roles. Multiple roles are OR.
|
|
55
|
+
- The frontend calls `/api/auth/signup`, `/api/auth/login`, `/api/auth/logout`, `/api/auth/me`, `/api/auth/forgot`, and `/api/auth/reset`. The template's `auth` helper in `src/client/api.ts` wraps them. Password reset emails link to `/reset-password?token=...`, which the template client renders; keep that route if you restructure the client.
|
|
56
|
+
- Roles are a list the platform owns. See the current list with `nerd auth config`. Add one with `nerd auth config --roles admin,member,editor`. The default role for new signups is set with `--default-role`. Turn public signup off with `--signup off`.
|
|
57
|
+
- To promote a user from code, use the admin client: `new AuthServiceClient(c.env).users.setRoles(userId, ["admin"])`. Guard that route with `requireRole("admin")`.
|
|
58
|
+
|
|
59
|
+
Example of a protected resource router:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Hono } from "hono";
|
|
63
|
+
import { eq } from "drizzle-orm";
|
|
64
|
+
import { requireAuth, type NerdHonoEnv } from "@nerdsystem/sdk";
|
|
65
|
+
import { db, schema } from "../db";
|
|
66
|
+
|
|
67
|
+
const notes = new Hono<NerdHonoEnv>();
|
|
68
|
+
notes.use("*", requireAuth());
|
|
69
|
+
notes.get("/", async (c) => {
|
|
70
|
+
const rows = await db(c.env.DB).select().from(schema.notes).where(eq(schema.notes.userId, c.var.user!.id)).all();
|
|
71
|
+
return c.json({ notes: rows });
|
|
72
|
+
});
|
|
73
|
+
export default notes;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then in `src/server/index.ts`: `app.route("/api/notes", notes);`
|
|
77
|
+
|
|
78
|
+
## Data rules
|
|
79
|
+
|
|
80
|
+
- Every table holding user data has a `user_id` text column, and every read and write filters on `c.var.user.id`. This is how users are kept apart.
|
|
81
|
+
- Schema changes are migrations. Edit `schema.ts`, run `pnpm db:generate`, commit the new file in `migrations/`. Never hand-write SQL into production; `nerd deploy` applies migrations in name order and records which ran.
|
|
82
|
+
- Migrations are forward-only. To undo, write another migration.
|
|
83
|
+
- Prefer small, additive migrations. SQLite cannot alter columns in place.
|
|
84
|
+
|
|
85
|
+
## Errors
|
|
86
|
+
|
|
87
|
+
API errors are `{ error: { code, message } }` with a matching HTTP status. Use the same shape in your routes so the frontend's `ApiError` keeps working. Codes in use: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `internal`.
|
|
88
|
+
|
|
89
|
+
## Security gates
|
|
90
|
+
|
|
91
|
+
The `nerdsystem` MCP server (configured in `.mcp.json`) gives you two tools; prefer them over shelling out:
|
|
92
|
+
|
|
93
|
+
- `run_gates` runs every gate a deploy runs (secrets in source and in the built bundle, vulnerable dependencies, platform rules, static analysis, acceptable-use policy, personal-data heuristics, typecheck, and scanner suppressions) and returns `failures`. Each has `rule_id`, `file`, `line`, `risk` (what could happen), `fix` (what to change), `verify` (the command that confirms it), `attempt`, and `audience`.
|
|
94
|
+
- `explain_failure` takes a `rule_id` and returns the risk, the fix, and a wrong/right example.
|
|
95
|
+
|
|
96
|
+
The loop: run `run_gates`, fix every failure with `audience: "agent"`, run `run_gates` again (it re-runs every gate, not just the one that failed), repeat. Rules you cannot clear:
|
|
97
|
+
|
|
98
|
+
- `audience: "human"` means a person must act: a secret that was committed must be rotated at the provider before you remove it; an ignore comment, ignore file, or gate setting you added is a suppression that only a person can approve in the dashboard. Never add `nosemgrep`, `gitleaks:allow`, `nerd-ignore`, or a `gates` key in `nerd.json` to make a finding go away.
|
|
99
|
+
- When `stop` is non-empty, the same rule has failed three runs in a row. Stop, summarize what you tried in plain words, and hand it to the person.
|
|
100
|
+
|
|
101
|
+
Without MCP, `nerd verify --json` returns the same shape, and `nerd verify --rule <rule_id>` runs one rule. Secrets are reported masked (`sk_live_****4f2a`); never print a credential yourself. Keep secrets in `.dev.vars` locally and in platform settings in production; never prefix a secret with `VITE_`, because those values are compiled into the public client bundle.
|
|
102
|
+
|
|
103
|
+
## What `nerd check` rejects
|
|
104
|
+
|
|
105
|
+
- A root app that is not `createNerdApp()` from `@nerdsystem/sdk`.
|
|
106
|
+
- Any route outside `/api/`. Other paths serve the static client and never reach the Worker.
|
|
107
|
+
- New bindings in `wrangler.jsonc`. Only `DB`, `STORAGE`, and `ASSETS` exist. Durable Objects are not available.
|
|
108
|
+
- Scripts that call `wrangler deploy`, `wrangler secret`, or remote `wrangler d1` commands.
|
|
109
|
+
- Typecheck failures in either `src/server` or `src/client`.
|
|
110
|
+
|
|
111
|
+
## Scheduled jobs and email
|
|
112
|
+
|
|
113
|
+
- **Scheduled jobs**: put the work in the `POST /api/cron` handler (already in `src/server/index.ts`, guarded by `verifyCron()`), then turn the schedule on with `nerd cron hourly` or `nerd cron daily`. The platform calls it; the app never needs its own timer.
|
|
114
|
+
- **Secrets** (Stripe keys, API tokens): never in code or in the client. Locally they live in `.dev.vars` (gitignored). For production, ask the person to run `nerd secret set NAME` and paste the value; it reaches the app as `c.env.NAME` on the next deploy. `nerd secret list` shows names only. Do not put a real secret value in any command you run yourself.
|
|
115
|
+
- **Email**: `sendEmail(c.env, { to, subject, text, html? })` from `@nerdsystem/sdk` sends through the platform. It is limited per day by plan (50 on Free, 2,000 on Pro at $39 a month, 10,000 on Studio), so use it for transactional mail such as receipts and notifications, not bulk. Plans can be paid monthly or yearly at ten times the monthly price.
|
|
116
|
+
|
|
117
|
+
## Debugging production
|
|
118
|
+
|
|
119
|
+
When something is wrong on the deployed app: `nerd logs --since 1h` (add `--q "text"` to search, `--pr 12` for a preview) shows errors with the request that caused them. Reproduce locally with `nerd dev` using the same input, fix it, run `nerd verify`, and deploy or open a pull request. If a deploy made things worse, `nerd rollback` puts the previous production bundle back without touching the database. Write a one-line note on every deploy (`nerd deploy --note "fix: todos query filtered by user"`); the dashboard shows these as a changelog people can read.
|
|
120
|
+
|
|
121
|
+
## Not available yet
|
|
122
|
+
|
|
123
|
+
Queues, Durable Objects, and log streaming. If the task needs one, say so instead of working around it with an unsupported binding.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: How to build, run, and deploy this app on nerdsystem. Applies to server, database, auth, and deploy work.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (2834c4c81f7f); edit the skill, then run: nerd agents sync -->
|
|
6
|
+
|
|
7
|
+
# Building on nerdsystem
|
|
8
|
+
|
|
9
|
+
This app is deployed by nerdsystem, which also provides authentication, authorization, a D1 database, and an R2 bucket. You never touch Cloudflare directly. Everything goes through the `nerd` CLI, which wraps the platform API. Every `nerd` command accepts `--json`; prefer it when you need to parse output.
|
|
10
|
+
|
|
11
|
+
## First session with a new project
|
|
12
|
+
|
|
13
|
+
If this project still has the template's todo app (the `todos` table and `src/server/routes/todos.ts`), treat the person as new and offer a short walkthrough before building anything:
|
|
14
|
+
|
|
15
|
+
1. Ask one question: what should the app do, in a sentence or two. Do not ask about stacks, databases, or hosting; those are decided.
|
|
16
|
+
2. Say what you will build first, in plain words: the one table and the one screen that make the idea real. Keep the first feature small enough to deploy within the session.
|
|
17
|
+
3. Replace the todo example with that feature: schema, migration (`pnpm db:generate`), a router under `/api/...`, and the client screen. Keep signup and login as they are; they already work.
|
|
18
|
+
4. Run `nerd check`, then `nerd dev`, and tell the person to open the local URL, create an account, and try it. Fix what they report.
|
|
19
|
+
5. Run `nerd deploy` and give them the live URL. Explain in two lines that every deploy is checked for leaked secrets and vulnerable dependencies, and that they can run `nerd verify` any time.
|
|
20
|
+
6. Offer the next steps once, briefly: link the GitHub repo for previews on pull requests (`nerd github connect`), add a custom domain, or invite a teammate from the dashboard.
|
|
21
|
+
|
|
22
|
+
Throughout, show commands rather than describing them, and never ask the person to edit config files; the platform owns `wrangler.jsonc` and `nerd.json`.
|
|
23
|
+
|
|
24
|
+
## The loop
|
|
25
|
+
|
|
26
|
+
1. Make the change.
|
|
27
|
+
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
28
|
+
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
29
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and prints the URL.
|
|
30
|
+
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
31
|
+
|
|
32
|
+
Run `nerd check --json` when you want a machine-readable list of problems.
|
|
33
|
+
|
|
34
|
+
## Deploying from GitHub
|
|
35
|
+
|
|
36
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Previews share the production database and do not run migrations; a PR that changes the schema will only work in full after it merges.
|
|
37
|
+
|
|
38
|
+
## Where things go
|
|
39
|
+
|
|
40
|
+
| Concern | Location |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| API routes | `src/server/index.ts` mounts routers under `/api/...`. Put each resource in `src/server/routes/<name>.ts`. |
|
|
43
|
+
| Database schema | `src/server/db/schema.ts` (Drizzle). Then `pnpm db:generate` to produce a migration in `migrations/`. |
|
|
44
|
+
| Queries | `db(c.env.DB)` from `src/server/db/index.ts`. Drizzle query builder, D1 dialect (SQLite). |
|
|
45
|
+
| Files | `c.env.STORAGE` is an R2 bucket. `put`, `get`, `delete`, `list`. Key files by user id. |
|
|
46
|
+
| Frontend | `src/client/` React, built by Vite. `src/client/api.ts` has a fetch wrapper that sends the session cookie. |
|
|
47
|
+
| Roles and signup policy | `nerd auth config`. Not in code. |
|
|
48
|
+
|
|
49
|
+
## Auth and authorization
|
|
50
|
+
|
|
51
|
+
The SDK handles sessions. There is no login code to write.
|
|
52
|
+
|
|
53
|
+
- `c.var.user` is `{ id, email, name, roles }` or `null` on every request.
|
|
54
|
+
- `requireAuth()` middleware returns 401 when signed out.
|
|
55
|
+
- `requireRole("admin")` returns 403 unless the user has one of the given roles. Multiple roles are OR.
|
|
56
|
+
- The frontend calls `/api/auth/signup`, `/api/auth/login`, `/api/auth/logout`, `/api/auth/me`, `/api/auth/forgot`, and `/api/auth/reset`. The template's `auth` helper in `src/client/api.ts` wraps them. Password reset emails link to `/reset-password?token=...`, which the template client renders; keep that route if you restructure the client.
|
|
57
|
+
- Roles are a list the platform owns. See the current list with `nerd auth config`. Add one with `nerd auth config --roles admin,member,editor`. The default role for new signups is set with `--default-role`. Turn public signup off with `--signup off`.
|
|
58
|
+
- To promote a user from code, use the admin client: `new AuthServiceClient(c.env).users.setRoles(userId, ["admin"])`. Guard that route with `requireRole("admin")`.
|
|
59
|
+
|
|
60
|
+
Example of a protected resource router:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { Hono } from "hono";
|
|
64
|
+
import { eq } from "drizzle-orm";
|
|
65
|
+
import { requireAuth, type NerdHonoEnv } from "@nerdsystem/sdk";
|
|
66
|
+
import { db, schema } from "../db";
|
|
67
|
+
|
|
68
|
+
const notes = new Hono<NerdHonoEnv>();
|
|
69
|
+
notes.use("*", requireAuth());
|
|
70
|
+
notes.get("/", async (c) => {
|
|
71
|
+
const rows = await db(c.env.DB).select().from(schema.notes).where(eq(schema.notes.userId, c.var.user!.id)).all();
|
|
72
|
+
return c.json({ notes: rows });
|
|
73
|
+
});
|
|
74
|
+
export default notes;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then in `src/server/index.ts`: `app.route("/api/notes", notes);`
|
|
78
|
+
|
|
79
|
+
## Data rules
|
|
80
|
+
|
|
81
|
+
- Every table holding user data has a `user_id` text column, and every read and write filters on `c.var.user.id`. This is how users are kept apart.
|
|
82
|
+
- Schema changes are migrations. Edit `schema.ts`, run `pnpm db:generate`, commit the new file in `migrations/`. Never hand-write SQL into production; `nerd deploy` applies migrations in name order and records which ran.
|
|
83
|
+
- Migrations are forward-only. To undo, write another migration.
|
|
84
|
+
- Prefer small, additive migrations. SQLite cannot alter columns in place.
|
|
85
|
+
|
|
86
|
+
## Errors
|
|
87
|
+
|
|
88
|
+
API errors are `{ error: { code, message } }` with a matching HTTP status. Use the same shape in your routes so the frontend's `ApiError` keeps working. Codes in use: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `internal`.
|
|
89
|
+
|
|
90
|
+
## Security gates
|
|
91
|
+
|
|
92
|
+
The `nerdsystem` MCP server (configured in `.mcp.json`) gives you two tools; prefer them over shelling out:
|
|
93
|
+
|
|
94
|
+
- `run_gates` runs every gate a deploy runs (secrets in source and in the built bundle, vulnerable dependencies, platform rules, static analysis, acceptable-use policy, personal-data heuristics, typecheck, and scanner suppressions) and returns `failures`. Each has `rule_id`, `file`, `line`, `risk` (what could happen), `fix` (what to change), `verify` (the command that confirms it), `attempt`, and `audience`.
|
|
95
|
+
- `explain_failure` takes a `rule_id` and returns the risk, the fix, and a wrong/right example.
|
|
96
|
+
|
|
97
|
+
The loop: run `run_gates`, fix every failure with `audience: "agent"`, run `run_gates` again (it re-runs every gate, not just the one that failed), repeat. Rules you cannot clear:
|
|
98
|
+
|
|
99
|
+
- `audience: "human"` means a person must act: a secret that was committed must be rotated at the provider before you remove it; an ignore comment, ignore file, or gate setting you added is a suppression that only a person can approve in the dashboard. Never add `nosemgrep`, `gitleaks:allow`, `nerd-ignore`, or a `gates` key in `nerd.json` to make a finding go away.
|
|
100
|
+
- When `stop` is non-empty, the same rule has failed three runs in a row. Stop, summarize what you tried in plain words, and hand it to the person.
|
|
101
|
+
|
|
102
|
+
Without MCP, `nerd verify --json` returns the same shape, and `nerd verify --rule <rule_id>` runs one rule. Secrets are reported masked (`sk_live_****4f2a`); never print a credential yourself. Keep secrets in `.dev.vars` locally and in platform settings in production; never prefix a secret with `VITE_`, because those values are compiled into the public client bundle.
|
|
103
|
+
|
|
104
|
+
## What `nerd check` rejects
|
|
105
|
+
|
|
106
|
+
- A root app that is not `createNerdApp()` from `@nerdsystem/sdk`.
|
|
107
|
+
- Any route outside `/api/`. Other paths serve the static client and never reach the Worker.
|
|
108
|
+
- New bindings in `wrangler.jsonc`. Only `DB`, `STORAGE`, and `ASSETS` exist. Durable Objects are not available.
|
|
109
|
+
- Scripts that call `wrangler deploy`, `wrangler secret`, or remote `wrangler d1` commands.
|
|
110
|
+
- Typecheck failures in either `src/server` or `src/client`.
|
|
111
|
+
|
|
112
|
+
## Scheduled jobs and email
|
|
113
|
+
|
|
114
|
+
- **Scheduled jobs**: put the work in the `POST /api/cron` handler (already in `src/server/index.ts`, guarded by `verifyCron()`), then turn the schedule on with `nerd cron hourly` or `nerd cron daily`. The platform calls it; the app never needs its own timer.
|
|
115
|
+
- **Secrets** (Stripe keys, API tokens): never in code or in the client. Locally they live in `.dev.vars` (gitignored). For production, ask the person to run `nerd secret set NAME` and paste the value; it reaches the app as `c.env.NAME` on the next deploy. `nerd secret list` shows names only. Do not put a real secret value in any command you run yourself.
|
|
116
|
+
- **Email**: `sendEmail(c.env, { to, subject, text, html? })` from `@nerdsystem/sdk` sends through the platform. It is limited per day by plan (50 on Free, 2,000 on Pro at $39 a month, 10,000 on Studio), so use it for transactional mail such as receipts and notifications, not bulk. Plans can be paid monthly or yearly at ten times the monthly price.
|
|
117
|
+
|
|
118
|
+
## Debugging production
|
|
119
|
+
|
|
120
|
+
When something is wrong on the deployed app: `nerd logs --since 1h` (add `--q "text"` to search, `--pr 12` for a preview) shows errors with the request that caused them. Reproduce locally with `nerd dev` using the same input, fix it, run `nerd verify`, and deploy or open a pull request. If a deploy made things worse, `nerd rollback` puts the previous production bundle back without touching the database. Write a one-line note on every deploy (`nerd deploy --note "fix: todos query filtered by user"`); the dashboard shows these as a changelog people can read.
|
|
121
|
+
|
|
122
|
+
## Not available yet
|
|
123
|
+
|
|
124
|
+
Queues, Durable Objects, and log streaming. If the task needs one, say so instead of working around it with an unsupported binding.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (2834c4c81f7f); edit the skill, then run: nerd agents sync -->
|
|
2
|
+
|
|
3
|
+
# Building on nerdsystem
|
|
4
|
+
|
|
5
|
+
This app is deployed by nerdsystem, which also provides authentication, authorization, a D1 database, and an R2 bucket. You never touch Cloudflare directly. Everything goes through the `nerd` CLI, which wraps the platform API. Every `nerd` command accepts `--json`; prefer it when you need to parse output.
|
|
6
|
+
|
|
7
|
+
## First session with a new project
|
|
8
|
+
|
|
9
|
+
If this project still has the template's todo app (the `todos` table and `src/server/routes/todos.ts`), treat the person as new and offer a short walkthrough before building anything:
|
|
10
|
+
|
|
11
|
+
1. Ask one question: what should the app do, in a sentence or two. Do not ask about stacks, databases, or hosting; those are decided.
|
|
12
|
+
2. Say what you will build first, in plain words: the one table and the one screen that make the idea real. Keep the first feature small enough to deploy within the session.
|
|
13
|
+
3. Replace the todo example with that feature: schema, migration (`pnpm db:generate`), a router under `/api/...`, and the client screen. Keep signup and login as they are; they already work.
|
|
14
|
+
4. Run `nerd check`, then `nerd dev`, and tell the person to open the local URL, create an account, and try it. Fix what they report.
|
|
15
|
+
5. Run `nerd deploy` and give them the live URL. Explain in two lines that every deploy is checked for leaked secrets and vulnerable dependencies, and that they can run `nerd verify` any time.
|
|
16
|
+
6. Offer the next steps once, briefly: link the GitHub repo for previews on pull requests (`nerd github connect`), add a custom domain, or invite a teammate from the dashboard.
|
|
17
|
+
|
|
18
|
+
Throughout, show commands rather than describing them, and never ask the person to edit config files; the platform owns `wrangler.jsonc` and `nerd.json`.
|
|
19
|
+
|
|
20
|
+
## The loop
|
|
21
|
+
|
|
22
|
+
1. Make the change.
|
|
23
|
+
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
24
|
+
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
25
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and prints the URL.
|
|
26
|
+
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
27
|
+
|
|
28
|
+
Run `nerd check --json` when you want a machine-readable list of problems.
|
|
29
|
+
|
|
30
|
+
## Deploying from GitHub
|
|
31
|
+
|
|
32
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Previews share the production database and do not run migrations; a PR that changes the schema will only work in full after it merges.
|
|
33
|
+
|
|
34
|
+
## Where things go
|
|
35
|
+
|
|
36
|
+
| Concern | Location |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| API routes | `src/server/index.ts` mounts routers under `/api/...`. Put each resource in `src/server/routes/<name>.ts`. |
|
|
39
|
+
| Database schema | `src/server/db/schema.ts` (Drizzle). Then `pnpm db:generate` to produce a migration in `migrations/`. |
|
|
40
|
+
| Queries | `db(c.env.DB)` from `src/server/db/index.ts`. Drizzle query builder, D1 dialect (SQLite). |
|
|
41
|
+
| Files | `c.env.STORAGE` is an R2 bucket. `put`, `get`, `delete`, `list`. Key files by user id. |
|
|
42
|
+
| Frontend | `src/client/` React, built by Vite. `src/client/api.ts` has a fetch wrapper that sends the session cookie. |
|
|
43
|
+
| Roles and signup policy | `nerd auth config`. Not in code. |
|
|
44
|
+
|
|
45
|
+
## Auth and authorization
|
|
46
|
+
|
|
47
|
+
The SDK handles sessions. There is no login code to write.
|
|
48
|
+
|
|
49
|
+
- `c.var.user` is `{ id, email, name, roles }` or `null` on every request.
|
|
50
|
+
- `requireAuth()` middleware returns 401 when signed out.
|
|
51
|
+
- `requireRole("admin")` returns 403 unless the user has one of the given roles. Multiple roles are OR.
|
|
52
|
+
- The frontend calls `/api/auth/signup`, `/api/auth/login`, `/api/auth/logout`, `/api/auth/me`, `/api/auth/forgot`, and `/api/auth/reset`. The template's `auth` helper in `src/client/api.ts` wraps them. Password reset emails link to `/reset-password?token=...`, which the template client renders; keep that route if you restructure the client.
|
|
53
|
+
- Roles are a list the platform owns. See the current list with `nerd auth config`. Add one with `nerd auth config --roles admin,member,editor`. The default role for new signups is set with `--default-role`. Turn public signup off with `--signup off`.
|
|
54
|
+
- To promote a user from code, use the admin client: `new AuthServiceClient(c.env).users.setRoles(userId, ["admin"])`. Guard that route with `requireRole("admin")`.
|
|
55
|
+
|
|
56
|
+
Example of a protected resource router:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { Hono } from "hono";
|
|
60
|
+
import { eq } from "drizzle-orm";
|
|
61
|
+
import { requireAuth, type NerdHonoEnv } from "@nerdsystem/sdk";
|
|
62
|
+
import { db, schema } from "../db";
|
|
63
|
+
|
|
64
|
+
const notes = new Hono<NerdHonoEnv>();
|
|
65
|
+
notes.use("*", requireAuth());
|
|
66
|
+
notes.get("/", async (c) => {
|
|
67
|
+
const rows = await db(c.env.DB).select().from(schema.notes).where(eq(schema.notes.userId, c.var.user!.id)).all();
|
|
68
|
+
return c.json({ notes: rows });
|
|
69
|
+
});
|
|
70
|
+
export default notes;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then in `src/server/index.ts`: `app.route("/api/notes", notes);`
|
|
74
|
+
|
|
75
|
+
## Data rules
|
|
76
|
+
|
|
77
|
+
- Every table holding user data has a `user_id` text column, and every read and write filters on `c.var.user.id`. This is how users are kept apart.
|
|
78
|
+
- Schema changes are migrations. Edit `schema.ts`, run `pnpm db:generate`, commit the new file in `migrations/`. Never hand-write SQL into production; `nerd deploy` applies migrations in name order and records which ran.
|
|
79
|
+
- Migrations are forward-only. To undo, write another migration.
|
|
80
|
+
- Prefer small, additive migrations. SQLite cannot alter columns in place.
|
|
81
|
+
|
|
82
|
+
## Errors
|
|
83
|
+
|
|
84
|
+
API errors are `{ error: { code, message } }` with a matching HTTP status. Use the same shape in your routes so the frontend's `ApiError` keeps working. Codes in use: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `internal`.
|
|
85
|
+
|
|
86
|
+
## Security gates
|
|
87
|
+
|
|
88
|
+
The `nerdsystem` MCP server (configured in `.mcp.json`) gives you two tools; prefer them over shelling out:
|
|
89
|
+
|
|
90
|
+
- `run_gates` runs every gate a deploy runs (secrets in source and in the built bundle, vulnerable dependencies, platform rules, static analysis, acceptable-use policy, personal-data heuristics, typecheck, and scanner suppressions) and returns `failures`. Each has `rule_id`, `file`, `line`, `risk` (what could happen), `fix` (what to change), `verify` (the command that confirms it), `attempt`, and `audience`.
|
|
91
|
+
- `explain_failure` takes a `rule_id` and returns the risk, the fix, and a wrong/right example.
|
|
92
|
+
|
|
93
|
+
The loop: run `run_gates`, fix every failure with `audience: "agent"`, run `run_gates` again (it re-runs every gate, not just the one that failed), repeat. Rules you cannot clear:
|
|
94
|
+
|
|
95
|
+
- `audience: "human"` means a person must act: a secret that was committed must be rotated at the provider before you remove it; an ignore comment, ignore file, or gate setting you added is a suppression that only a person can approve in the dashboard. Never add `nosemgrep`, `gitleaks:allow`, `nerd-ignore`, or a `gates` key in `nerd.json` to make a finding go away.
|
|
96
|
+
- When `stop` is non-empty, the same rule has failed three runs in a row. Stop, summarize what you tried in plain words, and hand it to the person.
|
|
97
|
+
|
|
98
|
+
Without MCP, `nerd verify --json` returns the same shape, and `nerd verify --rule <rule_id>` runs one rule. Secrets are reported masked (`sk_live_****4f2a`); never print a credential yourself. Keep secrets in `.dev.vars` locally and in platform settings in production; never prefix a secret with `VITE_`, because those values are compiled into the public client bundle.
|
|
99
|
+
|
|
100
|
+
## What `nerd check` rejects
|
|
101
|
+
|
|
102
|
+
- A root app that is not `createNerdApp()` from `@nerdsystem/sdk`.
|
|
103
|
+
- Any route outside `/api/`. Other paths serve the static client and never reach the Worker.
|
|
104
|
+
- New bindings in `wrangler.jsonc`. Only `DB`, `STORAGE`, and `ASSETS` exist. Durable Objects are not available.
|
|
105
|
+
- Scripts that call `wrangler deploy`, `wrangler secret`, or remote `wrangler d1` commands.
|
|
106
|
+
- Typecheck failures in either `src/server` or `src/client`.
|
|
107
|
+
|
|
108
|
+
## Scheduled jobs and email
|
|
109
|
+
|
|
110
|
+
- **Scheduled jobs**: put the work in the `POST /api/cron` handler (already in `src/server/index.ts`, guarded by `verifyCron()`), then turn the schedule on with `nerd cron hourly` or `nerd cron daily`. The platform calls it; the app never needs its own timer.
|
|
111
|
+
- **Secrets** (Stripe keys, API tokens): never in code or in the client. Locally they live in `.dev.vars` (gitignored). For production, ask the person to run `nerd secret set NAME` and paste the value; it reaches the app as `c.env.NAME` on the next deploy. `nerd secret list` shows names only. Do not put a real secret value in any command you run yourself.
|
|
112
|
+
- **Email**: `sendEmail(c.env, { to, subject, text, html? })` from `@nerdsystem/sdk` sends through the platform. It is limited per day by plan (50 on Free, 2,000 on Pro at $39 a month, 10,000 on Studio), so use it for transactional mail such as receipts and notifications, not bulk. Plans can be paid monthly or yearly at ten times the monthly price.
|
|
113
|
+
|
|
114
|
+
## Debugging production
|
|
115
|
+
|
|
116
|
+
When something is wrong on the deployed app: `nerd logs --since 1h` (add `--q "text"` to search, `--pr 12` for a preview) shows errors with the request that caused them. Reproduce locally with `nerd dev` using the same input, fix it, run `nerd verify`, and deploy or open a pull request. If a deploy made things worse, `nerd rollback` puts the previous production bundle back without touching the database. Write a one-line note on every deploy (`nerd deploy --note "fix: todos query filtered by user"`); the dashboard shows these as a changelog people can read.
|
|
117
|
+
|
|
118
|
+
## Not available yet
|
|
119
|
+
|
|
120
|
+
Queues, Durable Objects, and log streaming. If the task needs one, say so instead of working around it with an unsupported binding.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Keeps dependencies current. Each update arrives as a pull request, which nerdsystem builds,
|
|
2
|
+
# scans, and previews before you merge it. Minor and patch bumps are grouped into one weekly PR;
|
|
3
|
+
# security updates come separately so they stand out.
|
|
4
|
+
version: 2
|
|
5
|
+
updates:
|
|
6
|
+
- package-ecosystem: npm
|
|
7
|
+
directory: /
|
|
8
|
+
schedule:
|
|
9
|
+
interval: weekly
|
|
10
|
+
day: monday
|
|
11
|
+
open-pull-requests-limit: 5
|
|
12
|
+
groups:
|
|
13
|
+
minor-and-patch:
|
|
14
|
+
update-types: [minor, patch]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<!-- generated from .claude/skills/nerdsystem/SKILL.md (2834c4c81f7f); edit the skill, then run: nerd agents sync -->
|
|
2
|
+
|
|
3
|
+
# Building on nerdsystem
|
|
4
|
+
|
|
5
|
+
This app is deployed by nerdsystem, which also provides authentication, authorization, a D1 database, and an R2 bucket. You never touch Cloudflare directly. Everything goes through the `nerd` CLI, which wraps the platform API. Every `nerd` command accepts `--json`; prefer it when you need to parse output.
|
|
6
|
+
|
|
7
|
+
## First session with a new project
|
|
8
|
+
|
|
9
|
+
If this project still has the template's todo app (the `todos` table and `src/server/routes/todos.ts`), treat the person as new and offer a short walkthrough before building anything:
|
|
10
|
+
|
|
11
|
+
1. Ask one question: what should the app do, in a sentence or two. Do not ask about stacks, databases, or hosting; those are decided.
|
|
12
|
+
2. Say what you will build first, in plain words: the one table and the one screen that make the idea real. Keep the first feature small enough to deploy within the session.
|
|
13
|
+
3. Replace the todo example with that feature: schema, migration (`pnpm db:generate`), a router under `/api/...`, and the client screen. Keep signup and login as they are; they already work.
|
|
14
|
+
4. Run `nerd check`, then `nerd dev`, and tell the person to open the local URL, create an account, and try it. Fix what they report.
|
|
15
|
+
5. Run `nerd deploy` and give them the live URL. Explain in two lines that every deploy is checked for leaked secrets and vulnerable dependencies, and that they can run `nerd verify` any time.
|
|
16
|
+
6. Offer the next steps once, briefly: link the GitHub repo for previews on pull requests (`nerd github connect`), add a custom domain, or invite a teammate from the dashboard.
|
|
17
|
+
|
|
18
|
+
Throughout, show commands rather than describing them, and never ask the person to edit config files; the platform owns `wrangler.jsonc` and `nerd.json`.
|
|
19
|
+
|
|
20
|
+
## The loop
|
|
21
|
+
|
|
22
|
+
1. Make the change.
|
|
23
|
+
2. `nerd check` (static rules, then typecheck). Fix anything it reports.
|
|
24
|
+
3. `nerd dev` to run locally when you need to try it. The local app talks to the real auth service, so signup and login work exactly as in production.
|
|
25
|
+
4. `nerd deploy` when it is ready. It runs the checks, builds, applies pending migrations, uploads, and prints the URL.
|
|
26
|
+
5. If something is wrong after deploying: `nerd logs` shows recent output from the deployed app (`--q` to search, `--pr 12` for a preview), `nerd deployments` shows status and errors, `nerd db query "..."` inspects production data.
|
|
27
|
+
|
|
28
|
+
Run `nerd check --json` when you want a machine-readable list of problems.
|
|
29
|
+
|
|
30
|
+
## Deploying from GitHub
|
|
31
|
+
|
|
32
|
+
Once the repo is linked (`nerd github connect`, then `nerd github link owner/repo`), every push to the production branch builds and deploys automatically, and every pull request gets a preview at its own URL, posted as a PR comment. `nerd builds` lists builds; `nerd builds show <id>` prints the log when one fails. A failing build reports the same `nerd check` problems you would see locally, so fix and push again. Previews share the production database and do not run migrations; a PR that changes the schema will only work in full after it merges.
|
|
33
|
+
|
|
34
|
+
## Where things go
|
|
35
|
+
|
|
36
|
+
| Concern | Location |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| API routes | `src/server/index.ts` mounts routers under `/api/...`. Put each resource in `src/server/routes/<name>.ts`. |
|
|
39
|
+
| Database schema | `src/server/db/schema.ts` (Drizzle). Then `pnpm db:generate` to produce a migration in `migrations/`. |
|
|
40
|
+
| Queries | `db(c.env.DB)` from `src/server/db/index.ts`. Drizzle query builder, D1 dialect (SQLite). |
|
|
41
|
+
| Files | `c.env.STORAGE` is an R2 bucket. `put`, `get`, `delete`, `list`. Key files by user id. |
|
|
42
|
+
| Frontend | `src/client/` React, built by Vite. `src/client/api.ts` has a fetch wrapper that sends the session cookie. |
|
|
43
|
+
| Roles and signup policy | `nerd auth config`. Not in code. |
|
|
44
|
+
|
|
45
|
+
## Auth and authorization
|
|
46
|
+
|
|
47
|
+
The SDK handles sessions. There is no login code to write.
|
|
48
|
+
|
|
49
|
+
- `c.var.user` is `{ id, email, name, roles }` or `null` on every request.
|
|
50
|
+
- `requireAuth()` middleware returns 401 when signed out.
|
|
51
|
+
- `requireRole("admin")` returns 403 unless the user has one of the given roles. Multiple roles are OR.
|
|
52
|
+
- The frontend calls `/api/auth/signup`, `/api/auth/login`, `/api/auth/logout`, `/api/auth/me`, `/api/auth/forgot`, and `/api/auth/reset`. The template's `auth` helper in `src/client/api.ts` wraps them. Password reset emails link to `/reset-password?token=...`, which the template client renders; keep that route if you restructure the client.
|
|
53
|
+
- Roles are a list the platform owns. See the current list with `nerd auth config`. Add one with `nerd auth config --roles admin,member,editor`. The default role for new signups is set with `--default-role`. Turn public signup off with `--signup off`.
|
|
54
|
+
- To promote a user from code, use the admin client: `new AuthServiceClient(c.env).users.setRoles(userId, ["admin"])`. Guard that route with `requireRole("admin")`.
|
|
55
|
+
|
|
56
|
+
Example of a protected resource router:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { Hono } from "hono";
|
|
60
|
+
import { eq } from "drizzle-orm";
|
|
61
|
+
import { requireAuth, type NerdHonoEnv } from "@nerdsystem/sdk";
|
|
62
|
+
import { db, schema } from "../db";
|
|
63
|
+
|
|
64
|
+
const notes = new Hono<NerdHonoEnv>();
|
|
65
|
+
notes.use("*", requireAuth());
|
|
66
|
+
notes.get("/", async (c) => {
|
|
67
|
+
const rows = await db(c.env.DB).select().from(schema.notes).where(eq(schema.notes.userId, c.var.user!.id)).all();
|
|
68
|
+
return c.json({ notes: rows });
|
|
69
|
+
});
|
|
70
|
+
export default notes;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then in `src/server/index.ts`: `app.route("/api/notes", notes);`
|
|
74
|
+
|
|
75
|
+
## Data rules
|
|
76
|
+
|
|
77
|
+
- Every table holding user data has a `user_id` text column, and every read and write filters on `c.var.user.id`. This is how users are kept apart.
|
|
78
|
+
- Schema changes are migrations. Edit `schema.ts`, run `pnpm db:generate`, commit the new file in `migrations/`. Never hand-write SQL into production; `nerd deploy` applies migrations in name order and records which ran.
|
|
79
|
+
- Migrations are forward-only. To undo, write another migration.
|
|
80
|
+
- Prefer small, additive migrations. SQLite cannot alter columns in place.
|
|
81
|
+
|
|
82
|
+
## Errors
|
|
83
|
+
|
|
84
|
+
API errors are `{ error: { code, message } }` with a matching HTTP status. Use the same shape in your routes so the frontend's `ApiError` keeps working. Codes in use: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `internal`.
|
|
85
|
+
|
|
86
|
+
## Security gates
|
|
87
|
+
|
|
88
|
+
The `nerdsystem` MCP server (configured in `.mcp.json`) gives you two tools; prefer them over shelling out:
|
|
89
|
+
|
|
90
|
+
- `run_gates` runs every gate a deploy runs (secrets in source and in the built bundle, vulnerable dependencies, platform rules, static analysis, acceptable-use policy, personal-data heuristics, typecheck, and scanner suppressions) and returns `failures`. Each has `rule_id`, `file`, `line`, `risk` (what could happen), `fix` (what to change), `verify` (the command that confirms it), `attempt`, and `audience`.
|
|
91
|
+
- `explain_failure` takes a `rule_id` and returns the risk, the fix, and a wrong/right example.
|
|
92
|
+
|
|
93
|
+
The loop: run `run_gates`, fix every failure with `audience: "agent"`, run `run_gates` again (it re-runs every gate, not just the one that failed), repeat. Rules you cannot clear:
|
|
94
|
+
|
|
95
|
+
- `audience: "human"` means a person must act: a secret that was committed must be rotated at the provider before you remove it; an ignore comment, ignore file, or gate setting you added is a suppression that only a person can approve in the dashboard. Never add `nosemgrep`, `gitleaks:allow`, `nerd-ignore`, or a `gates` key in `nerd.json` to make a finding go away.
|
|
96
|
+
- When `stop` is non-empty, the same rule has failed three runs in a row. Stop, summarize what you tried in plain words, and hand it to the person.
|
|
97
|
+
|
|
98
|
+
Without MCP, `nerd verify --json` returns the same shape, and `nerd verify --rule <rule_id>` runs one rule. Secrets are reported masked (`sk_live_****4f2a`); never print a credential yourself. Keep secrets in `.dev.vars` locally and in platform settings in production; never prefix a secret with `VITE_`, because those values are compiled into the public client bundle.
|
|
99
|
+
|
|
100
|
+
## What `nerd check` rejects
|
|
101
|
+
|
|
102
|
+
- A root app that is not `createNerdApp()` from `@nerdsystem/sdk`.
|
|
103
|
+
- Any route outside `/api/`. Other paths serve the static client and never reach the Worker.
|
|
104
|
+
- New bindings in `wrangler.jsonc`. Only `DB`, `STORAGE`, and `ASSETS` exist. Durable Objects are not available.
|
|
105
|
+
- Scripts that call `wrangler deploy`, `wrangler secret`, or remote `wrangler d1` commands.
|
|
106
|
+
- Typecheck failures in either `src/server` or `src/client`.
|
|
107
|
+
|
|
108
|
+
## Scheduled jobs and email
|
|
109
|
+
|
|
110
|
+
- **Scheduled jobs**: put the work in the `POST /api/cron` handler (already in `src/server/index.ts`, guarded by `verifyCron()`), then turn the schedule on with `nerd cron hourly` or `nerd cron daily`. The platform calls it; the app never needs its own timer.
|
|
111
|
+
- **Secrets** (Stripe keys, API tokens): never in code or in the client. Locally they live in `.dev.vars` (gitignored). For production, ask the person to run `nerd secret set NAME` and paste the value; it reaches the app as `c.env.NAME` on the next deploy. `nerd secret list` shows names only. Do not put a real secret value in any command you run yourself.
|
|
112
|
+
- **Email**: `sendEmail(c.env, { to, subject, text, html? })` from `@nerdsystem/sdk` sends through the platform. It is limited per day by plan (50 on Free, 2,000 on Pro at $39 a month, 10,000 on Studio), so use it for transactional mail such as receipts and notifications, not bulk. Plans can be paid monthly or yearly at ten times the monthly price.
|
|
113
|
+
|
|
114
|
+
## Debugging production
|
|
115
|
+
|
|
116
|
+
When something is wrong on the deployed app: `nerd logs --since 1h` (add `--q "text"` to search, `--pr 12` for a preview) shows errors with the request that caused them. Reproduce locally with `nerd dev` using the same input, fix it, run `nerd verify`, and deploy or open a pull request. If a deploy made things worse, `nerd rollback` puts the previous production bundle back without touching the database. Write a one-line note on every deploy (`nerd deploy --note "fix: todos query filtered by user"`); the dashboard shows these as a changelog people can read.
|
|
117
|
+
|
|
118
|
+
## Not available yet
|
|
119
|
+
|
|
120
|
+
Queues, Durable Objects, and log streaming. If the task needs one, say so instead of working around it with an unsupported binding.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# This app runs on nerdsystem
|
|
2
|
+
|
|
3
|
+
nerdsystem deploys this app to Cloudflare and provides authentication and authorization. Work within these boundaries and `nerd check` will pass. The `nerdsystem` skill in `.claude/skills/` has the full workflow and code examples; load it for anything touching the server, database, auth, or deploys. `AGENTS.md`, `GEMINI.md`, `.cursor/rules/`, and `.github/copilot-instructions.md` are generated copies of it for other agents; edit the skill and run `nerd agents sync`.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `nerd dev` starts the app locally with a real session flow against the platform auth service.
|
|
8
|
+
- `nerd check` typechecks both sides and verifies the platform rules below. Run it before `nerd deploy`.
|
|
9
|
+
- `nerd deploy` builds, applies pending migrations to the production database, and ships the app. Prints the URL.
|
|
10
|
+
- `nerd db query "select ..."` runs SQL against the production database. `nerd deployments` lists recent deploys.
|
|
11
|
+
- With a linked GitHub repo, pushes to the production branch and pull requests deploy automatically. `nerd builds` and `nerd builds show <id>` show what happened.
|
|
12
|
+
- `pnpm db:generate` creates a migration after editing `src/server/db/schema.ts`. Never edit the database by hand.
|
|
13
|
+
|
|
14
|
+
## Layout
|
|
15
|
+
|
|
16
|
+
- `src/server/index.ts` is the Worker entry. Build the API here with Hono; mount routers under `/api/...`.
|
|
17
|
+
- `src/client/` is the React app, built by Vite and served as static assets for every path outside `/api`.
|
|
18
|
+
- `src/server/db/schema.ts` is the Drizzle schema for the app's D1 database (`c.env.DB`). Files go in `c.env.STORAGE` (R2).
|
|
19
|
+
|
|
20
|
+
## Rules the platform enforces
|
|
21
|
+
|
|
22
|
+
1. Keep `createNerdApp()` from `@nerdsystem/sdk` as the app's root. Do not replace it with a bare Hono instance.
|
|
23
|
+
2. Authentication is handled by the SDK. Do not write login, signup, password, or token code. Use `c.var.user`, `requireAuth()`, and `requireRole("admin")`.
|
|
24
|
+
3. Every table that stores user data carries a `user_id` column, and every query filters by `c.var.user.id`. Users must never see each other's rows.
|
|
25
|
+
4. Database changes go through Drizzle migrations in `migrations/`. `nerd deploy` applies them.
|
|
26
|
+
5. Keep every API route under `/api/`. Other paths are served from static assets and never reach the Worker.
|
|
27
|
+
6. Do not add Cloudflare bindings to `wrangler.jsonc`, and do not run `wrangler deploy`. Bindings are provisioned by the platform.
|
|
28
|
+
7. Secrets and environment variables for the deployed app are managed with `nerd` commands, never committed.
|
|
29
|
+
|
|
30
|
+
## Roles
|
|
31
|
+
|
|
32
|
+
The platform owns the list of roles and which users hold them. The default roles are `admin` and `member`; new signups get `member`. Change the list with `nerd auth config --roles admin,member,editor`. In code, gate with `requireRole("admin")` or check `c.var.user.roles`.
|