fable/val Public Public: anyone can read this; nobody can change it.
forked from benjivers/val
workbenchthis machine
Files bccb5ce2e2d8891f0fe7aca1ea6d5844cb9b5022
| 1 | # Working on this app |
| 2 | |
| 3 | You are one of the crew on this repository. The forge runs the app for you: |
| 4 | production follows the default branch, and every pull you open gets a |
| 5 | preview at its own address, deployed from your branch as you push. |
| 6 | |
| 7 | ## The rules |
| 8 | |
| 9 | - Work on the branch you were given. You cannot move the default branch; |
| 10 | a person merges your pull once it is reviewed. |
| 11 | - Keep state under `FORGE_APP_DATA`. Never write elsewhere at runtime. |
| 12 | - Run `bun test` before you push. It is the check the pull shows, and every |
| 13 | behaviour you add gets a test that would fail without it. |
| 14 | - `src/tty.ts` is the app's terminal interface. The exact command that |
| 15 | reaches it is `FORGE_APP_SSH` at runtime; print that verbatim wherever the |
| 16 | app tells people how to connect. Keep it working and keep it useful: what |
| 17 | a person can do on the web page they should be able to do here too. |
| 18 | - Keep it dependency-free unless a dependency is clearly worth it, and say |
| 19 | why in the pull. A dependency is code you now maintain. |
| 20 | - When you are done, push and stop. Your last message becomes the pull's |
| 21 | description: say what changed, what you chose not to do, and what to look |
| 22 | at in the preview. |
| 23 | |
| 24 | ## The bar |
| 25 | |
| 26 | - Shape: small modules named after one concept, plain data in and out. When |
| 27 | `src/server.ts` stops fitting in one read, split it — `src/db.ts` for |
| 28 | the schema and queries, `src/pages/<page>.ts` for rendering, |
| 29 | `src/routes.ts` for the table of paths — and keep the pattern you find. |
| 30 | - Security: every input is validated at the boundary and every output is |
| 31 | escaped; every response carries the security floor in `src/server.ts` |
| 32 | (keep it, extend it); a POST that is not same-origin is refused; secrets |
| 33 | come from the environment, never the repository; no `eval`, no shell |
| 34 | built from user text; a private thing needs a session in an HttpOnly cookie. |
| 35 | Every request body is capped by the bytes actually read (`readBody` in |
| 36 | `src/server.ts`) — never by a Content-Length, which a chunked request does |
| 37 | not have and a lie does not honour. `HEAD` answers like `GET`. |
| 38 | An app with an owner or admin is never claimed by whoever visits first: |
| 39 | the first sign-in needs a secret only the operator holds — a password from |
| 40 | the environment, or a one-time token printed to the run log and the app's |
| 41 | terminal — and a generated password is shown there once, never on a page. |
| 42 | - Secrets come from the environment: the app's Settings → Environment |
| 43 | (write-only), read at start-up; never the repository. |
| 44 | - Speed: the first paint is server-rendered HTML; JavaScript is small, in |
| 45 | files under `static/` named by content and cached for a year; no layout |
| 46 | shift; the common request answers in milliseconds; work that can wait is |
| 47 | done after the response. |
| 48 | - A phone is a first-class client: the page works at 360px wide with no |
| 49 | horizontal scroll, every control is at least 44px on a side, and the |
| 50 | keyboard reaches everything a mouse does. |
| 51 | - Finish: the app boots, `/` answers, the README says how to use it and |
| 52 | what is real versus simulated, `forge.json` says how to run it. |