Small local bridge for browser apps that need SSH-key signing, SSH tunneling, or localhost TCP access without giving the browser direct access to private keys.
It ships with Incarnate defaults, but the core is game-configurable. A different
game can use the same installed package by providing a bridge.game.json file
and running game-bridge.
Installing does not start the bridge:
- no
preinstall,install, orpostinstallscripts - one runtime dependency:
ws - no telemetry
- no browser storage access
- no shell startup file changes
- no background daemon
- no private monorepo dependency
- private keys never leave your machine
Keys, host trust, tunnels, and localhost listeners are created only when you run the matching command.
- Node.js 20 or newer
- npm
- OpenSSH client tools:
ssh,ssh-keygen
npm install -g @inc-realm/bridgeThis installs two CLI names:
game-bridge
incarnate-bridgeUse game-bridge for reusable integrations. incarnate-bridge remains for
Incarnate compatibility.
incarnate-bridge key generate
incarnate-bridge host trust --ssh-host game.inc-realm.com
incarnate-bridge browser start \
--transport ssh \
--ssh-host game.inc-realm.com \
--account "" \
--character ""The browser connects to the local URL printed by the command:
Incarnate bridge listening on ws://127.0.0.1:8787/?token=<random-token>
The token is required. A random website cannot use the bridge unless it knows the token and passes the configured browser origin policy.
The bridge keeps its game connection for up to five seconds after a browser
socket closes so a page reload can reattach. It checks browser liveness with a
WebSocket ping every ten seconds and a ten-second pong deadline; silent browser
loss triggers teardown within twenty-five seconds, including reconnect grace. A
bridge with no initial browser connection exits after sixty seconds. The reserved
bridge_disconnect message ends the game connection immediately.
Local launchers can query GET /__incarnate/status on the same loopback port
with Authorization: Bearer <session-token>. The response reports the bridge
instance ID, target host/account/key profile, active character, browser
attachment state, bridge lifecycle state, upstream session state, and package
version. The token is the same random value used in the WebSocket URL. Status
requests without it receive 401; unknown paths receive 404.
Status requests are accepted only from loopback addresses; remote requests with
the token receive 403.
Create a bridge.game.json:
{
"gameId": "example-realm",
"displayName": "Example Realm",
"signingNamespace": "example-realm-auth",
"defaultKeyPath": "~/.ssh/example_realm_ed25519",
"defaultKeyLabel": "device",
"defaultAiHost": "127.0.0.1",
"defaultAiPort": 8083,
"defaultBrowserBridgePort": 8787,
"defaultSshHost": "game.example.invalid"
}Then run:
game-bridge key generate --game-config ./bridge.game.json
game-bridge browser start \
--game-config ./bridge.game.json \
--transport sshThe server stays authoritative. The bridge forwards game command JSON; it does not decide whether a player may run a gameplay, admin, payment, or account command.
The bridge keeps sensitive operations local:
key generatecreates an Ed25519 key and.pubfilehost trustuses normal OpenSSHknown_hostsbrowser startlistens on loopback by default- browser access requires a random session token
- optional browser origin checks are enforced before session setup
- private keys are used only for local
ssh-keygen -Y sign - signatures are scoped by a game-specific signing namespace
Use this when a browser UI needs a narrow local capability. If your user is already in a terminal, plain SSH may be enough. If you need normal browser login, use WebAuthn/passkeys. If you need server-to-server identity, use SSH, mTLS, or another standard service-auth mechanism.
For the full architecture, threat boundaries, auth flow, environment variables,
and protocol customization, see docs/architecture.md.
import { defineBridgeGameConfig, startBrowserBridgeServer } from "@inc-realm/bridge";
import { ensureKeyPair } from "@inc-realm/bridge/openssh";
import { INCARNATE_GAME_CONFIG } from "@inc-realm/bridge/incarnate";git clone https://github.com/mshilts/incarnate-bridge.git
cd incarnate-bridge
npm install
npm testUseful checks:
npm audit --omit=dev
npm run build
npm run browser:security-test
npm run pack:checkThe npm package includes dist/, src/, tests/, docs/, examples/,
README.md, SECURITY.md, and LICENSE so the shipped JavaScript, source,
tests, architecture notes, and security policy are reviewable.
Incarnate migration notes live in
docs/incarnate-integration-plan.md.