Repository navigation
EN Bridge Connector Open Badges
Français · Type: how-to + reference · Audience: admin · Context: Reality Bridge · Status: ready · Applies from: 4.3.0
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:bridge-badge-code-once
The Open Badges connector serves a small form: a player posts a badge (an Open Badges 2.0 assertion or a 3.0
credential) with the pairing code their game shows, the connector verifies the badge, and the verdict (valid,
invalid, expired, revoked, indeterminate) proposes the signal the game declares for it. The game never receives
the badge. It is experimental, like every connector (Connectors overview).
A quest-n-click-connector open-badge process answering POST /v1/badges, fetching the badge's documents under a
strict network policy, and proposing one signal per badge and verdict.
- A connector beside a running Bridge, from 4.1.9. This page applies from 4.3.0, when a badge signed with a weak or
foreign key became
invalid(aneddsa-jcs-2022proof checked with an Ed25519 key only; RS256 needs a modulus of at least 2 048 bits, ES256 a P-256 key). -
Verified on: not replayed for this page. CI plays it from the release archive (
npm run connector-archive-e2e) and against twelve badge fixtures (tests/connectors-badges.test.ts); no person has yet shown it a real badge.
-
A running Bridge (Install and run the Bridge) and a game that declares the issuers it trusts and the signal of each verdict (Signals and the manifest):
'open-badge': { issuers: ['https://badges.example.org/issuer'], valid: 'badge.valid', revoked: 'badge.refused' }
-
A page of yours that posts the form to the connector (the connector serves the route, not a page).
grant where the Bridge's folder is; the connector behind your HTTPS proxy, in a folder holding its configuration and
token. npm run connector -- open-badge … in the engine repository, npx quest-n-click-connector open-badge … with
the package.
-
Grant the connector its token, for the badge signals only, with pairing:
# in the engine repository (the Bridge's folder is .cache/bridge) npm run bridge -- grant --connector=garden-badges --source=badge --signals=badge.valid,badge.refused --pair > badge.token
-
Write its configuration,
badge.json;hostsdefaults to the hosts of the game'shttps:issuers:{ "bridge": { "url": "http://127.0.0.1:8787/", "tokenFile": "badge.token" }, "gameId": "signals", "health": { "port": 9304 }, "open-badge": { "port": 8790, "hosts": ["badges.example.org"] } }
-
Start it:
# in the engine repository npm run connector -- open-badge --config badge.jsonIt refuses to start when the game declares no
reality.connectors['open-badge'].
-
A player posts, with a code the game has just shown:
# from the player's side (your form does this) curl -s -X POST http://127.0.0.1:8790/v1/badges -H 'Content-Type: application/json' \ -d '{"code":"ABCD2345","badge":"https://badges.example.org/assertions/42","email":"player@example.org"}'
badgeis a URL, a compact JWS, or the credential itself;email(orrecipient) is what a hashed recipient is checked against. The answer is202 {"status":"valid","signal":"accepted"}, or200 {"status":"…","signal":"none"}when the game declares no signal for that verdict.
-
GET http://127.0.0.1:9304/healthanswers 200{"connector":"open-badge","ok":true,"detail":"form on port 8790",…}. - The connector logs
"event":"open-badge.verdict"with the status and the format; the Bridge logs"event":"pairing.confirmed", then"event":"signal.accepted".
| Answer | Cause | Fix |
|---|---|---|
422 {"error":"pairing-pairing"}
|
The code is unknown, expired or used already: a code serves once | A new code from the game |
400 {"error":"shape"}
|
Not JSON, or no code
|
Post { code, badge, email? }
|
202 with "status":"indeterminate"
|
A proof suite it cannot check (eddsa-rdfc-2022…), or a document it could not fetch under its policy |
Ask the issuer for a supported format; check hosts
|
202 with "status":"invalid"
|
Another issuer than the game's, a recipient that does not match, a bad or weak signature | — |
413, or 429 {"error":"rate"}
|
A body over 64 KB; over 30 posts a minute from one address | — |
None on disk. Revocation and status lists are cached in memory for at most a day.
Stop the process (SIGTERM). Revoke its token on the Bridge if it leaked, and grant a new one.
Limit ·
bridge-badge-code-onceA pairing code serves once. Each post needs a code the game has just shown, and confirming it links the game to a new pseudonymous player, as any pairing does.
-
Open Badges 2.0: hosted (the assertion fetched again from its own id) or signed (a JWS whose key the issuer
owns). 3.0: a VC-JWT, or a Data Integrity proof
eddsa-jcs-2022; other suites, such aseddsa-rdfc-2022, areindeterminate, nevervalid. - The issuer among the game's
issuers; the recipient against the email (hashed with the badge's salt, then dropped); the dates; the revocation (OB2 revocation lists, OB31EdTechRevocationListand Bitstring status lists, cached at most a day). - From 4.3.0, a key too weak for its algorithm makes the badge
invalid(an RSA key under 2 048 bits, a curve other than P-256 for ES256, anything but Ed25519 foreddsa-jcs-2022). - The key is
sha256('open-badge:' + player + ':' + badge id + ':' + verdict): the same badge with the same verdict for the same player is one signal.
Every document is fetched under one policy: https: only; hosts from hosts (by default the hosts of the issuers);
the name resolved once, and no private, loopback, link-local, multicast or unspecified address (IPv4, IPv6 and
IPv4-mapped IPv6); two redirects at most, each checked again; 64 KB; 10 seconds; JSON nested at most 8 deep;
@context never fetched.
Guarantee ·
bridge-badge-network-policy· referenced · scope self-checked by the author (AI), not reviewed A fetch withouthttps:, to a host off the list, with credentials in its URL, or whose name resolves to a private address is refused, and a name is resolved once (no rebinding).
The open-badge section:
| Field | Default | Meaning |
|---|---|---|
port, host
|
—, 127.0.0.1
|
Where POST /v1/badges listens |
hosts |
the hosts of the game's https: issuers |
Where documents may be fetched from |
perMinutePerIp |
30 | Posts a minute per address |
maxBytes |
65 536 | A post's body |
The badge and the documents it points to are fetched, checked and dropped; only a verdict leaves the connector, as a
signal, with a hash of the badge's id and issuer as evidenceHash. The email a player gives to match a hashed
recipient is hashed with the badge's salt for the comparison, then dropped: never stored, logged or sent. Revocation
and status lists (an issuer's, not a person's) are cached for at most a day. The addresses that post to the form are
counted in memory for a minute (the rate limit), then forgotten.
- The code:
connectors/src/badges/connector.ts,verify.ts,fetch.ts. - The threat model:
docs/dev/threat-models/open-badge.md. - Tested by
tests/connectors-badges.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