Reference5 min read5 sections

Recover a saved traveler

docs/PLAYER_RECOVERY.mdSource updated 2026-10-26
Markdown
On this page 5 sections

A room owner can replace a lost browser credential without creating a new traveler or moving inventory between identities. Recovery keeps the existing player ID, pose, inventory and private rewards. It changes only access, the room's fencing epoch and the durable operation sequence/receipt.

This is an operator maintenance workflow, not public account signup. Verify which person owns the saved traveler before issuing access. Names are not unique: select the exact traveler UUID from the operator listing.

#Operator workflow

Use the PostgreSQL connection variables for the affected deployment and set DW_PERSISTENT_ROOM to its exact room ID. The CLI requires server/database access; never put those credentials in a player browser.

Terminal
export DW_PERSISTENT_ROOM=lantern-house
npm run player:recover -- list
npm run player:recover -- prepare ACTOR_UUID 'https://your-host/?persistent=lantern-house' /private/rowan-recovery.json

Replace ACTOR_UUID, the player URL and the private output path. The URL must already target the correct persistent room, including its retained engine path or physics query if required by the deployment. Preparation creates a new file with mode 0600 and refuses to overwrite one. It does not change access. The file retains the operation ID, credential precondition and replacement key so an interrupted application can be retried exactly. CLI stdout contains metadata and the file path, never the replacement key or private link.

Stop the affected room and its supervisor before applying. For a routed deployment, stop the coordinator that could immediately restart it as well. A live room lease rejects recovery; do not bypass this check.

Terminal
npm run player:recover -- apply /private/rowan-recovery.json

After success, restart the room. Read the url field from the private file and give it to the verified traveler. Keep the file private; its link grants access to that traveler. The browser shows Your journey is still here and asks the recipient to choose Restore my traveler. It removes the secret fragment from the address immediately and keeps the replacement only in memory until normal room admission accepts it. Choosing Keep my current journey leaves the browser's existing key unchanged. Normal room-passphrase rules still apply.

Once admitted, ordinary browser storage retains the new return credential. Reloading uses the same saved traveler. An invalid replacement cannot overwrite a previously stored credential. A new blank browser does not silently select an identity just because someone sent it a recovery link.

#Durability and revocation

Recovery locks the room row and checks the exact saved credential set. It removes all old credentials for that traveler, installs the new hash with the ordinary 30-day admission lifetime, fences the old room lease, and writes a durable credential-recovery receipt/outbox event in one PostgreSQL transaction. It does not change the world release, player revision, pose, item ownership or awards. No new database migration is needed; existing backup table coverage includes the changed rows. Full backup/restore qualification of this new event remains open.

Retry apply with the same private file after an uncertain response. The same operation returns its receipt without replacing credentials again. Reusing the operation for different inputs fails. If another recovery replaced the key or it expired, an old operation cannot revive it: prepare a new file. Credential preconditions reject competing/stale recovery plans. Do not prepare separate replacement files merely because a response was lost.

The room must be stopped because live browser and gateway connections retain admitted identities. Recovery does not attempt unsafe mid-session revocation. The changed room epoch fences previous owners; restarted admission rejects the old key. Room-ticket checks still require current room generation and ordinary session validation. Crash/partition and routed-ticket fault combinations remain in final qualification.

#Internal API

The existing private control service also exposes these operator-only POSTs:

  • /v1/players/list with {room} returns bounded traveler identity metadata and a credential-set fingerprint, not keys or credential hashes.
  • /v1/players/recover with {room, actor, operation, credentials, credentialHash} executes the replacement. credentials is the exact fingerprint from the listing; credentialHash is SHA-256 of a fresh random 256-bit key generated and securely retained by the operator before the call.

The control bearer credential is mandatory and browser Origin requests are rejected. These endpoints are not forwarded by the public room or doorway.

#Scope and remaining review

The browser confirmation requires the updated player client. Recovery does not implicitly upgrade a retained engine or migrate content; an older pinned client must receive a compatible, explicitly managed update before it understands the link. Use the ordinary retained-engine deployment procedure.

Global accounts, automatic identity verification, email/social login, world membership tied to a global player account, self-service recovery, live-session revocation and cross-region account failover remain separate unfinished work. Review real operator deployment/stop procedures, older engine compatibility, process-kill/fsync faults, routed tickets, lost-response retries over real networks, backup restore, mobile layouts and screen readers during qualification.

#Focused acceptance

Four native/service/CLI checks pass against fresh PostgreSQL: cooperative and trusted inventory-preserving recovery, active-lease refusal, transaction rollback, old-key rejection after host restart, retry receipts and superseded-key refusal; operator authentication/origin rejection and the real recovery endpoint; private CLI preparation followed by stopped-room apply and exact retry without printing the secret. The real software-WebGPU browser journey passes (14.0 seconds): confirmation before credential storage, restored identity/inventory, ordinary reload, cancellation, invalid-key rejection and preservation of an existing valid key. Its first extended run exposed same-document hash navigation skipping the confirmation; the player now reloads that navigation through the ordinary confirmation path, with listener cleanup on disposal.

Type/import checks, the client build and the control-service bundle pass. Confirmation screen and the restored in-world view were visually reviewed. Test source: tests/player-recovery.test.ts. Run native checks through node tools/cli/test-control.mjs tests/player-recovery.test.ts; set DW_RECOVERY_CLIENT_DIR to a freshly built client and configure the Playwright browser to include the browser journey. These focused checks do not close the broader qualification items above.

Account-linked travelers now use account key replacement. The room-key recovery command refuses these identities to preserve account ownership.

End of Player recovery