Chapter 11 of 13
Testing locally
No game runs offline: put your work behind a tunnel, play it in the Inspector's Test Room in any browser, and keep your tests offline.
The project create-camtable-addon makes (section 2: Quick start) is a complete reference: by default its game
runs in host mode, its rules a reducer that the room host’s page runs with createHostAuthority, and
with --engine the same reducer runs in engine mode, on a Worker. CamTable’s official games are built
the same way.
There is no offline way to try a game. A game runs in a CamTable room, a room runs on CamTable’s
server, and the server and the CamTable website reach an Addon only at an https address. So you
put your work in progress behind a tunnel and try it in the Inspector, or in a real Discord call.
What runs offline is your tests (below).
1. The Inspector, in any browser. The Inspector is a page of the CamTable website,
https://camtable.app/inspector. Sign in with Discord, paste your Addon’s https manifest
address (or open a link that carries it as ?url=), pick a game and a Play Option, and open a
Test Room. It accepts any Addon’s manifest, however it was built, and needs no extension.
- A Test Room is a real room on CamTable’s server, in both authority modes: the Lobby and Match Settings, the room’s limits, the Addon Blocklist and the action pipeline behave as in any room. It needs no paid Plan, is never listed, admits only you, and closes 30 minutes after it opens. You can have one open at a time.
- You play every seat. The Inspector shows one game frame per seat, each with its own room
connection, its own Match Player Id and a placeholder Video Tile, and each framed with the same
sandbox the extension uses. The first seat is the room’s Host: in host mode its page decides every
seat’s intents through
createHostAuthority, as in a real room. - In engine mode the room calls your engine with CamTable’s real signature and its engine limits,
and each request carries
"test": true(section 8: Host mode and engine mode, “Test matches”). - The Inspector reports a manifest it cannot fetch, a field that fails the protocol’s schema, and an Addon on the blocklist, each with its own message.
- The deployed Inspector accepts only
httpsmanifest addresses. A development build of the CamTable website also accepts a plain-httploopback address.
A game page opened directly in a browser tab, outside any room, says that it runs inside a CamTable
room and links to the Inspector (NotInRoomError, section 6: The game page and the SDK).
2. pnpm dev in a scaffolded project. It starts the addon’s local server (the Vite server in
host mode, the single Worker in engine mode), opens a temporary cloudflared
quick tunnel to it (no account needed), and prints three addresses:
Manifest URL https://<random-words>.trycloudflare.com/manifest.json
Inspector https://camtable.app/inspector?url=<manifest url>
Install link https://camtable.app/install?url=<manifest url>
Open the Inspector link to play, or the Install Link in a browser with the CamTable extension to
play in a real Discord call. The manifest is built for the tunnel’s address, so uiUrl and
engine.url never say localhost, and in engine mode the Worker verifies signatures against the
deployed CamTable server’s keys, because that server is the caller (a Test Room is one of its
rooms). The address changes on every run. Deploy (section 9: Hosting) when you are done.
cloudflared is recommended, not required: its quick tunnel needs no account, is free, and puts no page
of its own in front of your game page. Install it with brew install cloudflared,
winget install --id Cloudflare.cloudflared, or Cloudflare’s apt repository; pnpm dev says so if
it is missing.
Use your own tunnel. With another tunnel (ngrok, Tailscale Funnel, …), open it to a local port
yourself and hand its https address to pnpm dev in ADDON_ORIGIN:
PORT=4000 ADDON_ORIGIN=https://<your tunnel's address> pnpm dev. It then starts no cloudflared;
it starts the local server on that port for that address (the template’s Vite server allows the
Host of ADDON_ORIGIN), checks that the manifest answers through it and prints the same links. The
scaffold starts no tunnel other than cloudflared. ngrok’s free plan shows browsers a warning page
before your site, and in a room that page can take the place of your framed game page, so the game
never appears.
Against a CamTable on your machine (CamTable’s own development): CAMTABLE_SITE points the links at
another website, such as http://localhost:5190, and CAMTABLE_SERVER names the CamTable server
whose rooms call your engine, such as http://localhost:8787, so the Worker checks signatures with
that server’s published keys. With a ready loopback address in ADDON_ORIGIN, no tunnel is needed:
the local server and a development build of the website both accept it.
pnpm test in the template covers what should never regress, with no network: every route validates
against the protocol; the reducer’s replies (it is a pure function, so test it by calling it); a whole
match played through the rules; and, in engine mode, that the Worker refuses an unsigned request and
answers a correctly signed one. Extend tests/game.test.ts with your own rules.