Skip to content

Commit b6c87cd

Browse files
Add usage categories with tiered pricing and compliance levels
Introduce UsageCategory enum (summary, rag, research, training, commercial) across the entire protocol stack. Each category has escalating compliance requirements and pricing multipliers: - summary (1x, standard) — snippet/summary display - rag (2x, standard) — retrieval-augmented generation - research (3x, elevated) — academic/internal research - training (5x, strict) — model fine-tuning/pre-training - commercial (10x, strict) — redistribution/derivatives Changes span interfaces, API, MCP, payments, compliance, and docs: - UsageGrant now includes usage_category in its signed payload - PaymentRequirement returns available_tiers in 402 responses - X402 middleware resolves category from query/header/config - API routes accept ?usage= parameter and X-USAGE-CATEGORY header - MCP tools accept usage parameter for tier selection - ComplianceHeaders emit X-FairFetch-Usage-Category and X-FairFetch-Compliance-Level headers - README, DEVELOPMENT.md, and guide docs updated throughout - Also fixes stale GitHub URLs in docs and openapi.yaml
1 parent 2414550 commit b6c87cd

18 files changed

Lines changed: 426 additions & 76 deletions

DEVELOPMENT.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ from core.signatures import Ed25519Verifier
104104
public_key_b64 = "..." # base64-encoded Ed25519 public key
105105

106106
# 2. Reconstruct the signing payload from the grant fields
107-
payload = f"{grant_id}|{content_url}|{content_hash}|{license_type}|{granted_to}|{granted_at}"
107+
payload = f"{grant_id}|{content_url}|{content_hash}|{license_type}|{usage_category}|{granted_to}|{granted_at}"
108108

109109
# 3. Verify
110110
verifier = Ed25519Verifier(public_key_b64)
@@ -124,14 +124,18 @@ print(f"Grant valid: {grant.verify()}")
124124
## Testing x402 Payment Flow
125125

126126
```bash
127-
# Step 1: Request without payment -> 402
127+
# Step 1: Request without payment -> 402 (includes available_tiers with all pricing)
128128
curl -i "http://localhost:8402/content/fetch?url=https://example.com"
129-
# Response: 402 with {"accepts": {"price": "1000", "asset": "USDC", ...}}
129+
# Response: 402 with {"accepts": {...}, "available_tiers": {"summary": ..., "rag": ..., "training": ...}}
130130

131-
# Step 2: Request with test payment -> 200 + receipt + grant
131+
# Step 2: Request with test payment -> 200 + receipt + grant (default usage: summary)
132132
curl -i -H "X-PAYMENT: test_paid_fairfetch" \
133133
"http://localhost:8402/content/fetch?url=https://example.com"
134-
# Response: 200 with X-PAYMENT-RECEIPT and X-FairFetch-License-ID headers
134+
135+
# Step 3: Request with specific usage category -> higher compliance tier
136+
curl -i -H "X-PAYMENT: test_paid_fairfetch" \
137+
"http://localhost:8402/content/fetch?url=https://example.com&usage=training"
138+
# Response: 200 with X-FairFetch-Usage-Category: training, X-FairFetch-Compliance-Level: strict
135139
```
136140

137141
Any `X-PAYMENT` value starting with `test_` works. The token

README.md

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -227,28 +227,33 @@ asyncio.run(main())
227227
## 💳 The x402 Payment Flow
228228

229229
```
230-
Agent Fairfetch Facilitator
231-
| | |
232-
| GET /content/fetch?url=... | |
233-
|----------------------------->| |
234-
| | |
235-
| 402 Payment Required | |
236-
| { accepts: { price, asset, | |
237-
| network, payTo } } | |
238-
|<-----------------------------| |
239-
| | |
240-
| GET + X-PAYMENT: <proof> | |
241-
|----------------------------->| |
242-
| | POST /settle |
243-
| |-------------------------> |
244-
| | { valid, tx_hash } |
245-
| |<------------------------- |
246-
| | |
247-
| 200 OK + Content | |
248-
| X-PAYMENT-RECEIPT: 0x... | |
249-
| X-FairFetch-License-ID: ... | |
250-
| X-FairFetch-Origin-Sig: ... | |
251-
|<-----------------------------| |
230+
Agent Fairfetch Facilitator
231+
| | |
232+
| GET /content/fetch?url=... | |
233+
| &usage=rag | |
234+
|------------------------------>| |
235+
| | |
236+
| 402 Payment Required | |
237+
| { accepts: { price (2x), | |
238+
| usage_category: "rag" }, | |
239+
| available_tiers: {...} } | |
240+
|<------------------------------| |
241+
| | |
242+
| GET + X-PAYMENT: <proof> | |
243+
|------------------------------>| |
244+
| | POST /settle |
245+
| |-------------------------> |
246+
| | { valid, tx_hash } |
247+
| |<------------------------- |
248+
| | |
249+
| 200 OK + Content | |
250+
| X-PAYMENT-RECEIPT: 0x... | |
251+
| X-FairFetch-License-ID: ... | |
252+
| X-FairFetch-Usage-Category: | |
253+
| rag | |
254+
| X-FairFetch-Compliance-Level:| |
255+
| standard | |
256+
|<------------------------------| |
252257
```
253258

254259
> [!NOTE]
@@ -257,16 +262,49 @@ Agent Fairfetch Facilitator
257262
258263
<br />
259264

265+
## 📊 Usage Categories & Tiered Pricing
266+
267+
Not all content usage is equal. Fairfetch defines **usage categories** that control what an AI agent is permitted to do with the content, with escalating compliance requirements and pricing:
268+
269+
| Category | Compliance | Price Multiplier | Use Case |
270+
|----------|-----------|-----------------|----------|
271+
| `summary` | Standard | 1x | Display a short summary or snippet |
272+
| `rag` | Standard | 2x | Retrieval-Augmented Generation / search grounding |
273+
| `research` | Elevated | 3x | Academic or internal research use |
274+
| `training` | Strict | 5x | Model fine-tuning or pre-training |
275+
| `commercial` | Strict | 10x | Redistribution or commercial derivative works |
276+
277+
> [!IMPORTANT]
278+
> The `usage` parameter is specified via query param (`?usage=rag`), HTTP header (`X-USAGE-CATEGORY: training`), or MCP tool argument. It determines the effective price and the compliance level recorded in the Usage Grant.
279+
280+
```bash
281+
# Fetch for RAG (2x base price)
282+
curl -H "X-PAYMENT: test_paid_fairfetch" \
283+
"http://localhost:8402/content/fetch?url=https://example.com&usage=rag"
284+
285+
# Fetch for training (5x base price, strict compliance)
286+
curl -H "X-PAYMENT: test_paid_fairfetch" \
287+
"http://localhost:8402/content/fetch?url=https://example.com&usage=training"
288+
289+
# The 402 response includes all available tiers and their prices
290+
curl "http://localhost:8402/content/fetch?url=https://example.com"
291+
```
292+
293+
Every 402 response includes an `available_tiers` object showing the price for each category, so agents can choose the appropriate tier for their needs.
294+
295+
<br />
296+
260297
## 🔐 Usage Grants (Legal Indemnity)
261298

262-
A Usage Grant is an Ed25519-signed token proving legal content access:
299+
A Usage Grant is an Ed25519-signed token proving legal content access under a specific usage category:
263300

264301
```json
265302
{
266303
"grant_id": "a1b2c3d4...",
267304
"content_url": "https://publisher.com/article",
268305
"content_hash": "sha256:...",
269306
"license_type": "publisher-terms",
307+
"usage_category": "rag",
270308
"granted_to": "0xPayerWallet...",
271309
"granted_at": "2026-02-22T12:00:00Z",
272310
"signature": {
@@ -277,6 +315,8 @@ A Usage Grant is an Ed25519-signed token proving legal content access:
277315
}
278316
```
279317

318+
The `usage_category` field is part of the cryptographic signature, so it cannot be altered after issuance. An agent granted `summary` access cannot claim `training` rights — a new grant with appropriate pricing is required.
319+
280320
<details>
281321
<summary><strong>How to verify a grant locally</strong></summary>
282322

@@ -287,7 +327,7 @@ A Usage Grant is an Ed25519-signed token proving legal content access:
287327
# Or extract it from any response's grant signature (the publicKey field)
288328

289329
# The grant's signature covers:
290-
# grant_id | content_url | content_hash | license_type | granted_to | granted_at
330+
# grant_id | content_url | content_hash | license_type | usage_category | granted_to | granted_at
291331
# Verify using any Ed25519 library against the public key
292332
```
293333

@@ -297,7 +337,7 @@ A Usage Grant is an Ed25519-signed token proving legal content access:
297337

298338
## 🤖 MCP Server (Direct Pipeline)
299339

300-
Three tools for AI agents:
340+
Three tools for AI agents (all accept an optional `usage` parameter for tier selection):
301341

302342
| Tool | Description |
303343
|------|-------------|
@@ -380,6 +420,8 @@ Edge boilerplates are provided for **Cloudflare Workers**, **AWS CloudFront Lamb
380420
|--------|-------------|
381421
| `X-Data-Origin-Verified` | EU AI Act origin attestation |
382422
| `X-AI-License-Type` | `publisher-terms` · `commercial` · `research-only` · `opt-out` |
423+
| `X-FairFetch-Usage-Category` | `summary` · `rag` · `research` · `training` · `commercial` |
424+
| `X-FairFetch-Compliance-Level` | `standard` · `elevated` · `strict` |
383425
| `X-FairFetch-Origin-Signature` | Ed25519 signature of content body |
384426
| `X-FairFetch-License-ID` | Usage Grant compact identifier |
385427
| `X-Content-Hash` | `sha256:<hex>` hash of content |
@@ -414,7 +456,7 @@ fairfetch/
414456
├── interfaces/ # Open Standard (abstract bases)
415457
│ ├── facilitator.py # BaseFacilitator
416458
│ ├── summarizer.py # BaseSummarizer
417-
│ └── license_provider.py # BaseLicenseProvider + UsageGrant
459+
│ └── license_provider.py # BaseLicenseProvider + UsageGrant + UsageCategory
418460
├── core/ # Green AI layer
419461
│ ├── converter.py # HTML → Markdown (trafilatura)
420462
│ ├── summarizer.py # LiteLLM implementation
@@ -465,6 +507,7 @@ fairfetch/
465507
| `FAIRFETCH_CONTENT_PRICE` | `1000` | Price in smallest USDC unit |
466508
| `FAIRFETCH_SIGNING_KEY` | *(generated)* | Ed25519 private key (b64) |
467509
| `FAIRFETCH_LICENSE_TYPE` | `publisher-terms` | Default license |
510+
| `FAIRFETCH_DEFAULT_USAGE_CATEGORY` | `summary` | Default usage tier for pricing |
468511
| `FAIRFETCH_ENABLE_GRANTS` | `true` | Issue Usage Grants |
469512
| `FAIRFETCH_PREFERRED_ACCESS` | `true` | Inject bot-steering headers |
470513
| `LITELLM_MODEL` | `gpt-4o-mini` | LLM for summarization |

api/dependencies.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from core.signatures import Ed25519Signer
1313
from core.summarizer import Summarizer
1414
from interfaces.facilitator import BaseFacilitator, PaymentRequirement
15-
from interfaces.license_provider import BaseLicenseProvider
15+
from interfaces.license_provider import BaseLicenseProvider, UsageCategory
1616
from interfaces.summarizer import BaseSummarizer
1717
from payments.mock_facilitator import MockFacilitator
1818
from payments.mock_license_facilitator import MockLicenseFacilitator, MockLicenseProvider
@@ -32,6 +32,7 @@ class FairFetchConfig(BaseModel):
3232
litellm_model: str = Field(default="gpt-4o-mini")
3333
signing_key: str = Field(default="")
3434
license_type: str = Field(default="publisher-terms")
35+
default_usage_category: str = Field(default=UsageCategory.SUMMARY)
3536
host: str = Field(default="0.0.0.0")
3637
port: int = Field(default=8402)
3738

@@ -54,6 +55,9 @@ def from_env(cls) -> FairFetchConfig:
5455
litellm_model=os.getenv("LITELLM_MODEL", "gpt-4o-mini"),
5556
signing_key=os.getenv("FAIRFETCH_SIGNING_KEY", ""),
5657
license_type=os.getenv("FAIRFETCH_LICENSE_TYPE", "publisher-terms"),
58+
default_usage_category=os.getenv(
59+
"FAIRFETCH_DEFAULT_USAGE_CATEGORY", UsageCategory.SUMMARY
60+
),
5761
host=os.getenv("FAIRFETCH_HOST", "0.0.0.0"),
5862
port=int(os.getenv("FAIRFETCH_PORT", "8402")),
5963
llms_txt_url=os.getenv("FAIRFETCH_LLMS_TXT_URL", "/.well-known/llms.txt"),

api/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ def create_app() -> FastAPI:
4949
"*",
5050
"X-PAYMENT",
5151
"X-PAYMENT-RECEIPT",
52+
"X-USAGE-CATEGORY",
5253
"X-FairFetch-License-ID",
5354
"X-FairFetch-Origin-Signature",
5455
],
5556
expose_headers=[
5657
"X-PAYMENT-RECEIPT",
5758
"X-Data-Origin-Verified",
5859
"X-AI-License-Type",
60+
"X-FairFetch-Usage-Category",
61+
"X-FairFetch-Compliance-Level",
5962
"X-FairFetch-License-ID",
6063
"X-FairFetch-Origin-Signature",
6164
"X-FairFetch-Preferred-Access",

0 commit comments

Comments
 (0)