Skip to content
Tarn & Timber Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Durable Object transfer

Namespace ownership, callbacks, alarms and rollback for the move of the Durable Objects to the API Worker.

The stable logical IDs are ShopDatabase, ConversationHub, AccountWriter, Shop, and Api. The API declares both classes with transferredFrom: ['Shop']. Keep those declarations indefinitely: they cover lagging stages and are inert after transfer. The storefront binds both classes from the API through scriptName: api.worker.workerName and never declares them. The API is uploaded first because the storefront depends on its service binding.

The hub’s callback paths are served by the API Worker:

  • POST /internal/conversations/{id}/turn
  • POST /internal/conversations/{id}/handoff-timeout

They use ctx.exports.default on the class’s current host, authenticated by Authorization: Bearer <CONVERSATION_HUB_SECRET>. Preserve the existing ConversationHubSecret value across the move. The same release must contain the API callbacks and Fern.

Disposable stage

Use a stage named rehearsal-YYYYMMDD, dev-tenant identities and separate workers.dev origins. Never attach production custom domains. Leave ANTHROPIC_API_KEY unset on the stage API so the read-only fallback answers conversations. Use search tent, cart, orders, order <number or ID> or help; other messages show its limitations. Verify that add two tents to my cart changes nothing and explains that no human handoff is available. Start deletion of a test account before the transfer, so AccountWriter has persisted its blocked state, and check after the move that the blocked writer still refuses. Separately verify a conversation turn and the ConversationHub alarm on an open conversation. AccountWriter has no alarm; account deletion resumes through explicit recovery.

Rollback

A pre-transfer application rollback cannot undo namespace ownership. Keep an owner-created rollback build containing the earlier application behavior and the transferred topology: API class exports and callbacks, API transferredFrom declarations, Shop cross-script bindings, no Shop class exports. Run pnpm ready on that build.

Deploy it to the disposable stage and verify again. Then try a separate emergency reverse transfer: Shop declares transferredFrom: ['Api'], API references Shop, and both callbacks are hosted with the classes. Verify, transfer back to the API, and verify again. Reverse transfer is never the first incident response. Destroy only the disposable stage after recording the results. No production move is allowed until the owner has recorded all of these observations.

Owner smoke after the transfer

Use a disposable test account and the registered agent credentials described in scripts/smoke/poppy-direct.ts. Run it with POPPY_SMOKE_CONVERSATION=1 to add a Fern conversation round trip and an open event stream. After the agent is disconnected in the browser, the stream must end without an event: error, and a fresh read must be refused with a challenge. The script also prompts the operator to change, reload and restore a disposable address on /account/addresses. Retain that browser observation with the release record.

Check that the first hourly invocation after a release completes all five API jobs by reading the Worker’s JSON tail for scheduled_job records with worker: "api" and outcome: "ran". Successful jobs delete their lease rows, so an empty job_leases table after completion is expected.

Was this page helpful?