Skip to main content
The Hono integration provides two exports from @key0ai/key0/hono:
  • key0App(opts) — a Hono sub-app that serves the agent card and reserves the A2A endpoint.
  • honoValidateAccessToken({ secret }) — middleware that validates Bearer JWTs issued by Key0.

Installation

bun add @key0ai/key0 hono

Routes

When you mount key0App, the following routes are registered:
MethodPathDescriptionStatus
GET/.well-known/agent.jsonA2A agent card (discovery)Live
POST{basePath} (default /a2a)A2A JSON-RPC endpoint501
The A2A JSON-RPC endpoint currently returns 501 Not Implemented. Full A2A support is pending an upstream SDK update. Use the Express integration if you need the complete A2A flow today.
The x402 HTTP payment endpoint (/x402/access) is not available in the Hono adapter. Use the Express integration for full x402 support, or use the Hono adapter exclusively for agent card discovery and access token validation.

Setup

1

Configure the seller

Define your SellerConfig with wallet address, network, and pricing plans.
import type { SellerConfig } from "@key0ai/key0";

const sellerConfig: SellerConfig = {
  walletAddress: "0xYourWalletAddress",
  network: "testnet",
  basePath: "/a2a",
  plans: [
    {
      planId: "basic",
      unitAmount: "0.01",
      description: "Basic API access",
    },
  ],
  fetchResourceCredentials: async (challenge) => {
    return { token: "your-issued-credential" };
  },
};
2

Create stores and adapter

Provide the required IChallengeStore, ISeenTxStore, and IPaymentAdapter instances.
import {
  RedisChallengeStore,
  RedisSeenTxStore,
  X402Adapter,
} from "@key0ai/key0";
import Redis from "ioredis";

const redis = new Redis();
const store = new RedisChallengeStore({ redis });
const seenTxStore = new RedisSeenTxStore({ redis });
const adapter = new X402Adapter({ network: "testnet" });
3

Mount the sub-app

Import key0App from @key0ai/key0/hono and mount it on your main Hono app.
import { Hono } from "hono";
import { key0App } from "@key0ai/key0/hono";

const app = new Hono();

app.route(
  "/",
  key0App({
    config: sellerConfig,
    adapter,
    store,
    seenTxStore,
  }),
);
Your agent card is now available at GET /.well-known/agent.json.
4

Protect routes with access token validation

Use honoValidateAccessToken as middleware on any route that requires a valid Key0 JWT.
import { honoValidateAccessToken } from "@key0ai/key0/hono";

const guard = honoValidateAccessToken({
  secret: process.env.KEY0_JWT_SECRET!,
});

app.get("/protected/data", guard, (c) => {
  const token = c.get("key0Token");
  return c.json({
    message: "Authenticated",
    challengeId: token.jti,
    planId: token.planId,
    txHash: token.txHash,
  });
});

Accessing the decoded token

After honoValidateAccessToken succeeds, the decoded JWT payload is available via c.get("key0Token"). The payload contains:
FieldTypeDescription
substringThe original request ID
jtistringThe challenge ID
resourceIdstringThe resource that was paid for
planIdstringThe plan the client purchased
txHashstringThe on-chain transaction hash
On failure, the middleware returns a JSON error response:
ScenarioHTTP StatusError Code
Missing/malformed header401INVALID_REQUEST
Expired token401CHALLENGE_EXPIRED
Invalid signature401INVALID_REQUEST
Internal error500INTERNAL_ERROR

Full example

import { Hono } from "hono";
import { serve } from "@hono/node-server";
import {
  RedisChallengeStore,
  RedisSeenTxStore,
  X402Adapter,
} from "@key0ai/key0";
import { key0App, honoValidateAccessToken } from "@key0ai/key0/hono";
import Redis from "ioredis";

const redis = new Redis();

const sellerConfig = {
  walletAddress: "0xYourWalletAddress",
  network: "testnet" as const,
  plans: [
    {
      planId: "basic",
      unitAmount: "0.01",
      description: "Basic API access",
    },
  ],
  fetchResourceCredentials: async () => ({ token: "credential" }),
};

const app = new Hono();

// Mount Key0 sub-app (agent card + A2A placeholder)
app.route(
  "/",
  key0App({
    config: sellerConfig,
    adapter: new X402Adapter({ network: "testnet" }),
    store: new RedisChallengeStore({ redis }),
    seenTxStore: new RedisSeenTxStore({ redis }),
  }),
);

// Protected route
const guard = honoValidateAccessToken({
  secret: process.env.KEY0_JWT_SECRET!,
});

app.get("/api/resource", guard, (c) => {
  const token = c.get("key0Token");
  return c.json({ resource: "data", plan: token.planId });
});

serve({ fetch: app.fetch, port: 3000 });
console.log("Hono server running on http://localhost:3000");

API reference

key0App(opts)

Creates a Hono sub-app with agent card discovery and an A2A endpoint stub.
function key0App(opts: Key0Config): Hono
Parameters:
FieldTypeDescription
configSellerConfigSeller configuration (wallet, plans)
adapterIPaymentAdapterOn-chain payment verification adapter
storeIChallengeStoreChallenge state storage
seenTxStoreISeenTxStoreDouble-spend prevention storage

honoValidateAccessToken(config)

Returns Hono middleware that validates a Bearer token from the Authorization header.
function honoValidateAccessToken(config: ValidateAccessTokenConfig): MiddlewareHandler
Parameters:
FieldTypeDescription
secretstringThe JWT secret used to sign tokens