# How to wire Tidebase into a Hono app

Tidebase's recovery webhook handler is a fetch-style `Request → Response` function, and Hono hands you exactly that — so the integration is one line per route, no adapters. (The Tidebase server itself is built on Hono, which is why the seam is this clean.)

```typescript
import { Hono } from 'hono'
import { Tidebase } from '@tidebase/sdk'
import { generateReport } from './workflows.js'

const tide = new Tidebase()
tide.workflow('generate-report', generateReport)

const app = new Hono()
const webhook = tide.webhook()

// Recovery webhook: signed run.resume payloads re-invoke the workflow.
app.post('/tidebase', (c) => webhook(c.req.raw))

// Enqueue a durable run; return immediately.
app.post('/reports', async (c) => {
  const { topic } = await c.req.json()
  const { run } = await tide.enqueue('generate-report', {
    input: { topic },
    dedupeKey: `report-${topic}`
  })
  return c.json({ runId: run.id })
})

// Progress for your UI, straight from your Postgres.
app.get('/reports/:runId', async (c) => {
  const detail = await tide.runs.get(c.req.param('runId'))
  return c.json({ status: detail.run.status, state: detail.state })
})

export default app
```

Tidebase is an open-source checkpoint layer for AI agents: wrap your steps, and failed runs resume from the last safe point — in your own Postgres, without moving execution into a new runtime.

## The three roles in this file

1. **`/reports` enqueues.** The route does no agent work — `tide.enqueue` writes a durable `queued` run (deduped, so a retried request can't double-create) and returns the `runId` inside any runtime's request budget. This matters doubly on edge/serverless runtimes where Hono often lives: the long-running work must not be in the handler.
2. **`/tidebase` resumes.** When a non-queue run stalls or fails, Tidebase POSTs a signed `run.resume` payload here; the SDK verifies the HMAC (`TIDEBASE_WEBHOOK_SECRET` on both sides — pass `c.req.raw` so the body bytes reach it unparsed) and re-invokes the registered workflow with the same `runId`. Completed steps replay from checkpoints per [the replay contract](../replay-contract-is-it-safe-to-rerun.md).
3. **`/reports/:runId` reads.** Live state, step results, and pending [gates](../human-approval-gates-for-ai-agents.md) come from `runs.get` — your product UI renders progress and approvals without touching the worker.

## The worker

The honest tradeoff, stated plainly: Tidebase does not execute your code. A long-lived worker process claims queued runs and executes them:

```typescript
import { Tidebase } from '@tidebase/sdk'
import { generateReport } from './workflows.js'

const tide = new Tidebase()
tide.workflow('generate-report', generateReport)
await tide.work({ queues: ['default'] })
```

If you deploy Hono to an edge runtime, the worker is the one piece that needs a normal Node process (a container, a VM, a long-running service) — `tide.work` polls and executes, which is not an edge-shaped job. Retries, backoff, lease expiry, and requeueing are Tidebase's problem, not the worker's.

One caveat for fully-edge deployments: the webhook route executes the *remaining* workflow inline, so it inherits the runtime's CPU/time limits. If your unfinished tails can be long, prefer queues — the reconciler requeues a dead worker's run without any webhook involved.

## What Tidebase does not do here

- **Alpha, opt-in auth.** Set `TIDEBASE_API_KEY` before exposing the Tidebase server beyond localhost, and the webhook secret before trusting `/tidebase`.

Repo: <https://github.com/BlueprintLabIO/tidebase> · See also: [How to wire Tidebase into an Express app](express.md)