# Hono



```ts title="lib/postel.ts"
import { Postel, Secret } from "@postel/core";
import { config } from "./config.js";

export const postel = Postel({
  inbound: {
    vendor: {
      verify: Secret(config.webhookSecret),
    },
  },
});
```

## Routing facade [#routing-facade]

`HonoWebAdapter(postel, app)` binds to your app and registers gated routes by source key. Raw bytes are verified before your handler runs; the verified result is on `c.var.postel`, typed to the source's schema output.

```ts title="app.ts"
import { Hono } from "hono";
import { HonoWebAdapter } from "@postel/hono";
import { postel } from "./lib/postel";

const app = new Hono();
const hwa = HonoWebAdapter(postel, app);

hwa.inbound.vendor.post("/webhooks/vendor", (c) => {
  const { event } = c.var.postel; // verified · typed (schema output) · raw bytes intact
  return c.json({ ok: true, type: event.type });
});
```

The source key (`vendor`) is type-checked against the sources you configured. On failure the gate short-circuits with the mapped status and your handler never runs; a non-`PostelError` bubbles as 5xx.

### Other methods [#other-methods]

Webhooks are `POST` by default, and `.post` is sugar for `.on("POST", …)`. For a provider that delivers over `PUT`/`PATCH`, bind the method explicitly — only body-bearing verbs (`POST` | `PUT` | `PATCH`) are accepted, since the gate verifies a signature over the request body:

```ts
hwa.inbound.vendor.on("PUT", "/webhooks/vendor", (c) => c.json({ ok: true }));
```

## JWKS [#jwks]

When you configure an `outbound` slot, `hwa.outbound.bindJwks()` publishes your current public keys at `/.well-known/webhooks-keys`, defaulting the provider to `postel.outbound.keys.publicJwks()` so rotations appear without a redeploy. Pass a route and/or provider to override.

```ts
hwa.outbound.bindJwks();                         // GET /.well-known/webhooks-keys
hwa.outbound.bindJwks("/keys", customProvider);  // custom route + provider
```

## Admin [#admin]

`hwa.admin.bindAdminRoutes(prefix, opts)` mounts the [`@postel/admin`](/docs/operations/admin) control plane (Hono speaks Fetch natively). `authorize` is required.

```ts
hwa.admin.bindAdminRoutes("/admin", { authorize: (req) => checkAdminToken(req) });
```

## Low-level primitives [#low-level-primitives]

The facade is sugar over `withWebhook` / `verifyWebhook`. Reach for them to attach the gate to your own routing (extra middleware, custom paths). On this primitive path the context isn't statically typed, so read the verified result with `getVerified(c)` (it throws if the gate didn't run):

```ts
import { getVerified, verifyWebhook, withWebhook } from "@postel/hono";

app.post("/webhooks/vendor", verifyWebhook(postel.inbound.vendor), (c) => {
  const { event } = getVerified(c); // typed read on the primitive path
  return c.json({ ok: true, type: event.type });
});
app.post("/webhooks/github", withWebhook(postel.inbound.github, (c) => c.text("ok")));
```
