Repository navigation
EN Bridge Connector SSH
Français · Type: how-to + reference · Audience: admin · Context: Reality Bridge · Status: ready · Applies from: 4.2.5
The connectors (email, Telnet, SSH, Open Badges) · contract: experimental · verified by CI (support matrix of 4.4.0, 2026-10-23): Node 22 on Ubuntu (Telnet and SSH not on Windows) · automated checks: jobs
connectors,connector-archive-e2e; CI planreality; 3 test files · people's passes:connectors-real-securitynot run (#79) · known limits: none listed on this page
The SSH connector opens a virtual terminal over SSH: a player logs in with the pairing code their game shows as the
password (or with a public key the operator declared for them), then types the commands the game declares, and reads
the files of the game's virtual disk with ls, cd and cat. Nothing typed is ever run: there is no shell behind
it. It is experimental, like every connector (Connectors overview).
A quest-n-click-connector ssh process listening on a port with its own host key, answering the commands of
reality.connectors.ssh and showing its files, and proposing the commands' signals for the player who logged in.
Unlike Telnet, what is typed is encrypted on the way.
- A connector beside a running Bridge, from 4.1.9; this page applies from 4.2.5 (the
quest-n-click-connectorname). -
ssh2(pure JavaScript, MIT) carries the protocol; its optional native parts are refused (connectors/vendor/). -
Verified on: not replayed for this page. CI plays it from the release archive (
npm run connector-archive-e2e: the pairing code as password, the commands, a file of the virtual disk) and under floods intests/connectors-ssh.test.ts; no person has yet run it on a network.
-
A running Bridge (Install and run the Bridge) and a game whose
reality.connectors.sshdeclares its commands and files (Signals and the manifest):ssh: { prompt: 'shed$ ', commands: [{ says: 'lamp on', reply: 'Click.', signal: 'terminal.lamp' }], files: { '/notes/lamp.txt': 'The lamp obeys two words: lamp on.' }, }
-
ssh-keygen, to make the connector's host key.
grant where the Bridge's folder is; the connector on the machine players connect to, in a folder holding its
configuration, token and host key (mode 600). npm run connector -- ssh … in the engine repository,
npx quest-n-click-connector ssh … with the package.
-
Grant the connector its token, with pairing:
# in the engine repository (the Bridge's folder is .cache/bridge) npm run bridge -- grant --connector=shed-ssh --source=terminal --signals=terminal.lamp --pair > ssh.token
-
Make the connector's host key:
# in the connector's folder ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N ''
-
Write its configuration,
ssh.json.keysis optional: a public key per player, for players who would rather not type a code:{ "bridge": { "url": "http://127.0.0.1:8787/", "tokenFile": "ssh.token" }, "gameId": "signals", "health": { "port": 9303 }, "ssh": { "port": 2222, "hostKeyFile": "ssh_host_ed25519_key", "keys": [{ "key": "ssh-ed25519 AAAA…", "playerId": "p-0123456789abcdef" }] } }
-
Start it:
# in the engine repository npm run connector -- ssh --config ssh.json
-
Log in as a player would; the user name does not matter, the password is the code the game shows:
# on a machine that reaches the port ssh -p 2222 player@127.0.0.1After the banner,
lamp onprints the command's reply and proposesterminal.lamp;ls /notesandcat /notes/lamp.txtread the virtual disk.
-
GET http://127.0.0.1:9303/healthanswers 200;GET /metricscounts proposals and refusals. - The Bridge logs
"event":"pairing.confirmed"(for a password login with a code), then"event":"signal.accepted".
| Symptom | Cause | Fix |
|---|---|---|
ssh: "hostKeyFile" names the host key at start |
No host key in the configuration | Step 2, then hostKeyFile
|
Permission denied, then the connection closes |
A wrong or expired code, a resume word past its 30 minutes, or an undeclared key, three times | A new code from the game |
| The connection is refused at once | Six wrong passwords from that address in ten minutes, across reconnections; 3 connections already open from it, or 20 in all | Wait |
A command given on the ssh line, sftp, scp or a forwarding is refused |
Only an interactive terminal opens | Log in without a command |
| The connection closes | Not authenticated within 20 seconds; authenticated without opening a shell for a minute | Open the terminal at once |
None on disk. Resume words live in memory for 30 minutes; nothing typed is stored.
Stop the process (SIGTERM). A player's declared key is removed from ssh.keys, then the connector restarted. If the
connector's token leaked, revoke it on the Bridge and grant a new one; if its host key leaked, make a new one (players'
SSH clients will warn that the host key changed).
-
ssh: { "port", "host" (127.0.0.1), "hostKeyFile", "keys"?, "limits"? }. - The password is a pairing code or a resume word (three tries per connection); or a public key the operator declared
for a player (
keys). - Only a terminal opens: the game's commands,
help,clear,exit, andls,cd,pwd,caton the game's virtual disk (files).exec,sftp(any subsystem), environment, agent, X11 and forwarding are refused. One session with one shell per connection. - Paths never leave the virtual tree, nothing typed is interpreted. The output wraps at the terminal's width (10 to 500 columns, 80 by default).
- The same limits as Telnet (a line of 512 bytes, 100 lines a minute, 64 KB a second received, 20 seconds to log in,
a minute between lines, 30 minutes a session, 3 connections per address and 20 in all, 6 wrong passwords per address
in 10 minutes), each overridable in
limitsby its name (Telnet). - Each line typed is a new fact: its key is
sha256('ssh:' + session + ':' + line number).
Guarantee ·
bridge-ssh-terminal-only· referenced · scope self-checked by the author (AI), not reviewed The SSH connector refusesexec,sftpand forwarding; only its virtual terminal opens.
Nothing typed is stored: a line is matched against the game's commands and forgotten; the Bridge receives only the signal's name. A resume word links a later session to the same player for 30 minutes, in memory. The public keys an operator declares for players live in the connector's configuration until the operator removes them. The client's address is used for the connection and counted in memory for the limits; the connector keeps no list of them.
- The code:
connectors/src/ssh/connector.ts,connectors/src/terminal/. - The threat model:
docs/dev/threat-models/ssh.md. - Tested by
tests/connectors-ssh.test.ts.
Generated publication · publication générée — current documentation · documentation courante: quest'n'click 4.4.0 r1, documentation revision · révision de la documentation 8dc717c69acb62c4742b69c54a7e7dfb620f75d8.
Earlier versions and permalinks · versions précédentes et liens permanents: https://wanoo.github.io/quest-n-click/docs/
An edit made in this wiki is overwritten at the next publication · une modification faite dans ce wiki est écrasée à la publication suivante.
Edit the source · Modifier la source · Report an error · Signaler une erreur
Index (6)
Start (6)
Play (9)
Install (17)
- Requirements
- Install from the release archive
- Install from npm, once published
- Install from the repository to work on the engine
- doctor: checking your machine
- Image toolchain
- Audio toolchain: ffmpeg, fluidsynth, Furnace, uv
- Upgrade
- Upgrade notes: 4.4
- Upgrade notes: 4.3
- Upgrade notes: 4.2
- Upgrade notes: 4.1
- Upgrade notes: v3
- Upgrade notes: v2 to v3
- Coming from web-scumm
- Package reference
- Install troubleshooting
Create a game (55)
- Your first game
- Layout of a created project and of the engine repository
- The working loop
- Content principles and the identity contract
- Rooms
- Geometry: placement, walk zones, masks, layers, camera
- Something is visible but unreachable
- Reactions
- Game commands
- Conditions
- Flags, variables and events: which to use
- Custom code
- Characters
- Items and inventory
- World map
- Dialogues
- Scripts and events
- Moving characters: patrols and walks
- Exits
- Chapters and objectives
- State and saves: persistent or temporary, renaming an id, migrations
- Staging: wide rooms and animations
- The stage: lights, particles, floors
- Skin and fonts
- Title and credits
- Heroes: several characters and hero choice
- Built-in minigames
- A custom minigame
- Translations
- Music in stems
- Sound effects
- Voices: per language and per hero
- Images: prompts, cutting sheets, transparency, scale
- Asset provenance
- The storyboard, source of truth
- Puzzle design
- Classic mechanics
- Review pages
- DSL reference
- Checking your game
- What the solver proves
- Solver options
- Playtests
- Remix
- Speedrun for authors
- The sealed ending
- Production template
- Example games
- Working with an AI
- Build and release a game
- Publish on GitHub Pages
- The static game on Clever Cloud
- Other static hosts
- Sub-path, cache and service worker, incomplete deploys, going back
- Testing on a phone over the LAN
Local Studio (24)
- The local Studio: overview and launch
- Edit a room
- Placement and stage
- Preview, apply, undo
- Storyboard
- Assets and cutting
- Check: validator, solver, critical path, heat, health, coverage
- Before and after captures
- Semantic diff
- Play and replay a session
- Rule explainer
- Language and objectives
- Voices and the heroes × languages matrix
- Map
- Notes
- Remix in the Studio
- Music
- The Assistant: bring your own key
- Demo mode and studio-apply
- Local and LAN security
- Studio API
- Local MCP: setting up each client
- MCP tools
- Studio troubleshooting
Team Studio (21)
- What runs where
- Concepts: revision, transaction, span, lease, preview, build
- Starting as an invited dev
- Working together day to day
- Roles and actions: the full matrix
- Invitations
- Managing members
- Write refused on the hosted Studio: stale base revision (409)
- Write refused on the hosted Studio: span locked (423)
- Recovering after a lost lease, a disconnection or a stale preview
- Comments
- Map and semantic diff
- Publishing builds
- Remote MCP
- The game in Git
- Upgrading the engine of a hosted game
- Hosted assets
- The server's command line
- Health and metrics routes
- Trying it locally with compose.yml
- Known limits
Clever Cloud (23)
- Clever Cloud: overview, cost and prerequisites
- Apps and add-ons
- Domains and TLS
- Keycloak
- Generic OIDC and GitLab
- Configuration
- First deployment
- Backups
- RPO and RTO
- Restore the Studio on a fresh installation
- Redeploys
- Upgrading the server and checking state after a migration
- Limits of going back
- Monitoring
- Security
- Rotating keys
- Recovering admin access
- Changing domain or OIDC provider
- Worker, PostgreSQL or Cellar down
- Exporting the game and leaving the installation
- Retention
- Clever Cloud troubleshooting
- Costs and teardown
Reality Bridge and connectors (16)
- The Reality Bridge: what it is
- Quick start
- Guarantees
- Install and run the Bridge
- Backup and restore
- Several tenants
- Signals and the manifest
- The Bridge's command line
- Connectors overview
- Email connector
- Telnet connector
- SSH connector
- Open Badges connector
- The connector SDK
- Security: Biscuit and signatures
- Bridge and connector troubleshooting
Contribute (25)
- Welcome
- Developer setup
- The dev loop
- Before a pull request
- Conventions
- Writing documentation
- Architecture
- Code tour
- Import boundaries
- Which tests run when
- Mutation testing
- The coverage ratchet
- The quality baseline
- CI explained
- Field passes
- Benchmarks
- Releasing
- npm packages
- Decisions and ADRs
- Governance
- Bot pull requests
- Contributing with AI agents
- Security policy
- Code of conduct
- Licences and credits