quest'n'click 4.5.0 · r1

Built for quest'n'click 4.5.0, revision r1. Whether it is published, and which one is current, is recorded at publication.json.

Quick start

Français · Type: tutorial · Audience: author, admin · Context: Reality Bridge · Status: ready · Applies from: 4.2.5

The Reality Bridge, local profile (SQLite) · contract: stable · verified by CI (support matrix of 4.4.0, 2026-10-23): Node 22.13+ with node:sqlite, on Ubuntu; Chromium and WebKit end to end · automated checks: jobs reality, reality-xcheck; CI plan reality; 2 test files · people's passes: none · known limits: bridge-quick-start-local

In about fifteen minutes, on your own machine, you make the sample game signals react to the world outside: first with the Studio's simulated Bridge, then with a real Bridge on 127.0.0.1, a signal sent through its demonstration webhook, and the same signal sent twice. Nothing leaves your machine.

What you will build

The sample game games/signals opens with a garden, a closed gate and an old radio on a bench. At the end of this tutorial:

What you need

Try it in the Studio first

  1. Start the Studio on the sample game:

    # in the engine repository
    GAME=signals npm run studio
    

    The Play tab shows a Reality panel, because the game declares reality: a button per signal, faults to add (a delay, a duplicate, a bad signature, an expiry), the queue reversed, the connection cut and resumed, and what happened to each delivery. Press the button of mail.answer.correct: the gate opens.

    It is a simulated Bridge with a development key, running in the page: the signal goes through the same verification, session, save and deduplication as a real one. Stop the Studio (Ctrl-C) before going on.

Steps with a real Bridge

  1. Create the Bridge's keys and configuration for the game:

    # in the engine repository
    GAME=signals npm run bridge -- init
    

    You see ✔ …/.cache/bridge/config.json (keys, webhook secrets and tokens; mode 600), …/root.key (the root key: grant only) and …/admin-token, then the list of demonstration webhooks, one per source of the game's signals: /v1/hooks/badge, /v1/hooks/webhook, /v1/hooks/email, /v1/hooks/mail, /v1/hooks/terminal. The defaults suit this tutorial: the game's origins http://127.0.0.1:5173 and http://localhost:5173, the journal as the store.

  1. Start it, in a first terminal:

    # in the engine repository, terminal 1
    GAME=signals npm run bridge -- serve
    

    It prints one JSON line, {"event":"bridge.listening","url":"http://127.0.0.1:8787/","game":"signals",…}, and keeps running. Every later event (a pairing, a signal) is one more line here.

  1. Start the game, in a second terminal, and open http://127.0.0.1:5173/:

    # in the engine repository, terminal 2
    GAME=signals npm run dev
    

    The game's reality.bridge is already http://127.0.0.1:8787/ (games/signals/game.ts).

  1. Link the game. Open the pause menu, row World link, and choose "Link this game": the game shows an 8-character code (letters and digits, without 0, O, 1, I or L). It waits for a connector to confirm it.
  1. Confirm the code as a connector would, in a third terminal. The demonstration webhook of the source mail has a token that may confirm pairings; replace ABCD2345 with your code:

    # in the engine repository, terminal 3
    TOKEN=$(node -p 'require("./.cache/bridge/config.json").webhooks.mail.token')
    curl -s -X POST http://127.0.0.1:8787/v1/pairings/ABCD2345/confirm -H "Authorization: Bearer $TOKEN"
    

    The answer is {"playerId":"p-…"}: 16 hexadecimal digits, a pseudonymous id the Bridge made up. Terminal 1 logs "event":"pairing.confirmed". The game asks the Bridge every two seconds whether its code was confirmed: it collects its link by itself and says it is linked. Note the playerId for the next step.

  1. Send the signal through the demonstration webhook. A webhook request is signed with an HMAC-SHA256 of its body under the webhook's secret; this one-liner reads the secret from the configuration and signs. Replace the p-…:

    # in the engine repository, terminal 3
    node -e '
    const { createHmac } = require("node:crypto");
    const [playerId] = process.argv.slice(1);
    const { secret } = require("./.cache/bridge/config.json").webhooks.mail;
    const body = JSON.stringify({ playerId, event: "answer.correct", id: "try-1" });
    const signature = "sha256=" + createHmac("sha256", secret).update(body).digest("hex");
    fetch("http://127.0.0.1:8787/v1/hooks/mail", { method: "POST", body,
      headers: { "Content-Type": "application/json", "X-Web-Scumm-Signature": signature } })
      .then(async (r) => console.log(r.status, await r.text()));
    ' p-0123456789abcdef
    

    It prints 202 {"id":"s-…","sequence":1,"duplicate":false}. In the game, the gate opens. The event answer.correct of the webhook mail is the signal mail.answer.correct; id names this delivery.

  1. Run the same command again. It prints 200 {"id":"s-…","sequence":1,"duplicate":true}: the same id, the same sequence. The Bridge kept the first one; the game changes nothing.
  1. Stop the Bridge and the game with Ctrl-C in their terminals. The Bridge says "event":"bridge.stopping" and exits.

Limit · bridge-quick-start-local This tutorial runs on one machine, over plain HTTP, with demonstration webhooks that may send any signal of their source for any player for 30 days. That is for development only: on the Internet the Bridge runs behind HTTPS, with init --no-demo-webhooks and one narrow grant per connector (Install and run the Bridge).

In the player's hands

What you just did by hand is what a connector does for a player. On the player's side:

What the save keeps, and what happens when a save meets another player's link: Guarantees.

Going further: the sample chapter

games/signals has a chapter, "the mailbox and the terminal", played with the real connectors. Link the game (pause menu, World link), then:

None of it is needed to finish the game: the radio on the bench still opens the gate. The Telnet terminal is the quickest to try locally. With the Bridge of step 2 running:

# in the engine repository, terminal 3
GAME=signals npm run bridge -- grant --connector=shed-telnet --source=terminal --signals=terminal.lamp --pair > .cache/bridge/telnet.token
cat > .cache/bridge/telnet.json <<'EOF'
{
  "bridge": { "url": "http://127.0.0.1:8787/", "tokenFile": "telnet.token" },
  "gameId": "signals",
  "health": { "port": 9301 },
  "telnet": { "port": 2323 }
}
EOF
npm run connector -- telnet --config .cache/bridge/telnet.json

Then, from a fourth terminal, nc 127.0.0.1 2323 (or telnet 127.0.0.1 2323): the shed's banner asks for the pairing code your game shows; type it, then lamp on. The terminal answers "Click. Somewhere in the garden, a lamp glows." Each connector is described on its own page: Telnet, SSH, email, Open Badges.

What you learned

Where to go next


Edit the source of this page · Report an error