mason/cheffryjames-www
Public: anyone can read this; nobody can change it.
The site that explains Cheffry's Dungeon, the game at game.cheffryjames.com
forked from cheffryjames/www
This repository has no app yet.
following cheffryjames/www in step
Everything here is up to date with cheffryjames/www.
Proposes updates from cheffryjames/www.
- contract
- design
- scripts
- src
- static
- .gitignore 37 B · ~11 tok
- AGENTS.md 7.6 KB · ~1.9k tok
- contract.json 756 B · ~252 tok
- forge.json 137 B · ~46 tok
- package.json 368 B · ~123 tok
- README.md 7.6 KB · ~1.9k tok
README.md
www
The site that explains Cheffry's Dungeon, the game at game.cheffryjames.com. It is served at www.cheffryjames.com.
Two pages, server-rendered, no script, nothing from any other site:
/— what the game is, what exists in it today, and Play./how-to-play— getting in, the controls, a run, the enemies, the
chests, the score and the daily board.
Every fact the pages state about the game lives in src/game.ts, read
from the game's own repository (cheffryjames/cheffry-s-dungeon on this
forge): its content/*.json for names and numbers, src/sim.ts for the
rules of a run, its pages and scripts for the controls. Nothing here is
simulated or promised; when the game changes, change src/game.ts.
The branding is a placeholder: the name, tagline and wordmark are
src/brand.ts, the colours design/tokens.json. Replace those two and the
site is rebranded.
The template's notes, its API, MCP server and terminal are still here,
unchanged and off the pages: they carry the contract (contract.json)
the forge audits.
This is an app: the forge runs it. Production follows the default branch;
every open pull gets a preview at its own address with its own copy of the
data. Everything the app keeps lives under FORGE_APP_DATA, which is what
makes "production data" and "preview data" two different things.
PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts # run it here
bun test # what `forge.json`'s app.test runs on every push
FORGE_APP_DATA=./data bun run src/tty.ts # the terminal
FORGE_APP_DATA=./data bun run src/db.ts --check # schema version, integrity
bun run lint:design # no colour but the tokens'
bun run contract:emit # rewrite contract/openapi.json from the routes
app.test in forge.json runs beside app.run in every deploy, not before
it: the preview comes up the moment the app answers, and says live, or
serving · tests failed the moment the suite disagrees.
The shape
One implementation, many doors. src/actions.ts is every operation as a
plain function — (db, who, input) in, data or a named failure out — and
the page, the JSON API, the MCP server and the terminal all call it and
decide nothing themselves. AGENTS.md says how to add to it.
| file | what it is |
|---|---|
src/pages/ | the site: layout, home, how-to-play, rendered on the server |
src/game.ts | what the game is, as data: every fact the pages state |
src/brand.ts | the placeholder name, tagline and wordmark |
src/actions.ts | list, add, delete; the audit trail, read |
contract.json | who may do what: roles, and the least role each verb needs |
src/contract.ts, src/can.ts | the table, read at boot; the forge's evaluator, verbatim |
src/security.ts | the door: rationing, same origin, the table before the body |
src/audit.ts | every change and every refused one, as a row |
src/who.ts | who is asking, from the forge's signed headers |
src/db.ts | SQLite under FORGE_APP_DATA, WAL, forward-only migrations |
src/live.ts | the in-process bus and GET /events |
src/mcp.ts | the actions as MCP tools at POST /mcp |
src/routes.ts | every path, in one table, each API route described beside it |
src/openapi.ts | the routes as OpenAPI; committed at contract/openapi.json |
design/tokens.json, src/tokens.ts | the look as data, light and dark; compiled to /tokens.css |
src/http.ts | the floor: headers, body cap, files |
src/server.ts | boot: open the database, serve |
src/tty.ts | the terminal, over the same actions |
src/env.ts | what the forge told this process |
static/app.css | the look in the tokens' words |
What every app stands on
- The door. Before a route sees a request,
src/security.tsrations
writes (and a stranger's API calls), refuses a write from another site, and turns away — before the body is read — anyone the table lets through a resource's door for no row at all: 401 for a stranger, 403 for anyone else. The action still decides about the real row.
- The audit trail. Every change, and every change refused, is a row:
who, which resource and verb, which row, when, and whether they were
let. Admins read it at GET /api/audit. A row that cannot be written
is a log line, never a failed request.
- The API, described.
GET /openapi.jsoniscontract/openapi.json,
written from the routes by bun run contract:emit; each operation names
its cell of the table (x-contract). bun test fails while it is stale.
- Up, or not.
GET /healthzanswers{ ok, version, uptime }; the
forge waits on it before it sends anyone to a deploy.
- The look.
design/tokens.jsonholds every colour, space and size,
light and dark; nothing else spells out a colour (bun run lint:design),
and every pair a person reads meets 4.5:1 in both themes.
- The open web. A shared link unfurls (description, Open Graph, a
card); /robots.txt keeps crawlers to the page, and out of previews
entirely; /.well-known/security.txt sends a reporter to the forge
that runs the app.
Who you are
The forge says who is asking on every request it proxies, and the app
verifies the signature (src/who.ts); nothing else is trusted. Three tiers:
- anon — anyone. Reads.
- user — signed in to the forge and able to see this app. Adds notes, as
themselves, and removes their own.
- admin — able to write this repository. Removes any note.
"Sign in" is /__forge/signin?to=<path> and "Sign out" is
/__forge/signout; the forge answers both on the app's own host.
Any interface
The same actions, the same rules, at every door. A script is the person whose forge grant it carries; a bare script is anonymous.
curl https://<app>/api/notes
curl -X POST https://<app>/api/notes -H 'content-type: application/json' -d '{"body":"hi"}'
curl -X DELETE https://<app>/api/notes/1
As an MCP server, for Claude Code or Claude Desktop — list_notes,
add_note, delete_note:
claude mcp add --transport http www https://<app>/mcp
{
"mcpServers": { "www": { "type": "http", "url": "https://<app>/mcp" } }
}
A client that wants notes as they change listens on /events.
The terminal opens with the exact command in FORGE_APP_SSH when the app
runs (ssh -p 2222 <name>@<forge> — the app is the login name, the forge
the host; <name>--<pull>@<forge> for a preview's). It is src/tty.ts on
a real pty over the same data, as the account at the keyboard.
The forge sets PORT, FORGE_APP_DATA, FORGE_APP_ENV (production or
preview), FORGE_APP_URL, FORGE_APP_COMMIT, FORGE_APP_SSH and
FORGE_APP_IDENTITY_KEY when it starts the app. forge.json says how.