# OpenBotAuth

Open pay-per-crawl infra behind any CDN

OpenBotAuth enables agents to access the web by proving identity, intent and paying for content. By logging in with Github, developers can host their agent keys for Web Bot Auth on the registry, skipping buying a domain, DNS verification or identity verification with a CDN. Registry tracks your agent telemetry over HTTP.

Publishers and Indie websites can point to the registry to block unverified scrapers and monetize legitimate agent traffic directly. We've a WordPress plugin available today, with other platform support in the works.&#x20;

Works with browsers like BrowserBase, Onkernel, AgentCore, and agent frameworks like Langchain, OpenAI, Mastra, etc. by adding custom HTTP header.

We'd love your contribution to the open problems. Please visit our [Research Page](https://openbotauth.discourse.group/) or take a quick look at [Open Research Problems](https://docs.openbotauth.org/open-research-problems).&#x20;

If you haven't already, create an account on the [OpenBotRegistry](https://openbotauth.org/login).&#x20;


# TL;DR

ELI5 for Open Bot Auth

If everyone is gonna have a [dedicated GPU](https://www.techradar.com/pro/you-really-want-every-person-to-have-their-own-dedicated-gpu-openai-becomes-nvidias-biggest-cheerleader-as-its-president-calls-for-10-billion-gpu-bonanza-but-no-mention-of-the-petawatt-electricity-requirement-these-will-command) in future, then quite possibly, there will be 1000s of agents on this GPU. There's no way to delegate them your identity today.&#x20;

OpenBotAuth proves your ownership of the agent to the website and allows your agent to pay on your behalf for consuming content. Once you create your profile on the Registry, you can create agent signature cards tied to your identity.&#x20;

OpenBotRegistry is an attempt to tie your already verified social idenitites to agents, making verification process fast and accessible.&#x20;

### Why is this cool?&#x20;

1. Developers don't have to keep buying websites to hosts JWKS (or[ Signature Agent Cards](https://thibmeu.github.io/http-message-signatures-directory/draft-meunier-webbotauth-registry.html#name-signature-agent-card)) or worry about tracking key rotation.
2. Identities are not tied to a CDN, ie, identities are open to the whole  web, if they choose to adopt it.&#x20;
3. Websites transact directly with the agent owner rather than middlemen. Content creators maintain control over pricing and access rights.&#x20;

<figure><img src="/files/DAJbkssQtr2BzY6UhBSg" alt=""><figcaption></figcaption></figure>

### What is an "Origin"?

The **origin** is your actual website server.

### What is a "Proxy"?

A **proxy** is a middleman  that sits between bots and your origin server. Instead of visitors connecting directly to your website, they connect to the proxy first, and the proxy forwards requests to your origin.

### What is Direct Verification? <a href="#what-is-direct-verification" id="what-is-direct-verification"></a>

**Direct verification** means your origin server checks if a bot is legitimate without relying on a proxy or middleman.​​

Here's how it works:

1. The bot creates a digital signature.
2. The bot sends this signature directly to your origin server (example.com) along with their request (we call this intent).​​
3. Your origin server verifies the signature​

Refer to [Usecases](/usecases) to read more on different industry use cases.&#x20;


# Goals

Features we'd like to achieve in this spec

#### Origin Enforcement

Enable websites to verify and block bots at the origin server level, without relying solely on edge or proxy-based filtering.

#### Intent Declaration

Allow origin to selectively reveal or hide content based on bot's intent, ie, to train LLM models,  for search indexing or summarization, etc.&#x20;

#### Open registry

An open bot registry tied to other online identities, such that anyone can create a verified identity without an intermediary.


# Expected Outcomes

#### Non-Spoofable Identity

The specification must provide non-repudiable proof of bot identity and actions, creating a verifiable record that can serve as legal evidence when disputes arise.

#### No Discrimination

Bots and agents from large corporations shouldn't receive preferential access or rates over smaller organisations.

#### Private Browsing

Internet was built on the backbone of privacy and we don't want a future where private lanes don't exists. There must be reasonable rate limites to accomodate anonymous bot traffic.&#x20;

#### Open Registry Verification

No single entity should monopolize bot authentication and blocking decisions, ensuring the system remains open and resistant to centralized control.

#### Runtime Attenuation

Origin can dynamically adjust bot permissions during an active session—restricting or expanding access in real-time based on observed behavior, declared intent, and current context without forcing re-authentication.


# Open Research Problems

We'd love to solve these problem with the community in Research Forum

1. **What is a bot?:** A formal definition of a bot is still TBD.
2. **What is Trust?:** Establishing what constitutes "trusted" behavior or "trustworthy" agents is left to implementers and policy decisions outside this protocol.
3. **Multi-user OBO agent :** Authentication for agents that act on behalf of multiple users simultaneously is not addressed in this specification.
4. **Server-side vs client-side bot architecture:** The specification does not differentiate between centrally-hosted bots and locally-run agents originating from end-user devices.
5. **Reputation systems:** Mechanisms for tracking bot behavior history or maintaining reputation scores across services are not included.
6. **Bot impersonation:** Detecting or preventing bots from falsely claiming another bot's identity is outside the core authentication scope.


# Usecases

Some jibber jabber on the future of open bot auth

### Beloved E-Commerce

Amazon probably does not want Walmart agents on their website. We envision a world where Google agents do not see different pricing than Amazon agents. Recently, [Shopify](https://chrisleverseo.com/blog/how-to-fix-shopify-crawl-issues-in-screaming-frog-and-sitebulb-using-web-bot-auth/) released an independent implementation of Web Bot Auth to whitelist their SEO crawlers. The ability to allow specific users in a fast lane so they don't hit 429 error is powerful for merchants.&#x20;

### Publishers and Bloggers

As a blogger, I'm happy to get crawlers on my website, as it boosts visibility. However, the only thing that worked for bloggers is Google Adsense and now with deteriorating web traffic, a blogger posting about their eurotrip should be able to earn their fair share from LLMs. With OpenBotAuth, content creators can finally track who accesses their work, enforce usage rights, and build monetization models around legitimate AI agent access.

### Financial Services

Another heavy user of AI products. This industry has a lot of legal requirements around authenticated data sources. Furthermore, they want ot know if the client connecting to the servers is compliant and follow specific standards. This can be solved with a whitelisted registry for financial markets in the OpenBotRegistry directly.&#x20;

### Agentic Login

Agents often get stuck on login pages, as those were made for the human. As less and less traffic gets redirected to websites, we need new ways to authenticate users of the web.

### Travel

Travel booking agents verify their identity to search flights, hotels, and rental cars across multiple providers. OpenBotAuth enables travel sites to offer preferential API-like access to authenticated agents while protecting against price scraping and inventory hoarding by malicious bots.

### Human Trafficking Prevention

Law enforcement and NGO agents verify their credentials to access sensitive case databases and cross-reference victim information across jurisdictions. OpenBotAuth provides non-repudiable identity proof suitable for legal evidence while protecting database access from unauthorized actors.

### Public Health Research

Epidemiological research agents authenticate to access COVID tracking data, hospitalization rates, and vaccination statistics. Health agencies can grant verified researchers full dataset access while limiting anonymous access to protect patient privacy.

### Content Moderation

Platform safety agents authenticate to scan for harmful content, copyright violations, and policy breaches. Content platforms can grant verified moderation bots elevated access to private user data while maintaining strict controls on unverified automated access.


# FAQ

To add questions in this section, please open a Github Issue.

#### Why Web Bot Auth? Not OAuth?

Oauth introduces complexity in implementation. There's an open issue on Thibault's [Github](https://github.com/thibmeu/http-message-signatures-directory/issues/13) discussing this. There another cloudflare project, [OpenPubkey](https://www.bastionzero.com/openpubkey) which works with OIDC and they use an MPC. OpenBotAuth intends to avoid any complex cryptography.&#x20;

#### Why Can't I Sign-in with Facebook/X?

Agents live and die in developer's terminal. Almost all developers have a Github Account and understand the value of hosted services. Developers remain the primary agent builders. Hence it was a no brainer to allow Github Profiles to verify first. We will consider expanding to other verification methods.


# Overview

This sequence diagram shows:

<figure><img src="/files/mUq7sA1R20fcI2rkLhhN" alt=""><figcaption></figcaption></figure>

* **Registration Phase**: Bot operator publishes signature-agent card to OpenBotRegistry
* **Request Phase**: Bot signs HTTP request with private key; origin server queries OpenBotRegistry directly for the signature-agent card to get public key
* **Verification**: Origin server validates signature using public key from agent card, then policy engine evaluates intent and scopes


# OpenBotRegistry

Github for AI Agent Identities

Developers can host all their agents with unique identities, tied to their Github profile, on the registry and use it to log agent activities in public.

OpenBotRegistry binds your Github identity to a public key, following [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) configuration. After logging in with Github's authorization, developers may generate a public key, list their agents in a public directory and log agent HTTP messages.&#x20;

The registry hosts a separate Signature Agent Card/JWKS on a dedicated link, unique to your agent. This card is signed by OpenBotRegistry as a certificate.&#x20;

The registry operates a trusted model in favor of time to market. It extends the [Signature Agent Card ](https://thibmeu.github.io/http-message-signatures-directory/draft-meunier-webbotauth-registry.html#name-signature-agent-card)to include verified Github profile details.&#x20;


# Overview

Crawlers (AI agents) register with OpenBotRegistry to establish their cryptographic identity. This identity enables them to sign HTTP requests using RFC 9421 HTTP Message Signatures, which publishers can then verify.

## Why Register?

When your crawler is registered with OpenBotRegistry:

* **Publishers can verify your identity** - Your signed requests prove you are who you claim to be
* **Access to premium content** - Publishers can grant access to registered, verified crawlers
* **Pay-per-crawl support** - Enable monetized content access through the Web Bot Auth payment protocol
* **Trust building** - Build reputation as a legitimate AI agent

## Registration Process

### 1. Generate Ed25519 Key Pair

Use the [@openbotauth/registry-signer](/crawlers/registry-signer) package to generate an Ed25519 key pair for signing requests.

```bash
npm install @openbotauth/registry-signer
```

```typescript
import { generateEd25519KeyPair, publicKeyToJwk } from '@openbotauth/registry-signer';

const keyPair = await generateEd25519KeyPair();
const jwk = await publicKeyToJwk(keyPair.publicKey);

console.log('JWK:', JSON.stringify(jwk, null, 2));
```

### 2. Create JWKS Endpoint

Host your public key as a JSON Web Key Set (JWKS) at a well-known URL:

```json
{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "your-base64url-encoded-public-key",
      "kid": "your-key-id"
    }
  ]
}
```

Example URL: `https://your-domain.com/.well-known/jwks.json`

### 3. Register with OpenBotRegistry

Submit your crawler registration at [OpenBotRegistry Portal](https://registry.openbotauth.org):

| Field             | Description                           |
| ----------------- | ------------------------------------- |
| **Crawler Name**  | Human-readable name for your AI agent |
| **Organization**  | Your company or project name          |
| **JWKS URL**      | URL where your public key is hosted   |
| **Contact Email** | For verification and communication    |

### 4. Sign Your Requests

Use your private key to sign HTTP requests according to RFC 9421:

```typescript
// Pseudo-code for request signing
const signature = sign(privateKey, {
  method: 'GET',
  url: 'https://publisher.example.com/content',
  headers: {
    'host': 'publisher.example.com',
    'date': new Date().toISOString()
  }
});

// Add signature headers to your request
headers['Signature-Input'] = '...';
headers['Signature'] = '...';
```

## Key Management Best Practices

* **Keep private keys secure** - Never expose your private key in client-side code or public repositories
* **Rotate keys regularly** - Update your JWKS and re-register with a new key periodically
* **Use key IDs** - Include a unique `kid` in your JWK for key identification
* **Monitor usage** - Track which requests are being made with your identity

## Verification Flow

When a publisher receives your signed request:

```
1. Extract signature headers from request
2. Fetch your JWKS from the registered URL
3. Verify the signature using your public key
4. Check that the key belongs to a registered crawler
5. Make policy decision (allow, deny, or require payment)
```

## Related Resources

* [registry-signer Package](/crawlers/registry-signer) - Generate Ed25519 keys for signing
* [Architecture Overview](/architecture/overview) - How OpenBotAuth works
* [RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421) - HTTP Message Signatures specification


# registry-signer Package

A TypeScript/JavaScript library for generating Ed25519 key pairs and formatting them as JSON Web Keys (JWK) for use with OpenBotRegistry.

## Installation

```bash
npm install @openbotauth/registry-signer
```

## Quick Start

```typescript
import {
  generateEd25519KeyPair,
  publicKeyToJwk,
  privateKeyToJwk,
  createJwks
} from '@openbotauth/registry-signer';

// Generate a new Ed25519 key pair
const keyPair = await generateEd25519KeyPair();

// Export public key as JWK (for registration)
const publicJwk = await publicKeyToJwk(keyPair.publicKey, 'my-key-id');

// Export private key as JWK (for signing - keep secure!)
const privateJwk = await privateKeyToJwk(keyPair.privateKey, 'my-key-id');

// Create a JWKS containing your public key
const jwks = await createJwks([publicJwk]);
console.log(JSON.stringify(jwks, null, 2));
```

## API Reference

### Key Generation

#### `generateEd25519KeyPair()`

Generates a new Ed25519 key pair using the Web Crypto API.

```typescript
const keyPair = await generateEd25519KeyPair();
// Returns: CryptoKeyPair { publicKey, privateKey }
```

### JWK Conversion

#### `publicKeyToJwk(publicKey, keyId?)`

Converts a CryptoKey public key to JWK format.

```typescript
const jwk = await publicKeyToJwk(keyPair.publicKey, 'optional-key-id');
// Returns:
// {
//   kty: 'OKP',
//   crv: 'Ed25519',
//   x: 'base64url-encoded-public-key',
//   kid: 'optional-key-id'
// }
```

#### `privateKeyToJwk(privateKey, keyId?)`

Converts a CryptoKey private key to JWK format.

```typescript
const jwk = await privateKeyToJwk(keyPair.privateKey, 'optional-key-id');
// Returns:
// {
//   kty: 'OKP',
//   crv: 'Ed25519',
//   x: 'base64url-encoded-public-key',
//   d: 'base64url-encoded-private-key',
//   kid: 'optional-key-id'
// }
```

### JWKS Creation

#### `createJwks(keys)`

Creates a JSON Web Key Set from an array of JWKs.

```typescript
const jwks = await createJwks([publicJwk]);
// Returns:
// {
//   keys: [
//     { kty: 'OKP', crv: 'Ed25519', x: '...', kid: '...' }
//   ]
// }
```

### Base64 Utilities

#### `base64UrlEncode(data)`

Encodes a Uint8Array to base64url format (no padding).

```typescript
const encoded = base64UrlEncode(new Uint8Array([1, 2, 3]));
```

#### `base64UrlDecode(str)`

Decodes a base64url string to Uint8Array.

```typescript
const decoded = base64UrlDecode('AQID');
```

## Hosting Your JWKS

Once you've generated your key pair, host the JWKS at a publicly accessible URL:

### Option 1: Static File

Save the JWKS output to a file and serve it:

```bash
# Generate and save JWKS
node -e "
const { generateEd25519KeyPair, publicKeyToJwk, createJwks } = require('@openbotauth/registry-signer');
(async () => {
  const keyPair = await generateEd25519KeyPair();
  const jwk = await publicKeyToJwk(keyPair.publicKey, 'key-1');
  const jwks = await createJwks([jwk]);
  console.log(JSON.stringify(jwks, null, 2));
})();
" > .well-known/jwks.json
```

### Option 2: Dynamic Endpoint

Serve the JWKS from your application:

```typescript
// Express.js example
app.get('/.well-known/jwks.json', (req, res) => {
  res.json({
    keys: [
      {
        kty: 'OKP',
        crv: 'Ed25519',
        x: process.env.PUBLIC_KEY_X,
        kid: 'crawler-key-1'
      }
    ]
  });
});
```

## Security Considerations

* **Never expose your private key** - The private key (`d` parameter) should never be shared or included in your JWKS
* **Store private keys securely** - Use environment variables, secret managers, or hardware security modules
* **Use unique key IDs** - Include a `kid` to identify keys during rotation
* **Rotate keys periodically** - Generate new keys and update your registration

## TypeScript Support

Full TypeScript definitions are included. The package exports proper types for all functions and return values.

## Browser Compatibility

This package uses the Web Crypto API and works in:

* Node.js 18+
* Modern browsers (Chrome, Firefox, Safari, Edge)
* Deno
* Cloudflare Workers

## Source Code

GitHub: [OpenBotAuth/openbotauth](https://github.com/OpenBotAuth/openbotauth/tree/main/packages/registry-signer)

npm: [@openbotauth/registry-signer](https://www.npmjs.com/package/@openbotauth/registry-signer)


# Overview

OpenBotAuth provides official SDKs for integrating signature verification directly into your applications. Both SDKs follow the same design principles:

* **No local cryptography** - All verification is delegated to the OpenBotAuth verifier service
* **Middleware support** - Drop-in middleware for popular frameworks
* **Observe mode** - Non-blocking verification for gradual rollout
* **Security-first** - Sensitive headers are never forwarded

## Available SDKs

| SDK                                  | Package                                                                                    | Frameworks                |
| ------------------------------------ | ------------------------------------------------------------------------------------------ | ------------------------- |
| [Node.js / TypeScript](/sdks/nodejs) | [@openbotauth/verifier-client](https://www.npmjs.com/package/@openbotauth/verifier-client) | Express, Next.js          |
| [Python](/sdks/python)               | [openbotauth-verifier](https://pypi.org/project/openbotauth-verifier/)                     | FastAPI, Flask, Starlette |

## When to Use SDKs

Use the SDK approach when:

* You want fine-grained control over verification logic
* You need to integrate with your existing authentication/authorization
* You want to make policy decisions in your application code
* You're building a Node.js or Python application

## Alternative: Proxy

If you prefer a zero-code approach or use a different language/framework, consider the [OpenBotAuth Proxy](/proxy/proxy) which sits in front of any HTTP backend.

## Core Concepts

### Verification Flow

```
1. Extract RFC 9421 signature headers from request
2. Send headers to verifier service
3. Receive verification result (verified, agent info, or error)
4. Make policy decision in your application
```

### Middleware Modes

Both SDKs support two middleware modes:

| Mode                | Behavior                                                           |
| ------------------- | ------------------------------------------------------------------ |
| `observe` (default) | All requests pass through; verification result attached to request |
| `require-verified`  | Protected paths return 401 if verification fails                   |

### Request State

After middleware processing, verification state is attached to the request:

```javascript
// Node.js
req.oba.signed    // boolean - request had signature headers
req.oba.result    // VerificationResult or null
```

```python
# Python
request.state.oba.signed    # bool - request had signature headers
request.state.oba.result    # VerificationResult or None
```


# Node.js / TypeScript

The official Node.js SDK for OpenBotAuth signature verification.

**Package:** [@openbotauth/verifier-client](https://www.npmjs.com/package/@openbotauth/verifier-client)

## Installation

```bash
# npm
npm install @openbotauth/verifier-client

# pnpm
pnpm add @openbotauth/verifier-client

# yarn
yarn add @openbotauth/verifier-client
```

**Requirements:** Node.js >= 18.0.0

## Quick Start

### Express Middleware

The fastest way to integrate is with the Express middleware:

```typescript
import express from 'express';
import { openBotAuthMiddleware } from '@openbotauth/verifier-client/express';

const app = express();

// Add middleware (observe mode by default)
app.use(openBotAuthMiddleware());

app.get('/api/content', (req, res) => {
  const oba = (req as any).oba;

  if (oba.signed && oba.result?.verified) {
    // Verified bot - full access
    res.json({
      content: 'Full article content...',
      agent: oba.result.agent
    });
  } else {
    // Anonymous or unverified - limited access
    res.json({
      content: 'Article preview...',
      upgrade: 'Sign requests for full access'
    });
  }
});

app.listen(3000);
```

### Next.js App Router

For Next.js Server Components and Route Handlers:

```typescript
// app/api/content/route.ts
import { NextRequest, NextResponse } from 'next/server';
import {
  VerifierClient,
  buildVerifyRequestForNext,
  hasSignatureHeaders
} from '@openbotauth/verifier-client';

const client = new VerifierClient();

export async function GET(request: NextRequest) {
  // Check if request has signature headers
  if (!hasSignatureHeaders(request.headers)) {
    return NextResponse.json({ content: 'Preview only' });
  }

  // Build verification request from Next.js request
  const verifyRequest = buildVerifyRequestForNext(request);
  const result = await client.verify(verifyRequest);

  if (result.verified) {
    return NextResponse.json({
      content: 'Full content',
      agent: result.agent
    });
  }

  return NextResponse.json(
    { error: 'Verification failed', reason: result.error },
    { status: 401 }
  );
}
```

### Direct Client Usage

For custom integrations:

```typescript
import { VerifierClient, VerificationRequest } from '@openbotauth/verifier-client';

const client = new VerifierClient({
  verifierUrl: 'https://verifier.openbotauth.org/verify', // default
  timeoutMs: 5000 // default
});

const request: VerificationRequest = {
  method: 'GET',
  url: 'https://example.com/api/content',
  headers: {
    'host': 'example.com',
    'signature-input': 'sig=("@method" "@target-uri" "host");created=1699900000;keyid="key-1";alg="ed25519"',
    'signature': 'sig=:base64signature...:',
    'signature-agent': 'https://registry.openbotauth.org/jwks/mybot.json'
  }
};

const result = await client.verify(request);

if (result.verified) {
  console.log('Verified agent:', result.agent?.client_name);
  console.log('Key ID:', result.kid);
} else {
  console.log('Verification failed:', result.error);
}
```

## API Reference

### VerifierClient

Main client class for calling the verifier service.

```typescript
interface VerifierClientOptions {
  verifierUrl?: string;   // Default: 'https://verifier.openbotauth.org/verify'
  timeoutMs?: number;     // Default: 5000
}

class VerifierClient {
  constructor(options?: VerifierClientOptions);
  verify(request: VerificationRequest): Promise<VerificationResult>;
}
```

### VerificationRequest

Request object sent to the verifier:

```typescript
interface VerificationRequest {
  method: string;           // HTTP method (GET, POST, etc.)
  url: string;              // Full request URL
  headers: Record<string, string>;  // Request headers
  body?: string;            // Request body (for POST/PUT)
}
```

### VerificationResult

Response from the verifier:

```typescript
interface VerificationResult {
  verified: boolean;        // Whether signature is valid
  agent?: {                 // Agent info (if verified)
    client_name: string;
    client_uri?: string;
    // ... other agent metadata
  };
  kid?: string;             // Key ID used for signing
  jwks_url?: string;        // JWKS URL for the agent
  error?: string;           // Error message (if failed)
  created?: number;         // Signature creation timestamp
  expires?: number;         // Signature expiration timestamp
}
```

### Middleware Options

```typescript
interface MiddlewareOptions {
  verifierUrl?: string;     // Verifier service URL
  mode?: 'observe' | 'require-verified';  // Default: 'observe'
  attachProperty?: string;  // Request property name (default: 'oba')
  timeoutMs?: number;       // Verification timeout
}
```

### OBAState

State attached to requests by middleware:

```typescript
interface OBAState {
  signed: boolean;          // Request had signature headers
  result?: VerificationResult;  // Verification result (null if not signed)
}
```

## Header Utilities

Utility functions for working with RFC 9421 headers:

```typescript
import {
  hasSignatureHeaders,
  parseCoveredHeaders,
  extractForwardedHeaders
} from '@openbotauth/verifier-client';

// Check if request has signature headers
const hasSig = hasSignatureHeaders(request.headers);

// Parse covered headers from Signature-Input
const covered = parseCoveredHeaders(signatureInput);
// Returns: ['@method', '@target-uri', 'host', ...]

// Extract only safe headers for forwarding to verifier
const safeHeaders = extractForwardedHeaders(
  request.headers,
  coveredHeaders
);
```

## Security

### Sensitive Headers

The SDK automatically blocks sensitive headers from being forwarded to the verifier:

* `cookie`
* `authorization`
* `proxy-authorization`
* `www-authenticate`

If a `Signature-Input` references any of these headers, the request will be rejected.

### Timeout Handling

All verification requests have a configurable timeout (default 5 seconds). On timeout, verification is treated as failed.

## Middleware Modes

### Observe Mode (Default)

All requests pass through regardless of verification status. Use this for:

* Logging and analytics
* Gradual rollout
* A/B testing between verified and unverified access

```typescript
app.use(openBotAuthMiddleware({ mode: 'observe' }));
```

### Require-Verified Mode

Protected paths return 401 for unsigned or failed verification:

```typescript
app.use(openBotAuthMiddleware({
  mode: 'require-verified',
  protectedPaths: ['/api/premium', '/api/content']
}));
```

## Error Handling

```typescript
try {
  const result = await client.verify(request);
  if (!result.verified) {
    // Verification failed (invalid signature, expired, etc.)
    console.log('Reason:', result.error);
  }
} catch (error) {
  // Network error, timeout, or verifier service unavailable
  console.error('Verification error:', error);
}
```

## TypeScript Support

The package is written in TypeScript and exports all types:

```typescript
import type {
  VerifierClientOptions,
  VerificationRequest,
  VerificationResult,
  MiddlewareOptions,
  OBAState
} from '@openbotauth/verifier-client';
```

## Examples

### Express with Custom Verifier

```typescript
import express from 'express';
import { openBotAuthMiddleware } from '@openbotauth/verifier-client/express';

const app = express();

app.use(openBotAuthMiddleware({
  verifierUrl: 'https://your-verifier.example.com/verify',
  mode: 'observe',
  timeoutMs: 3000
}));

app.get('/api/data', (req, res) => {
  const { signed, result } = (req as any).oba;

  res.json({
    authenticated: signed && result?.verified,
    agent: result?.agent?.client_name || 'anonymous'
  });
});
```

### Next.js with Body Verification

```typescript
// For POST requests that need body in signature
import { buildVerifyRequestForNextWithBody } from '@openbotauth/verifier-client';

export async function POST(request: NextRequest) {
  const body = await request.text();
  const verifyRequest = buildVerifyRequestForNextWithBody(request, body);
  const result = await client.verify(verifyRequest);
  // ...
}
```

## Links

* **npm:** <https://www.npmjs.com/package/@openbotauth/verifier-client>
* **GitHub:** <https://github.com/OpenBotAuth/openbotauth>


# Python

The official Python SDK for OpenBotAuth signature verification.

**Package:** [openbotauth-verifier](https://pypi.org/project/openbotauth-verifier/)

## Installation

```bash
# Core package
pip install openbotauth-verifier

# With FastAPI/Starlette support
pip install openbotauth-verifier[fastapi]

# With Flask support
pip install openbotauth-verifier[flask]

# All extras
pip install openbotauth-verifier[all]
```

**Requirements:** Python >= 3.10

## Quick Start

### FastAPI Middleware

The fastest way to integrate with FastAPI:

```python
from fastapi import FastAPI, Request
from openbotauth_verifier import OpenBotAuthASGIMiddleware

app = FastAPI()

# Add middleware (observe mode by default)
app.add_middleware(OpenBotAuthASGIMiddleware)

@app.get("/api/content")
async def get_content(request: Request):
    oba = request.state.oba

    if oba.signed and oba.result.verified:
        # Verified bot - full access
        return {
            "content": "Full article content...",
            "agent": oba.result.agent
        }
    else:
        # Anonymous or unverified - limited access
        return {
            "content": "Article preview...",
            "upgrade": "Sign requests for full access"
        }
```

### Flask Middleware

For Flask applications:

```python
from flask import Flask, request, g
from openbotauth_verifier.middleware.wsgi import OpenBotAuthWSGIMiddleware

app = Flask(__name__)

# Wrap WSGI app
app.wsgi_app = OpenBotAuthWSGIMiddleware(app.wsgi_app)

@app.before_request
def load_oba():
    """Load OBA state from WSGI environ into Flask's g object"""
    g.oba = request.environ.get("openbotauth.oba")

@app.route("/api/content")
def get_content():
    if g.oba and g.oba.signed and g.oba.result.verified:
        return {
            "content": "Full article content...",
            "agent": g.oba.result.agent
        }
    return {
        "content": "Article preview...",
        "upgrade": "Sign requests for full access"
    }
```

### Direct Client Usage

For custom integrations:

```python
from openbotauth_verifier import VerifierClient

client = VerifierClient(
    verifier_url="https://verifier.openbotauth.org/verify",  # default
    timeout_s=5.0  # default
)

# Async usage
result = await client.verify(
    method="GET",
    url="https://example.com/api/content",
    headers={
        "host": "example.com",
        "signature-input": 'sig=("@method" "@target-uri" "host");created=1699900000;keyid="key-1";alg="ed25519"',
        "signature": "sig=:base64signature...:",
        "signature-agent": "https://registry.openbotauth.org/jwks/mybot.json"
    }
)

if result.verified:
    print(f"Verified agent: {result.agent['client_name']}")
else:
    print(f"Verification failed: {result.error}")

# Sync usage
result = client.verify_sync(
    method="GET",
    url="https://example.com/api/content",
    headers={...}
)
```

## API Reference

### VerifierClient

Main client class for calling the verifier service.

```python
class VerifierClient:
    def __init__(
        self,
        verifier_url: str = "https://verifier.openbotauth.org/verify",
        timeout_s: float = 5.0,
    ):
        ...

    async def verify(
        self,
        method: str,
        url: str,
        headers: dict[str, str],
        body: str | None = None
    ) -> VerificationResult:
        """Async verification"""
        ...

    def verify_sync(
        self,
        method: str,
        url: str,
        headers: dict[str, str],
        body: str | None = None
    ) -> VerificationResult:
        """Synchronous verification"""
        ...
```

### VerificationRequest

Request data model:

```python
from dataclasses import dataclass

@dataclass
class VerificationRequest:
    method: str              # HTTP method (GET, POST, etc.)
    url: str                 # Full request URL
    headers: dict[str, str]  # Request headers
    body: str | None = None  # Request body (for POST/PUT)
```

### VerificationResult

Response from the verifier:

```python
from dataclasses import dataclass
from typing import Any

@dataclass
class VerificationResult:
    verified: bool                      # Whether signature is valid
    agent: dict[str, Any] | None = None # Agent info (if verified)
    error: str | None = None            # Error message (if failed)
    created: int | None = None          # Signature creation timestamp
    expires: int | None = None          # Signature expiration timestamp
```

### OBAState

State attached to requests by middleware:

```python
@dataclass
class OBAState:
    signed: bool                         # Request had signature headers
    result: VerificationResult | None = None  # Verification result
```

## Middleware Configuration

### ASGI Middleware (FastAPI/Starlette)

```python
from openbotauth_verifier import OpenBotAuthASGIMiddleware

app.add_middleware(
    OpenBotAuthASGIMiddleware,
    verifier_url="https://verifier.openbotauth.org/verify",  # optional
    require_verified=False,  # True to enforce verification
    timeout_s=5.0    # optional
)
```

### WSGI Middleware (Flask)

```python
from openbotauth_verifier.middleware.wsgi import OpenBotAuthWSGIMiddleware

app.wsgi_app = OpenBotAuthWSGIMiddleware(
    app.wsgi_app,
    verifier_url="https://verifier.openbotauth.org/verify",  # optional
    require_verified=False,  # True to enforce verification
    timeout_s=5.0    # optional
)
```

## Header Utilities

Utility functions for working with RFC 9421 headers:

```python
from openbotauth_verifier import (
    has_signature_headers,
    parse_covered_headers,
    extract_forwarded_headers
)

# Check if request has signature headers
has_sig = has_signature_headers(headers)

# Parse covered headers from Signature-Input
covered = parse_covered_headers(signature_input)
# Returns: ['@method', '@target-uri', 'host', ...]

# Extract only safe headers for forwarding to verifier
safe_headers = extract_forwarded_headers(headers, covered)
```

## Security

### Sensitive Headers

The SDK automatically blocks sensitive headers from being forwarded to the verifier:

* `cookie`
* `authorization`
* `proxy-authorization`
* `www-authenticate`

If a `Signature-Input` references any of these headers, a `ValueError` is raised.

### Timeout Handling

All verification requests have a configurable timeout (default 5 seconds). On timeout, verification is treated as failed.

## Middleware Modes

### Observe Mode (Default)

All requests pass through regardless of verification status:

```python
app.add_middleware(OpenBotAuthASGIMiddleware, require_verified=False)
```

### Require-Verified Mode

Returns 401 for unsigned or failed verification:

```python
app.add_middleware(OpenBotAuthASGIMiddleware, require_verified=True)
```

## Error Handling

```python
from openbotauth_verifier import VerifierClient
from httpx import HTTPError

client = VerifierClient()

try:
    result = await client.verify(method="GET", url="...", headers={...})
    if not result.verified:
        # Verification failed (invalid signature, expired, etc.)
        print(f"Reason: {result.error}")
except HTTPError as e:
    # Network error, timeout, or verifier service unavailable
    print(f"Verification error: {e}")
```

## Type Hints

The package includes full type hints and is compatible with mypy:

```python
from openbotauth_verifier import (
    VerifierClient,
    VerificationRequest,
    VerificationResult,
    OBAState
)
```

## Examples

### FastAPI with Custom Verifier

```python
from fastapi import FastAPI, Request, HTTPException
from openbotauth_verifier import OpenBotAuthASGIMiddleware

app = FastAPI()

app.add_middleware(
    OpenBotAuthASGIMiddleware,
    verifier_url="https://your-verifier.example.com/verify",
    require_verified=False,
    timeout_s=3.0
)

@app.get("/api/data")
async def get_data(request: Request):
    oba = request.state.oba

    return {
        "authenticated": oba.signed and oba.result.verified if oba.result else False,
        "agent": oba.result.agent.get("client_name") if oba.result and oba.result.agent else "anonymous"
    }
```

### Starlette Direct Usage

```python
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import JSONResponse
from openbotauth_verifier import OpenBotAuthASGIMiddleware

async def homepage(request):
    oba = request.state.oba
    if oba.signed and oba.result.verified:
        return JSONResponse({"message": "Hello verified bot!"})
    return JSONResponse({"message": "Hello anonymous!"})

app = Starlette(routes=[Route("/", homepage)])
app = OpenBotAuthASGIMiddleware(app)
```

### Flask with Protected Routes

```python
from functools import wraps
from flask import Flask, request, g, jsonify
from openbotauth_verifier.middleware.wsgi import OpenBotAuthWSGIMiddleware

app = Flask(__name__)
app.wsgi_app = OpenBotAuthWSGIMiddleware(app.wsgi_app)

@app.before_request
def load_oba():
    g.oba = request.environ.get("openbotauth.oba")

def require_verified(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not g.oba or not g.oba.signed or not g.oba.result.verified:
            return jsonify({"error": "Verification required"}), 401
        return f(*args, **kwargs)
    return decorated

@app.route("/api/public")
def public():
    return jsonify({"message": "Public content"})

@app.route("/api/protected")
@require_verified
def protected():
    return jsonify({
        "message": "Protected content",
        "agent": g.oba.result.agent
    })
```

## Dependencies

| Package   | Version   | Purpose                      |
| --------- | --------- | ---------------------------- |
| httpx     | >= 0.25.0 | HTTP client (async & sync)   |
| fastapi   | >= 0.100  | FastAPI framework (optional) |
| starlette | >= 0.27   | ASGI framework (optional)    |
| flask     | >= 2.0    | Flask framework (optional)   |

## Links

* **PyPI:** <https://pypi.org/project/openbotauth-verifier/>
* **GitHub:** <https://github.com/OpenBotAuth/openbotauth>


# Overview

The OpenBotAuth Proxy is a reverse proxy that sits in front of your backend and automatically verifies RFC 9421 HTTP message signatures from AI bots.

## Why Use the Proxy?

* **Zero-code integration** - No changes to your application code
* **Language agnostic** - Works with any HTTP backend (Node.js, Python, Go, PHP, etc.)
* **Easy deployment** - Available as npm package, Docker image, or GitHub Container Registry
* **Gradual rollout** - Observe mode lets you monitor before enforcing

## How It Works

```
┌────────────┐      ┌────────────┐      ┌──────────────┐
│   AI Bot   │─────→│   Proxy    │─────→│   Verifier   │
│  (Signed)  │      │  (OBA)     │      │   Service    │
└────────────┘      └────────────┘      └──────────────┘
                           │
                           ↓
                    ┌──────────────┐
                    │   Backend    │
                    │  (Your App)  │
                    └──────────────┘
```

1. AI bot sends a signed HTTP request to the proxy
2. Proxy extracts RFC 9421 signature headers
3. Proxy calls the verifier service to validate the signature
4. Proxy adds `X-OBAuth-*` headers with verification results
5. Proxy forwards the request to your backend
6. Your backend reads the headers to make access decisions

## Installation Options

| Method                  | Install                                             | Best For                          |
| ----------------------- | --------------------------------------------------- | --------------------------------- |
| [NPM / CLI](/proxy/npm) | `npx @openbotauth/proxy`                            | Quick start, Node.js environments |
| [Docker](/proxy/docker) | `docker pull hammadtariq/openbotauth-proxy`         | Containerized deployments         |
| [GHCR](/proxy/ghcr)     | `docker pull ghcr.io/openbotauth/openbotauth-proxy` | GitHub-integrated workflows       |

## Headers Injected

The proxy adds these headers to every request forwarded to your backend:

| Header              | Description                                     | Example                                          |
| ------------------- | ----------------------------------------------- | ------------------------------------------------ |
| `X-OBAuth-Verified` | `true` if signature verified, `false` otherwise | `true`                                           |
| `X-OBAuth-Agent`    | Bot's client name (if verified)                 | `MyAIBot`                                        |
| `X-OBAuth-JWKS-URL` | Bot's JWKS URL                                  | `https://registry.openbotauth.org/jwks/bot.json` |
| `X-OBAuth-Kid`      | Key ID used for signing                         | `key-abc123`                                     |
| `X-OBAuth-Error`    | Error message (on failure)                      | `signature_expired`                              |

## Modes

### Observe Mode (Default)

All requests pass through to backend. Use for:

* Logging and analytics
* Gradual rollout
* Testing before enforcement

```bash
OBA_MODE=observe npx @openbotauth/proxy
```

### Require-Verified Mode

Protected paths return 401 if verification fails:

```bash
OBA_MODE=require-verified \
OBA_PROTECTED_PATHS=/api,/content \
npx @openbotauth/proxy
```

## Configuration

All configuration is via environment variables:

| Variable              | Default                                   | Description                                  |
| --------------------- | ----------------------------------------- | -------------------------------------------- |
| `PORT`                | `8088`                                    | Proxy listen port                            |
| `UPSTREAM_URL`        | `http://localhost:8080`                   | Backend server URL                           |
| `OBA_VERIFIER_URL`    | `https://verifier.openbotauth.org/verify` | Verifier service endpoint                    |
| `OBA_MODE`            | `observe`                                 | `observe` or `require-verified`              |
| `OBA_TIMEOUT_MS`      | `5000`                                    | Verifier request timeout                     |
| `OBA_PROTECTED_PATHS` | `/protected`                              | Comma-separated paths requiring verification |

## Backend Integration

Your backend reads the `X-OBAuth-*` headers to make access decisions:

### Node.js / Express

```javascript
app.get('/api/content', (req, res) => {
  if (req.headers['x-obauth-verified'] === 'true') {
    const agent = req.headers['x-obauth-agent'];
    res.json({ content: 'Full access', bot: agent });
  } else {
    res.json({ content: 'Limited preview' });
  }
});
```

### Python / Flask

```python
@app.route('/api/content')
def content():
    if request.headers.get('X-OBAuth-Verified') == 'true':
        agent = request.headers.get('X-OBAuth-Agent')
        return {'content': 'Full access', 'bot': agent}
    return {'content': 'Limited preview'}
```

### Go

```go
func handler(w http.ResponseWriter, r *http.Request) {
    if r.Header.Get("X-OBAuth-Verified") == "true" {
        agent := r.Header.Get("X-OBAuth-Agent")
        json.NewEncoder(w).Encode(map[string]string{
            "content": "Full access",
            "bot": agent,
        })
    } else {
        json.NewEncoder(w).Encode(map[string]string{
            "content": "Limited preview",
        })
    }
}
```

### PHP

```php
$verified = $_SERVER['HTTP_X_OBAUTH_VERIFIED'] ?? 'false';
$agent = $_SERVER['HTTP_X_OBAUTH_AGENT'] ?? '';

if ($verified === 'true') {
    echo json_encode(['content' => 'Full access', 'bot' => $agent]);
} else {
    echo json_encode(['content' => 'Limited preview']);
}
```

## Health Check

The proxy exposes a health check endpoint:

```bash
curl http://localhost:8088/.well-known/health
```

Response:

```json
{
  "status": "ok",
  "service": "openbotauth-proxy",
  "upstream": "http://localhost:8080",
  "verifier": "https://verifier.openbotauth.org/verify",
  "mode": "observe"
}
```

## When to Use Proxy vs SDK

| Use Proxy When                   | Use SDK When                 |
| -------------------------------- | ---------------------------- |
| Backend is not Node.js or Python | Need fine-grained control    |
| Want zero-code integration       | Integrating with auth system |
| Need language-agnostic solution  | Building custom middleware   |
| Prefer infrastructure approach   | Want to avoid extra hop      |

## Links

* **npm:** <https://www.npmjs.com/package/@openbotauth/proxy>
* **Docker Hub:** <https://hub.docker.com/r/hammadtariq/openbotauth-proxy>
* **GitHub Container Registry:** <https://github.com/OpenBotAuth/openbotauth/pkgs/container/openbotauth-proxy>
* **GitHub:** <https://github.com/OpenBotAuth/openbotauth>


# NPM / CLI

Run the OpenBotAuth Proxy using npm or npx.

**Package:** [@openbotauth/proxy](https://www.npmjs.com/package/@openbotauth/proxy)

## Quick Start

```bash
# Run without installing (recommended)
npx @openbotauth/proxy

# Or install globally
npm install -g @openbotauth/proxy
openbotauth-proxy
```

**Requirements:** Node.js >= 18.0.0

## Installation Methods

### npx (No Install)

Run directly without installing:

```bash
npx @openbotauth/proxy
```

### Global Install

```bash
# npm
npm install -g @openbotauth/proxy

# pnpm
pnpm add -g @openbotauth/proxy

# yarn
yarn global add @openbotauth/proxy
```

After global install, run with:

```bash
openbotauth-proxy
# or
oba-proxy
```

### Local Install

Add to your project:

```bash
npm install @openbotauth/proxy
```

Then run via npm scripts in `package.json`:

```json
{
  "scripts": {
    "proxy": "openbotauth-proxy"
  }
}
```

## Configuration

All configuration is via environment variables:

| Variable              | Default                                   | Description                                  |
| --------------------- | ----------------------------------------- | -------------------------------------------- |
| `PORT`                | `8088`                                    | Proxy listen port                            |
| `UPSTREAM_URL`        | `http://localhost:8080`                   | Backend server URL                           |
| `OBA_VERIFIER_URL`    | `https://verifier.openbotauth.org/verify` | Verifier service endpoint                    |
| `OBA_MODE`            | `observe`                                 | `observe` or `require-verified`              |
| `OBA_TIMEOUT_MS`      | `5000`                                    | Verifier request timeout (ms)                |
| `OBA_PROTECTED_PATHS` | `/protected`                              | Comma-separated paths requiring verification |

## Usage Examples

### Basic Usage

Proxy requests from port 8088 to localhost:8080:

```bash
npx @openbotauth/proxy
```

### Custom Backend

Proxy to a different backend:

```bash
UPSTREAM_URL=http://localhost:3000 npx @openbotauth/proxy
```

### Custom Port

Run on a different port:

```bash
PORT=9000 npx @openbotauth/proxy
```

### Require Verification

Enforce verification on all paths:

```bash
OBA_MODE=require-verified npx @openbotauth/proxy
```

### Protected Paths Only

Require verification only on specific paths:

```bash
OBA_MODE=require-verified \
OBA_PROTECTED_PATHS=/api,/content,/premium \
npx @openbotauth/proxy
```

### Full Configuration

```bash
PORT=8088 \
UPSTREAM_URL=http://localhost:3000 \
OBA_VERIFIER_URL=https://verifier.openbotauth.org/verify \
OBA_MODE=require-verified \
OBA_TIMEOUT_MS=3000 \
OBA_PROTECTED_PATHS=/api/v1,/protected \
npx @openbotauth/proxy
```

## Process Managers

### PM2

```javascript
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'oba-proxy',
    script: 'npx',
    args: '@openbotauth/proxy',
    env: {
      PORT: 8088,
      UPSTREAM_URL: 'http://localhost:3000',
      OBA_MODE: 'observe'
    }
  }]
};
```

```bash
pm2 start ecosystem.config.js
```

### systemd

```ini
# /etc/systemd/system/oba-proxy.service
[Unit]
Description=OpenBotAuth Proxy
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/oba-proxy
Environment=PORT=8088
Environment=UPSTREAM_URL=http://localhost:3000
Environment=OBA_MODE=observe
ExecStart=/usr/bin/npx @openbotauth/proxy
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

```bash
sudo systemctl enable oba-proxy
sudo systemctl start oba-proxy
```

## Programmatic Usage

You can also use the proxy programmatically in your Node.js application:

```typescript
import { createProxyServer } from '@openbotauth/proxy';

const server = createProxyServer({
  port: 8088,
  upstream: 'http://localhost:3000',
  verifierUrl: 'https://verifier.openbotauth.org/verify',
  mode: 'observe',
  timeoutMs: 5000,
  protectedPaths: ['/api', '/protected']
});

server.listen();
```

## Health Check

Verify the proxy is running:

```bash
curl http://localhost:8088/.well-known/health
```

Response:

```json
{
  "status": "ok",
  "service": "openbotauth-proxy",
  "upstream": "http://localhost:8080",
  "verifier": "https://verifier.openbotauth.org/verify",
  "mode": "observe"
}
```

## Logging

The proxy logs to stdout:

```
[OBA] Proxy started on port 8088
[OBA] Upstream: http://localhost:3000
[OBA] Mode: observe
[OBA] GET /api/content -> verified (agent: MyBot)
[OBA] GET /public -> unsigned
```

## Troubleshooting

### Proxy won't start

Check that the port is available:

```bash
lsof -i :8088
```

### Verification always fails

1. Check the verifier URL is accessible
2. Verify the bot is sending correct RFC 9421 headers
3. Check clock synchronization (signatures expire)

### Timeout errors

Increase the timeout:

```bash
OBA_TIMEOUT_MS=10000 npx @openbotauth/proxy
```

### Backend not receiving requests

1. Verify `UPSTREAM_URL` is correct
2. Check backend is running and accessible
3. Test direct connection to backend

## Links

* **npm:** <https://www.npmjs.com/package/@openbotauth/proxy>
* **GitHub:** <https://github.com/OpenBotAuth/openbotauth>


# Docker

Run the OpenBotAuth Proxy using Docker Hub images.

**Image:** [hammadtariq/openbotauth-proxy](https://hub.docker.com/r/hammadtariq/openbotauth-proxy)

## Quick Start

```bash
docker run -p 8088:8088 hammadtariq/openbotauth-proxy
```

## Supported Platforms

The Docker image supports multiple architectures:

* `linux/amd64` - Intel/AMD 64-bit
* `linux/arm64` - ARM 64-bit (Apple Silicon, AWS Graviton)

## Pull Image

```bash
# Latest version
docker pull hammadtariq/openbotauth-proxy

# Specific version
docker pull hammadtariq/openbotauth-proxy:0.1.5

# Latest tag
docker pull hammadtariq/openbotauth-proxy:latest
```

## Configuration

Configure via environment variables:

| Variable              | Default                                   | Description                     |
| --------------------- | ----------------------------------------- | ------------------------------- |
| `PORT`                | `8088`                                    | Proxy listen port               |
| `UPSTREAM_URL`        | `http://localhost:8080`                   | Backend server URL              |
| `OBA_VERIFIER_URL`    | `https://verifier.openbotauth.org/verify` | Verifier endpoint               |
| `OBA_MODE`            | `observe`                                 | `observe` or `require-verified` |
| `OBA_TIMEOUT_MS`      | `5000`                                    | Verifier timeout (ms)           |
| `OBA_PROTECTED_PATHS` | `/protected`                              | Comma-separated protected paths |

## Usage Examples

### Basic Usage

```bash
docker run -p 8088:8088 hammadtariq/openbotauth-proxy
```

### Custom Backend

```bash
docker run -p 8088:8088 \
  -e UPSTREAM_URL=http://host.docker.internal:3000 \
  hammadtariq/openbotauth-proxy
```

### Require Verification

```bash
docker run -p 8088:8088 \
  -e UPSTREAM_URL=http://backend:3000 \
  -e OBA_MODE=require-verified \
  -e OBA_PROTECTED_PATHS=/api,/content \
  hammadtariq/openbotauth-proxy
```

### Full Configuration

```bash
docker run -p 8088:8088 \
  -e PORT=8088 \
  -e UPSTREAM_URL=http://backend:3000 \
  -e OBA_VERIFIER_URL=https://verifier.openbotauth.org/verify \
  -e OBA_MODE=require-verified \
  -e OBA_TIMEOUT_MS=3000 \
  -e OBA_PROTECTED_PATHS=/api/v1,/protected \
  hammadtariq/openbotauth-proxy
```

## Docker Compose

### Basic Setup

```yaml
# docker-compose.yml
version: '3.8'

services:
  proxy:
    image: hammadtariq/openbotauth-proxy
    ports:
      - "8088:8088"
    environment:
      - UPSTREAM_URL=http://backend:3000
      - OBA_MODE=observe
    depends_on:
      - backend

  backend:
    image: your-backend-image
    expose:
      - "3000"
```

### Production Setup

```yaml
# docker-compose.yml
version: '3.8'

services:
  proxy:
    image: hammadtariq/openbotauth-proxy
    ports:
      - "8088:8088"
    environment:
      - UPSTREAM_URL=http://backend:3000
      - OBA_MODE=require-verified
      - OBA_PROTECTED_PATHS=/api,/content
      - OBA_TIMEOUT_MS=3000
    depends_on:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8088/.well-known/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  backend:
    image: your-backend-image
    expose:
      - "3000"
    restart: unless-stopped
```

### With Nginx Frontend

```yaml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - proxy

  proxy:
    image: hammadtariq/openbotauth-proxy
    expose:
      - "8088"
    environment:
      - UPSTREAM_URL=http://backend:3000
      - OBA_MODE=observe
    depends_on:
      - backend

  backend:
    image: your-backend-image
    expose:
      - "3000"
```

## Kubernetes

### Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: oba-proxy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: oba-proxy
  template:
    metadata:
      labels:
        app: oba-proxy
    spec:
      containers:
        - name: oba-proxy
          image: hammadtariq/openbotauth-proxy
          ports:
            - containerPort: 8088
          env:
            - name: UPSTREAM_URL
              value: "http://backend-service:3000"
            - name: OBA_MODE
              value: "observe"
          livenessProbe:
            httpGet:
              path: /.well-known/health
              port: 8088
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /.well-known/health
              port: 8088
            initialDelaySeconds: 5
            periodSeconds: 5
```

### Service

```yaml
apiVersion: v1
kind: Service
metadata:
  name: oba-proxy
spec:
  selector:
    app: oba-proxy
  ports:
    - port: 8088
      targetPort: 8088
  type: ClusterIP
```

## Networking

### Accessing Host Services

When proxying to services on the Docker host:

**Docker Desktop (Mac/Windows):**

```bash
docker run -p 8088:8088 \
  -e UPSTREAM_URL=http://host.docker.internal:3000 \
  hammadtariq/openbotauth-proxy
```

**Linux:**

```bash
docker run -p 8088:8088 \
  --add-host=host.docker.internal:host-gateway \
  -e UPSTREAM_URL=http://host.docker.internal:3000 \
  hammadtariq/openbotauth-proxy
```

### Docker Network

For containers in the same network:

```bash
# Create network
docker network create oba-network

# Run backend
docker run -d --name backend --network oba-network your-backend

# Run proxy
docker run -p 8088:8088 --network oba-network \
  -e UPSTREAM_URL=http://backend:3000 \
  hammadtariq/openbotauth-proxy
```

## Health Check

```bash
curl http://localhost:8088/.well-known/health
```

Response:

```json
{
  "status": "ok",
  "service": "openbotauth-proxy",
  "upstream": "http://backend:3000",
  "verifier": "https://verifier.openbotauth.org/verify",
  "mode": "observe"
}
```

## Troubleshooting

### Cannot connect to backend

1. Ensure backend is on the same Docker network
2. Use service name (not localhost) for `UPSTREAM_URL`
3. Check backend is exposing the correct port

### Image pull fails

```bash
# Check Docker Hub status
docker pull hammadtariq/openbotauth-proxy

# Try with explicit registry
docker pull docker.io/hammadtariq/openbotauth-proxy
```

### Container exits immediately

Check container logs:

```bash
docker logs <container_id>
```

## Links

* **Docker Hub:** <https://hub.docker.com/r/hammadtariq/openbotauth-proxy>
* **GitHub:** <https://github.com/OpenBotAuth/openbotauth>


# GitHub Container Registry

Run the OpenBotAuth Proxy using GitHub Container Registry (GHCR) images.

**Image:** [ghcr.io/openbotauth/openbotauth-proxy](https://github.com/OpenBotAuth/openbotauth/pkgs/container/openbotauth-proxy)

## Why GHCR?

* **GitHub integration** - Works seamlessly with GitHub Actions and workflows
* **Same source** - Images built directly from GitHub repository
* **Alternative registry** - Fallback if Docker Hub is unavailable

## Quick Start

```bash
docker run -p 8088:8088 ghcr.io/openbotauth/openbotauth-proxy
```

## Supported Platforms

* `linux/amd64` - Intel/AMD 64-bit
* `linux/arm64` - ARM 64-bit (Apple Silicon, AWS Graviton)

## Pull Image

```bash
# Latest version
docker pull ghcr.io/openbotauth/openbotauth-proxy

# Specific version
docker pull ghcr.io/openbotauth/openbotauth-proxy:0.1.5

# Latest tag
docker pull ghcr.io/openbotauth/openbotauth-proxy:latest
```

## Authentication

GHCR images are public, but for private images or higher rate limits:

```bash
# Login with GitHub token
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
```

## Configuration

Same environment variables as Docker Hub image:

| Variable              | Default                                   | Description                     |
| --------------------- | ----------------------------------------- | ------------------------------- |
| `PORT`                | `8088`                                    | Proxy listen port               |
| `UPSTREAM_URL`        | `http://localhost:8080`                   | Backend server URL              |
| `OBA_VERIFIER_URL`    | `https://verifier.openbotauth.org/verify` | Verifier endpoint               |
| `OBA_MODE`            | `observe`                                 | `observe` or `require-verified` |
| `OBA_TIMEOUT_MS`      | `5000`                                    | Verifier timeout (ms)           |
| `OBA_PROTECTED_PATHS` | `/protected`                              | Comma-separated protected paths |

## Usage Examples

### Basic Usage

```bash
docker run -p 8088:8088 ghcr.io/openbotauth/openbotauth-proxy
```

### With Configuration

```bash
docker run -p 8088:8088 \
  -e UPSTREAM_URL=http://backend:3000 \
  -e OBA_MODE=require-verified \
  -e OBA_PROTECTED_PATHS=/api,/content \
  ghcr.io/openbotauth/openbotauth-proxy
```

## Docker Compose

```yaml
version: '3.8'

services:
  proxy:
    image: ghcr.io/openbotauth/openbotauth-proxy
    ports:
      - "8088:8088"
    environment:
      - UPSTREAM_URL=http://backend:3000
      - OBA_MODE=observe
    depends_on:
      - backend

  backend:
    image: your-backend-image
    expose:
      - "3000"
```

## GitHub Actions

### Use in Workflows

```yaml
name: Deploy with OBA Proxy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    services:
      oba-proxy:
        image: ghcr.io/openbotauth/openbotauth-proxy
        ports:
          - 8088:8088
        env:
          UPSTREAM_URL: http://localhost:3000
          OBA_MODE: observe

    steps:
      - uses: actions/checkout@v4

      - name: Start backend
        run: npm start &

      - name: Test through proxy
        run: curl http://localhost:8088/.well-known/health
```

### Build and Deploy with GHCR

```yaml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Pull OBA Proxy
        run: docker pull ghcr.io/openbotauth/openbotauth-proxy

      - name: Deploy
        run: |
          docker run -d \
            -p 8088:8088 \
            -e UPSTREAM_URL=http://backend:3000 \
            ghcr.io/openbotauth/openbotauth-proxy
```

## Kubernetes with GHCR

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: oba-proxy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: oba-proxy
  template:
    metadata:
      labels:
        app: oba-proxy
    spec:
      containers:
        - name: oba-proxy
          image: ghcr.io/openbotauth/openbotauth-proxy
          ports:
            - containerPort: 8088
          env:
            - name: UPSTREAM_URL
              value: "http://backend-service:3000"
            - name: OBA_MODE
              value: "observe"
```

For private images, add imagePullSecrets:

```yaml
spec:
  imagePullSecrets:
    - name: ghcr-secret
  containers:
    - name: oba-proxy
      image: ghcr.io/openbotauth/openbotauth-proxy
```

## Comparing Registries

| Feature        | Docker Hub                      | GHCR                                    |
| -------------- | ------------------------------- | --------------------------------------- |
| URL            | `hammadtariq/openbotauth-proxy` | `ghcr.io/openbotauth/openbotauth-proxy` |
| Auth           | Docker Hub account              | GitHub token                            |
| Rate Limits    | 100 pulls/6hr (anon)            | 1000+ pulls/hr                          |
| GitHub Actions | Manual login                    | Native integration                      |
| Same Image     | Yes                             | Yes                                     |

## Health Check

```bash
curl http://localhost:8088/.well-known/health
```

Response:

```json
{
  "status": "ok",
  "service": "openbotauth-proxy",
  "upstream": "http://backend:3000",
  "verifier": "https://verifier.openbotauth.org/verify",
  "mode": "observe"
}
```

## Troubleshooting

### Rate limit exceeded

Login to GHCR for higher limits:

```bash
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
```

### Image not found

Verify the image exists:

```bash
docker manifest inspect ghcr.io/openbotauth/openbotauth-proxy
```

### Permission denied

For private images, ensure your token has `read:packages` scope.

## Links

* **GHCR:** <https://github.com/OpenBotAuth/openbotauth/pkgs/container/openbotauth-proxy>
* **GitHub:** <https://github.com/OpenBotAuth/openbotauth>


# Overview

OpenBotAuth provides plugins for popular content management systems, allowing publishers to easily integrate bot verification without writing code.

## Available Plugins

| Plugin                          | Platform       | Status                     |
| ------------------------------- | -------------- | -------------------------- |
| [WordPress](/plugins/wordpress) | WordPress 6.0+ | Available (manual install) |

## Why Use Plugins?

Plugins provide the simplest way to add OpenBotAuth verification to your site:

* **No code changes** - Install, configure, and go
* **Per-content policies** - Control bot access at the post/page level
* **Content teasers** - Show previews to unverified bots
* **Payment support** - Enable 402 Payment Required flows
* **Rate limiting** - Prevent bot abuse

## Alternative Integration Methods

If a plugin isn't available for your platform:

* [**SDKs**](/sdks/sdks) - Node.js and Python libraries for custom integration
* [**Proxy**](/proxy/proxy) - Zero-code reverse proxy for any HTTP backend


# WordPress

The OpenBotAuth WordPress plugin enables content owners to control how AI agents and bots access their content using RFC 9421 HTTP Message Signatures.

## Features

* **Signature Verification** - Verify bot identity using Ed25519 cryptographic signatures
* **Content Teasers** - Show first N words to unverified bots
* **Payment Flow** - Return 402 Payment Required for premium content
* **Rate Limiting** - Per-agent rate limits to prevent abuse
* **Access Control** - Whitelist/blacklist specific bots
* **Per-Post Policies** - Override default policy on individual posts

## Requirements

* WordPress 6.0 or higher
* PHP 7.4 or higher
* Access to OpenBotAuth Verifier Service

## Installation

> **Note:** The plugin is currently under review at WordPress.org. For now, use manual installation.

### Manual Installation

**Option 1: Download and Upload**

1. Download the plugin from GitHub:

   ```bash
   git clone https://github.com/OpenBotAuth/openbotauth.git
   cd openbotauth/plugins
   zip -r wordpress-openbotauth.zip wordpress-openbotauth
   ```
2. Upload via WordPress Admin:
   * Go to **Plugins → Add New → Upload Plugin**
   * Choose the `wordpress-openbotauth.zip` file
   * Click **Install Now**
   * Click **Activate**

**Option 2: Copy to Plugins Directory**

```bash
# Clone the repository
git clone https://github.com/OpenBotAuth/openbotauth.git

# Copy plugin to WordPress
cp -r openbotauth/plugins/wordpress-openbotauth /path/to/wordpress/wp-content/plugins/
```

Then activate via **WordPress Admin → Plugins**.

## Configuration

### Basic Setup

1. Go to **Settings → OpenBotAuth**
2. Configure the **Verifier Service URL**:

   | Environment         | URL                                       |
   | ------------------- | ----------------------------------------- |
   | Production (hosted) | `https://verifier.openbotauth.org/verify` |
   | Self-hosted         | `https://verifier.yourdomain.com/verify`  |
   | Local development   | `http://localhost:8081/verify`            |
3. Set **Default Policy**:
   * **Allow** - All bots can access content
   * **Teaser** - Show preview to unverified bots (recommended)
   * **Deny** - Block unverified bots
4. Set **Teaser Word Count** (default: 100)
5. Click **Save Settings**

### Per-Post Policies

Override the default policy for individual posts:

1. Edit a post or page
2. Find the **OpenBotAuth Policy** meta box in the sidebar
3. Check **Override default policy**
4. Configure:
   * **Effect**: Allow, Teaser, or Deny
   * **Teaser Words**: Number of words for preview
   * **Price (cents)**: Require payment (e.g., `500` for $5.00)
5. Save the post

### Advanced Policy Configuration

For advanced policies, edit the Policy JSON directly in settings:

```json
{
  "default": {
    "effect": "teaser",
    "teaser_words": 100,
    "whitelist": [
      "https://trusted-bot.example.com/jwks.json"
    ],
    "blacklist": [
      "https://badbot.example.com/*"
    ],
    "rate_limit": {
      "max_requests": 100,
      "window_seconds": 3600
    }
  }
}
```

## Policy Options

| Field                       | Type   | Description                                  |
| --------------------------- | ------ | -------------------------------------------- |
| `effect`                    | string | Default action: `allow`, `deny`, or `teaser` |
| `teaser_words`              | number | Words to show in preview (0 = no teaser)     |
| `price_cents`               | number | Price in cents (0 = free, >0 = 402 response) |
| `currency`                  | string | Currency code (default: `USD`)               |
| `whitelist`                 | array  | Bot patterns to always allow                 |
| `blacklist`                 | array  | Bot patterns to always deny                  |
| `rate_limit.max_requests`   | number | Max requests per window                      |
| `rate_limit.window_seconds` | number | Time window in seconds                       |

## Response Headers

The plugin adds an `X-OBA-Decision` header to responses:

| Value        | Meaning                                 |
| ------------ | --------------------------------------- |
| `allow`      | Bot is verified and allowed full access |
| `teaser`     | Unverified bot receives preview content |
| `pay`        | Payment required (402 response)         |
| `deny`       | Bot is denied access (403 response)     |
| `rate_limit` | Rate limit exceeded (429 response)      |

## Hooks and Filters

### Filter: `openbotauth_policy`

Modify policy before applying:

```php
add_filter('openbotauth_policy', function($policy, $post) {
    if ($post->post_type === 'premium') {
        $policy['price_cents'] = 1000;
    }
    return $policy;
}, 10, 2);
```

### Action: `openbotauth_verified`

Triggered when a bot is verified:

```php
add_action('openbotauth_verified', function($agent, $post) {
    error_log("Bot {$agent['jwks_url']} accessed post {$post->ID}");
}, 10, 2);
```

### Action: `openbotauth_payment_required`

Triggered when 402 is returned:

```php
add_action('openbotauth_payment_required', function($agent, $post, $price) {
    // Track payment requests
}, 10, 3);
```

## Troubleshooting

### Verifier Connection Failed

**Error**: "Verifier service error: Connection refused"

1. Check verifier service is running
2. Verify URL in Settings → OpenBotAuth
3. Check firewall rules

### Teaser Not Showing

1. Verify policy effect is set to `teaser`
2. Ensure `teaser_words` > 0
3. Log out of WordPress (logged-in users see full content)
4. Check `X-OBA-Decision` header in response

### No X-OBA-Decision Header

1. Ensure you're testing on a singular post/page (not homepage)
2. Log out of WordPress
3. Check PHP error logs for verifier connection issues

## Source Code

GitHub: [OpenBotAuth/openbotauth](https://github.com/OpenBotAuth/openbotauth/tree/main/plugins/wordpress-openbotauth)

## License

GPLv2 or later


# Say Hi!

Gaurav Shukla - [Github](https://github.com/runnerelectrode), [X](https://x.com/RunnerElektrode)

Hammad Tariq - [Github](https://github.com/hammadtq), [X](https://x.com/hammadtariq)


