Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
},
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' }],
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
},
ignorePatterns: ['dist/', 'node_modules/', '*.cjs', '*.config.*'],
Expand Down
109 changes: 109 additions & 0 deletions packages/tollway-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# @tollway/client

Drop-in `fetch` replacement for AI agents. Handles Tollway identity headers, site policy enforcement, HTTP 402 payment flows, and structured data extraction automatically.

Part of the [Tollway open protocol](https://tollway.dev) — robots.txt rebuilt for the agentic era.

## Install

```bash
npm install @tollway/client
```

## Quick Start

```ts
import { createAgent } from '@tollway/client';

const agent = createAgent({
did: process.env.AGENT_DID, // did:key:z6Mk...
privateKey: process.env.AGENT_KEY, // Ed25519 private key (hex)
purpose: 'Summarise recent AI news',
scope: 'read',
});

const result = await agent.fetch('https://techcrunch.com/2026/03/09/ai-news/');

console.log(result.data); // { title, description, url, domain }
console.log(result.attribution); // "TechCrunch (https://...)" if required
console.log(result.paid); // true if a micropayment was made
console.log(result.cost); // "0.001" USDC
console.log(result.policy); // site's tollway.json policy
```

## API

### `createAgent(options)`

Returns an agent instance bound to a set of identity options.

```ts
const agent = createAgent({
did: 'did:key:z6Mk...', // required — your agent's DID
privateKey: '...', // required — Ed25519 private key (hex)
purpose: 'Research', // required — human-readable intent
scope: 'read', // required — read | search | summarize | train | scrape_bulk
wallet: '0x...', // optional — USDC wallet for payments
maxPriceUsdc: '0.01', // optional — max per-request price (default 0.01)
principalDid: 'did:key:...', // optional — operator DID if different from agent
framework: 'langchain/0.3.0', // optional — framework identifier
reputationOracle: 'https://...', // optional — custom reputation oracle
});

agent.fetch(url, init?) // fetch with identity headers attached
agent.checkPolicy(url) // fetch site's tollway.json without making a request
agent.options // the options this agent was created with
```

### `fetch(url, init?)`

Low-level fetch with optional `tollway` options. Use `createAgent` for most cases.

```ts
import { fetch } from '@tollway/client';

const result = await fetch('https://example.com/', {
tollway: { did, privateKey, purpose, scope },
});
```

### `getReputation(did, oracle?)`

Look up an agent's reputation score from a Tollway reputation oracle.

```ts
import { getReputation } from '@tollway/client';

const rep = await getReputation('did:key:z6Mk...');
// { score: 0.95, observations: 1234, flags: [] }
```

## How It Works

1. **Policy fetch** — checks `/.well-known/tollway.json` on the target site (cached 5 min)
2. **Identity headers** — attaches `X-Tollway-DID`, `X-Tollway-Purpose`, `X-Tollway-Scope`, `X-Tollway-Nonce`, `X-Tollway-Timestamp`, and an Ed25519 `X-Tollway-Signature`
3. **402 handling** — if the site returns HTTP 402, attempts payment via x402 (USDC on Base) and retries
4. **Extraction** — parses OG tags and metadata from HTML responses into structured JSON

## Generating a DID + Key Pair

```ts
import { generateKeyPair } from '@noble/ed25519';
import { base58btc } from 'multiformats/bases/base58';

const privKey = crypto.getRandomValues(new Uint8Array(32));
const pubKey = await generateKeyPair(privKey); // ed25519

// Multicodec prefix for Ed25519 public key: 0xed01
const multicodecKey = new Uint8Array([0xed, 0x01, ...pubKey]);
const did = `did:key:z${base58btc.encode(multicodecKey)}`;
const privateKeyHex = Buffer.from(privKey).toString('hex');
```

## Protocol

`@tollway/client` implements the [Tollway v0.1 specification](https://github.com/TollwayProtocol/Tollway/blob/main/SPEC.md).

- **Spec:** CC BY 4.0
- **Code:** MIT
- **GitHub:** [TollwayProtocol/Tollway](https://github.com/TollwayProtocol/Tollway)
13 changes: 11 additions & 2 deletions packages/tollway-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tollway/client",
"version": "0.1.1",
"version": "0.1.2",
"description": "Drop-in fetch replacement for AI agents — identity headers, policy enforcement, and payment flows",
"keywords": [
"tollway",
Expand All @@ -27,9 +27,18 @@
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"homepage": "https://tollway.dev",
"bugs": {
"url": "https://github.com/TollwayProtocol/Tollway/issues"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/TollwayProtocol"
},
"files": [
"dist",
"LICENSE"
"LICENSE",
"README.md"
],
"scripts": {
"build": "tsup",
Expand Down
157 changes: 157 additions & 0 deletions packages/tollway-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# @tollway/server

Express and Next.js middleware for the [Tollway protocol](https://tollway.dev). Automatically serves `/.well-known/tollway.json`, validates agent identity headers, enforces rate limits, and handles HTTP 402 payment flows.

Part of the Tollway open protocol — robots.txt rebuilt for the agentic era.

## Install

```bash
npm install @tollway/server
```

## Quick Start — Express

```ts
import express from 'express';
import { tollwayMiddleware } from '@tollway/server';

const app = express();

app.use(tollwayMiddleware({
policy: {
freeRequestsPerDay: 100,
trainingAllowed: false,
attributionRequired: true,
prohibitedActions: ['scrape_bulk'],
paymentRequiredActions: ['train'],
pricing: [
{ action: 'read', price: '0.001' },
{ action: 'summarize', price: '0.005' },
{ action: 'train', price: '0.05' },
],
},
paymentAddress: process.env.WALLET_ADDRESS, // USDC address on Base
}));
```

Your policy is now live at `GET /.well-known/tollway.json`. All agent requests are validated, rate-limited, and logged automatically.

## Quick Start — Next.js

```ts
// middleware.ts
import { createNextjsMiddleware } from '@tollway/server';

const tollway = createNextjsMiddleware({
policy: {
freeRequestsPerDay: 100,
trainingAllowed: false,
prohibitedActions: ['scrape_bulk'],
},
paymentAddress: process.env.WALLET_ADDRESS,
});

export async function middleware(request: Request) {
const response = await tollway(request);
if (response) return response; // handled by Tollway (policy, 402, etc.)
// your normal middleware continues here
}

export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
```

## Generate `tollway.json` standalone

No server needed — generate a static policy file to host anywhere:

```ts
import { generateTollwayJson } from '@tollway/server';

const json = generateTollwayJson(
{
freeRequestsPerDay: 1000,
trainingAllowed: false,
attributionRequired: true,
prohibitedActions: ['scrape_bulk'],
},
'0xYourWalletAddress',
);

// Write to public/.well-known/tollway.json
```

## API

### `tollwayMiddleware(options)` → Express middleware

| Option | Type | Description |
|--------|------|-------------|
| `policy` | `ServerPolicy` | Site policy (see below) |
| `paymentAddress` | `string?` | USDC address for micropayments |
| `paymentNetwork` | `string?` | Chain (default: `"base"`) |
| `enableLogging` | `boolean?` | Log agent requests (default: `true`) |
| `onAgentRequest` | `fn?` | Callback on every verified agent request |
| `onPayment` | `fn?` | Callback on payment received |

### `ServerPolicy`

```ts
{
freeRequestsPerDay?: number; // free tier before payment kicks in
pricing?: { action, price }[]; // per-action USDC prices
trainingAllowed?: boolean;
attributionRequired?: boolean;
attributionFormat?: string; // e.g. "{title} ({url})"
cacheAllowed?: boolean;
cacheTtlSeconds?: number;
minimumReputation?: number; // 0–1
requireDid?: boolean;
allowedActions?: string[];
prohibitedActions?: string[]; // e.g. ["scrape_bulk", "train"]
paymentRequiredActions?: string[];
requestsPerMinute?: number;
requestsPerDay?: number;
}
```

### What the middleware does

For every request:

1. **Serves policy** — `GET /.well-known/tollway.json` returns your policy JSON
2. **Passes through** — non-agent requests (no `X-Tollway-*` headers) continue normally
3. **Validates timestamp** — rejects requests outside a ±5-minute window
4. **Checks nonce** — prevents replay attacks
5. **Verifies DID** — validates `did:key` Ed25519 signatures
6. **Enforces actions** — 403 for prohibited scopes
7. **Rate limits** — 429 per DID per minute/day
8. **Handles payment** — 402 with payment details if action requires it
9. **Attaches identity** — sets `req.tollwayIdentity` for downstream handlers
10. **Responds headers** — adds `X-Tollway-Served: 1` to responses

### Accessing agent identity downstream

```ts
app.get('/article', (req, res) => {
const agent = req.tollwayIdentity; // AgentIdentity | undefined
if (agent) {
console.log(agent.did, agent.purpose, agent.scope, agent.verified);
}
// ...
});
```

## Production notes

The default nonce store and rate-limit counters are **in-memory**. For multi-instance deployments, replace them with Redis. See [CONTRIBUTING.md](https://github.com/TollwayProtocol/Tollway/blob/main/CONTRIBUTING.md) for guidance.

## Protocol

`@tollway/server` implements the [Tollway v0.1 specification](https://github.com/TollwayProtocol/Tollway/blob/main/SPEC.md).

- **Spec:** CC BY 4.0
- **Code:** MIT
- **GitHub:** [TollwayProtocol/Tollway](https://github.com/TollwayProtocol/Tollway)
13 changes: 11 additions & 2 deletions packages/tollway-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tollway/server",
"version": "0.1.1",
"version": "0.1.2",
"description": "Express/Next.js middleware for the Tollway protocol — policy serving, identity validation, and payment flows",
"keywords": [
"tollway",
Expand All @@ -27,9 +27,18 @@
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"homepage": "https://tollway.dev",
"bugs": {
"url": "https://github.com/TollwayProtocol/Tollway/issues"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/TollwayProtocol"
},
"files": [
"dist",
"LICENSE"
"LICENSE",
"README.md"
],
"scripts": {
"build": "tsup",
Expand Down
Loading