npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@okrlinkhub/agent-bridge

v3.1.3

Published

A agent bridge component for Convex.

Readme

Convex Agent Bridge (Config-First)

npm version

@okrlinkhub/agent-bridge exposes an HTTP gateway for external agents with a config-first approach:

  • declare exposed Convex functions in a single file;
  • configure permissions in batch only for those functions;
  • no changes to existing Convex queries/mutations/actions.

Installation

npm install @okrlinkhub/agent-bridge

Quick setup

1) Initialize files in the consumer project

npx @okrlinkhub/agent-bridge init

This creates:

  • agent-bridge.config.ts (project root)
  • convex/agentBridge.ts

2) Enable the component in convex/convex.config.ts

import { defineApp } from "convex/server";
import agentBridge from "@okrlinkhub/agent-bridge/convex.config.js";

const app = defineApp();
app.use(agentBridge);
export default app;

3) Mount routes in convex/http.ts

import { httpRouter } from "convex/server";
import { registerAgentBridgeRoutes } from "./agentBridge";

const http = httpRouter();
registerAgentBridgeRoutes(http);
export default http;

Optional: customize auth in registerRoutes:

registerRoutes(http, components.agentBridge, bridgeConfig, {
  pathPrefix: "/agent",
  serviceKeysEnvVar: "AGENT_BRIDGE_SERVICE_KEYS_JSON",
  linkingMode: "component_api_only",
});

linkingMode: "component_api_only" is the default and keeps linking on the component's Convex API (no HTTP linking endpoints exposed by the bridge).

4) Configure exposed functions in agent-bridge.config.ts

import { api } from "./convex/_generated/api";
import { defineAgentBridgeConfig } from "@okrlinkhub/agent-bridge";

export default defineAgentBridgeConfig({
  functions: {
    "cart.calculatePrice": { ref: api.cart.calculatePrice, type: "query" },
    "cart.applyDiscount": { ref: api.cart.applyDiscount, type: "mutation" },
    "okr.create": { ref: api.okr.create, type: "mutation" },
  },
  metadata: {
    "cart.calculatePrice": {
      description: "Calculate total price",
      riskLevel: "low",
      category: "commerce",
    },
  },
});

Exposed HTTP endpoints

  • POST /agent/execute
  • GET /agent/functions

POST /agent/execute

Required headers (strict-only):

  • X-Agent-Service-Id: <service-id>
  • X-Agent-Service-Key: <service-key>
  • X-Agent-App: <app-key> (e.g. crm, billing)

Optional header for Convex user context:

  • Authorization: Bearer <user-jwt>

Optional headers for audit linking (hashed in bridge logs):

  • X-Agent-Link-Provider
  • X-Agent-Link-Provider-User-Id
  • X-Agent-Link-User-Subject
  • X-Agent-Link-Status

When to use:

  • If the target function uses ctx.auth.getUserIdentity(), always send Authorization.
  • If the function is service-only, Authorization can be omitted.

Required body:

{
  "functionKey": "cart.calculatePrice",
  "args": { "cartId": "..." }
}

Response:

  • success: { "success": true, "result": ... }
  • error: { "success": false, "error": "..." }

Main status codes: 401, 403, 404, 429, 500.

User context cross-app (best practice)

To use Agent Bridge in Convex apps with different auth stacks, follow this contract:

  1. Service auth (always): X-Agent-Service-Id, X-Agent-Service-Key, X-Agent-App
  2. User auth (when needed): Authorization: Bearer <user-jwt>

Common token sources:

  • nextauth_convex: read session.convexToken server-side
  • auth0: use Auth0 access token valid for Convex
  • custom_oidc: use OIDC token from the app's provider

The package includes reusable helpers:

import {
  buildAgentBridgeStrictHeaders,
  createAuth0TokenAdapter,
  createCustomOidcTokenAdapter,
  createNextAuthConvexTokenAdapter,
  parseAppBaseUrlMap,
  resolveAppBaseUrlForAppKey,
  resolveUserToken,
  validateJwtClaims,
} from "@okrlinkhub/agent-bridge";

Quick example:

const tokenAdapter = createNextAuthConvexTokenAdapter({
  getSession: async () => session,
});

const userToken = await resolveUserToken(tokenAdapter);
const validation = userToken
  ? validateJwtClaims(userToken, { expectedAudience: "convex" })
  : { valid: false };

const headers = buildAgentBridgeStrictHeaders({
  serviceId: process.env.OPENCLAW_SERVICE_ID!,
  serviceKey: process.env.OPENCLAW_SERVICE_KEY!,
  appKey: "crm",
  userToken: validation.valid ? userToken : null,
});

Notes:

  • validateJwtClaims only checks claims (exp, iss, aud) and does not replace Convex's cryptographic validation.
  • Never log user tokens or service keys.

Environment variables — detailed setup

Single source of truth: .env.local in the project root.

Put all variables in .env.local, then sync them to Convex, Vercel, and Fly.io (or Railway) according to the matrix below.

Sync matrix (from .env.local to platforms)

| Variable | Convex | Vercel | Fly.io / Railway | |----------|--------|--------|-----------------| | AGENT_BRIDGE_SERVICE_KEYS_JSON | ✓ | — | — | | AGENT_BRIDGE_AUDIT_HASH_SALT | ✓ | — | — | | PUBLISHED_SITE_URL | ✓ | — | — | | AGENT_BRIDGE_BASE_URL | — | ✓ | — | | APP_BASE_URL_MAP_JSON | — | ✓ | ✓ | | OPENCLAW_SERVICE_ID | — | ✓ | ✓ | | OPENCLAW_SERVICE_KEY | — | ✓ | ✓ |

Important: This package reads only AGENT_BRIDGE_SERVICE_KEYS_JSON, AGENT_BRIDGE_AUDIT_HASH_SALT, and APP_BASE_URL_MAP_JSON. Variables like OPENCLAW_*, PUBLISHED_SITE_URL, and AGENT_BRIDGE_BASE_URL belong to the integration flow (OpenClaw + frontend/BFF), not the package runtime.

Where do service_id and service_key come from?

  • service_id: You choose it. A readable identifier for the service instance calling the bridge (e.g. openclaw-prod, openclaw-staging, my-agent).
  • service_key: Generate it with the package helper. A cryptographic secret (format abs_live_<random>).

Flow:

  1. Choose a service_id (e.g. openclaw-prod).
  2. Generate the service_key (see below).
  3. Add the pair to AGENT_BRIDGE_SERVICE_KEYS_JSON on Convex.
  4. Use the same service_id and service_key in OPENCLAW_SERVICE_ID and OPENCLAW_SERVICE_KEY on Vercel/Fly.io/Railway.

Generate service_key (Node.js, requires package installed):

node -e "import('@okrlinkhub/agent-bridge').then(m => console.log(m.generateAgentBridgeServiceKey()))"

Or in TypeScript:

import { generateAgentBridgeServiceKey } from "@okrlinkhub/agent-bridge";

const serviceKey = generateAgentBridgeServiceKey(); // e.g. abs_live_abc123...

Generate AGENT_BRIDGE_AUDIT_HASH_SALT:

openssl rand -base64 32

Or with Node.js:

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Complete .env.local example

# Convex (sync to Convex Dashboard)
AGENT_BRIDGE_SERVICE_KEYS_JSON={"openclaw-prod":"abs_live_xxx","openclaw-staging":"abs_live_yyy"}
AGENT_BRIDGE_AUDIT_HASH_SALT=<random-32-chars>
PUBLISHED_SITE_URL=https://app.example.com

# Vercel / Fly.io / Railway (sync to all platforms)
APP_BASE_URL_MAP_JSON={"crm":"https://crm.example.com","billing":"https://billing.example.com"}
OPENCLAW_SERVICE_ID=openclaw-prod
OPENCLAW_SERVICE_KEY=abs_live_xxx

# Vercel only (BFF that invokes the bridge)
AGENT_BRIDGE_BASE_URL=https://your-deployment.convex.site

Convex Dashboard — step by step

  1. Go to dashboard.convex.dev.
  2. Select your consumer app project.
  3. Sidebar → SettingsEnvironment Variables.
  4. Click Add Environment Variable.
  5. Name: AGENT_BRIDGE_SERVICE_KEYS_JSON
  6. Value: JSON (e.g. {"openclaw-prod":"abs_live_xxx"}). No extra spaces, use double quotes.
  7. Select Development and Production.
  8. Click Save.

Repeat for AGENT_BRIDGE_AUDIT_HASH_SALT and PUBLISHED_SITE_URL.

OpenClaw multi-app setup

For multiple OpenClaw instances managing multiple apps:

  1. In Convex, set the variables from the matrix above.
  2. Register an agent per app with a unique appKey: crm, billing, warehouse, etc.
  3. OpenClaw sends for each call:
    • X-Agent-Service-Id (instance identity)
    • X-Agent-Service-Key (key for that instance)
    • X-Agent-App (varies by target app)

Multi-app URL routing (appKey -> baseUrl)

When OpenClaw must call multiple consumer apps (e.g. execute-on-behalf endpoint), use:

  • APP_BASE_URL_MAP_JSON={"crm":"https://crm.example.com","billing":"https://billing.example.com"}

Package helpers:

import {
  parseAppBaseUrlMap,
  resolveAppBaseUrlForAppKey,
} from "@okrlinkhub/agent-bridge";

const appBaseUrlMap = parseAppBaseUrlMap({
  appBaseUrlMapEnvVar: "APP_BASE_URL_MAP_JSON",
});
const resolvedBaseUrl = resolveAppBaseUrlForAppKey({
  appKey: "crm",
  appBaseUrlMap,
});
if (!resolvedBaseUrl.ok) {
  throw new Error(resolvedBaseUrl.error);
}
// resolvedBaseUrl.baseUrl => https://crm.example.com

Policy: no fallback to legacy APP_BASE_URL. If appKey is not in the map, fail explicitly.

Platform-specific setup

Vercel: Project → Settings → Environment Variables. Sync variables from .env.local per the matrix. Set for Production, Preview, and Development.

Fly.io: App → Secrets (or fly secrets set KEY=value). Sync APP_BASE_URL_MAP_JSON, OPENCLAW_SERVICE_ID, OPENCLAW_SERVICE_KEY from .env.local.

Railway: Service → Variables. Same variables as Fly.io.

Consistency checklist

  1. OPENCLAW_SERVICE_ID + OPENCLAW_SERVICE_KEY must match an entry in AGENT_BRIDGE_SERVICE_KEYS_JSON on Convex.
  2. appKey values in APP_BASE_URL_MAP_JSON must match X-Agent-App in requests and the database.
  3. No fallback to a single APP_BASE_URL: if appKey is not mapped, fail explicitly.
  4. Never log secrets (OPENCLAW_SERVICE_KEY, bearer token).

Benefits:

  • centralized control and debugging in the Convex bridge;
  • no multiple API key submissions in requests;
  • rotation and per-app policies managed in the bridge.

Agent and permission management

Component mutations/queries available in components.agentBridge:

  • agents.createAgent
  • agents.updateAgent
  • agents.rotateApiKey
  • agents.listAgents
  • gateway.authorizeByAppKey
  • permissions.setAgentPermissions (batch)
  • permissions.listAgentPermissions
  • permissions.setFunctionOverrides (batch)
  • permissions.listFunctionOverrides
  • gateway.queryAccessLog
  • linking.upsertLink
  • linking.resolveLink
  • linking.revokeLink
  • linking.listLinks

Link registry in the component (per-app)

The user link registry is persisted in the component's Convex DB:

  • logical key: provider + providerUserId + appKey
  • target: appUserSubject
  • status: active | revoked | expired

Each app that installs the component keeps its own registry in its Convex deployment, without a centralized cross-app database.

Batch permissions example

await ctx.runMutation(components.agentBridge.permissions.setAgentPermissions, {
  agentId,
  rules: [
    { pattern: "cart.*", permission: "allow" },
    {
      pattern: "okr.create",
      permission: "rate_limited",
      rateLimitConfig: { requestsPerHour: 60, tokenBudget: 50000 },
    },
  ],
  availableFunctionKeys: Object.keys(config.functions),
});

Breaking change: strict-only

As of this version:

  • X-Agent-API-Key is no longer supported in the HTTP runtime;
  • there is no single-key fallback;
  • the triad X-Agent-Service-Id + X-Agent-Service-Key + X-Agent-App is required.

Migration 0.2 -> next major

Main breaking changes:

  • removed token/instance token provisioning flow;
  • removed runtime function registration via createFunctionHandle;
  • removed use of legacy AgentBridge class.

New flow:

  1. configure functions in agent-bridge.config.ts;
  2. strict auth via X-Agent-Service-Id + X-Agent-Service-Key + X-Agent-App;
  3. batch policy via component mutations;
  4. centralized logs in agentLogs.

Local development

npm i
npm run dev