# Chess-IO
Real-time chess over socket.io. My fork of
[Reti-Chess](https://github.com/romanmatiasko/reti-chess).
Built with [Node.js](https://nodejs.org/) | [Express](https://expressjs.com/) |
[Socket.IO](https://socket.io/) | [Pug](https://pugjs.org/) |
[chess.js](https://github.com/jhlywa/chess.js)
Games live in memory only: reload or disconnect and the game is gone. There is
no database and no accounts — the game link is the only thing gating a game.
## Run it
```sh
npm install
cp .env.example .env
npm run dev # or: npm start
```
`npm run dev` loads `.env` and restarts on change. With `BASE_PATH=/chess` the
app serves at .
## Configuration
Everything comes from the environment — see `.env.example`. This replaces the
old `ENV` switch in `public/javascripts/app.js`, which hardcoded a host and port
into the client and had to be hand-edited per deploy.
| Var | Default | Notes |
| --- | --- | --- |
| `PORT` | `8181` | Internal; Apache fronts it in production. |
| `BASE_PATH` | *(empty)* | `/chess` to mount at a subpath. Empty = domain root. |
| `TRUST_PROXY` | *(empty)* | `1` behind one reverse proxy. |
| `LOG_FILE` | `logs/games.log` | What `GET /logs` serves. |
| `DAW_ORIGIN` | *(empty)* | **Local dev only.** See "Shared chrome". |
`BASE_PATH` drives three things at once: the Express mount, the socket.io path
(`${BASE_PATH}/socket.io`), and every asset URL in the templates. The client
reads it back from `window.$BASE`, which `views/layout.pug` emits. Nothing is
baked in at build time, so the same tree serves `/` or `/chess` unchanged.
## Mounted at /chess
The app sits behind Apache at `davidwindham.com/chess`, same shape as `/radio`:
```apache
ProxyPass /chess http://127.0.0.1:8181/chess upgrade=websocket
ProxyPassReverse /chess http://127.0.0.1:8181/chess
```
The prefix is kept on **both** sides — `BASE_PATH=/chess` tells the app to expect
it. `upgrade=websocket` is what carries socket.io; it needs httpd 2.4.47+ and no
`mod_proxy_wstunnel`.
Being same-origin with the main site is the point: the client calls `io()` with
no host, so it follows http/https on its own. The old build dialled
`ws://chess.davidawindham.com:8181` explicitly, which a TLS page blocks as mixed
content.
## Shared chrome
`views/layout.pug` renders `` / `` and loads
`/embed/chrome.js` root-relative. In production Apache serves `/embed` from the
WordPress docroot alongside `/chess`, so it just works.
Locally, hitting node directly on `:8181` has no Apache in front and the chrome
would 404 — set `DAW_ORIGIN=http://daw.stu` and the app proxies `/embed` to the
main site. **Leave `DAW_ORIGIN` unset in production.** Going through
`daw.stu/chess` doesn't need it either way.
`public/stylesheets/daw.css` is the integration layer that pulls the page onto
the site's slate/card palette. It is deliberately separate from `style.css` so
the migration delta stays isolated — see the comment at the top of that file.
## Routes
All relative to `BASE_PATH`.
| Route | Notes |
| --- | --- |
| `/` | Create a game, get a link. |
| `/about` | |
| `/play/:token/:time/:increment` | The board. Params are validated; anything malformed is a 404. |
| `/logs` | Serves `games.log`. **Unauthenticated** — see below. |
## Gotchas
- **`style.css` is edited directly — there is no sass build.** A `style.scss`
used to sit alongside it but had drifted years out of sync (different button,
different palette) with no build step wiring the two, so it was deleted rather
than left as a trap. `daw.css` holds the daw-integration styles separately.
- **`/logs` is public.** Nothing gates it. `gameLogger` is its only writer and it
records a running game count and nothing else — no addresses, no chat, no
player identifiers. Keep it that way, or put the route behind auth. Operational
logging goes to the console logger precisely so it stays out of that file.
- **Tokens are the only access control.** They're 15 CSPRNG bytes as base64url;
`TOKEN_RE` in `server.js` pins the shape the `/play` route will accept.
- **Node 24.** Runs on the same 24.x as the server. The pre-migration tree
(Express 4 / socket.io 2 / jade) also booted on 24 — the upgrade was for
support and security, not because 24 forced it.
## History
- 2015 — original fork of Reti-Chess.
- 2020 — reworked toward Express 5.
- May 2022 — merged `_old` back into `master`, still on the old fork.
- July 2026 — modernized (Express 5 / socket.io 4 / Pug) and mounted under
`davidwindham.com/chess` with the shared site chrome.