Metadata-Version: 2.4
Name: discourse-toll
Version: 0.1.1
Summary: L402 micropayment middleware for discourse — trust-weighted, progressive pricing for forums and APIs
Author-email: Jeletor <jeletor@jeletor.com>
License: MIT
Project-URL: Homepage, https://github.com/jeletor/discourse-toll-python
Project-URL: Repository, https://github.com/jeletor/discourse-toll-python
Keywords: l402,lightning,bitcoin,discourse,forum,micropayment,trust,ai-wot,nostr,fastapi
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.25.0
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
Provides-Extra: nwc
Requires-Dist: websockets>=12.0; extra == "nwc"
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
Dynamic: license-file

# discourse-toll (Python)

L402 micropayment middleware for discourse. Trust-weighted, progressive pricing for forums and APIs.

**Python port of [discourse-toll](https://github.com/jeletor/discourse-toll) (Node.js).**

**The thesis:** Karma selects for popularity. Rate limiting is indiscriminate. Economic cost selects for intentionality.

## Install

```bash
pip install discourse-toll
```

With FastAPI support:
```bash
pip install discourse-toll[fastapi]
```

## FastAPI Server

```python
from fastapi import FastAPI, Depends
from discourse_toll import create_toll, TollConfig, PricingConfig, MockWallet

app = FastAPI()
mock = MockWallet()  # Replace with real wallet in production

toll = create_toll(TollConfig(
    secret="your-hmac-secret",
    wallet=mock,
    pricing=PricingConfig(
        base_sats=1,
        progressive_multiplier=1.5,
        progressive_cap=50,
    ),
))

@app.post("/api/comments")
async def post_comment(
    request: Request,
    paid: bool = Depends(toll(context_from="body.thread_id")),
):
    return {"ok": True}
```

## Client

```python
from discourse_toll import create_discourse_client

client = create_discourse_client(
    pay_fn=my_wallet.pay_invoice,  # async (invoice) -> {"preimage": str}
    max_sats=50,
    agent_id="my-nostr-pubkey",
)

result = await client.post(
    "https://forum.example/api/comments",
    json={"text": "Worth paying for", "thread_id": "abc"},
)
print(result.paid, result.sats)  # True, 1
```

## How pricing works

| Scenario | Cost |
|---|---|
| First comment in a thread | 1 sat |
| Second comment, same thread | 2 sats |
| Third comment, same thread | 3 sats |
| 10th comment, same thread | 38 sats |
| First comment in a different thread | 1 sat |
| Any comment with trust score ≥ 80 | Free |
| Any comment with trust score 30-79 | 50% off |
| Comment after waiting > 1 minute | 25% off |

## Trust integration

```python
from discourse_toll import static_resolver, api_resolver

# Static (testing)
toll = create_toll(TollConfig(
    secret="test",
    wallet=mock,
    trust=static_resolver({"pubkey-1": 90, "pubkey-2": 40}),
))

# ai.wot REST API (production)
toll = create_toll(TollConfig(
    secret="test",
    wallet=mock,
    trust=api_resolver("https://wot.jeletor.cc"),
))
```

## Wallet backend

Implement the wallet protocol:

```python
class MyWallet:
    async def create_invoice(self, sats: int, description: str) -> dict:
        """Return {"invoice": "lnbc...", "payment_hash": "hex"}"""
        ...

    async def lookup_invoice(self, payment_hash: str) -> dict:
        """Return {"paid": bool, "preimage": "hex" | None}"""
        ...
```

Or use `MockWallet` for testing (no Lightning needed).

## Security

- Only give the server wallet `make_invoice` and `lookup_invoice` permissions
- Macaroon caveats are locked to: expiry, endpoint, method, context, agent
- Fail open on errors (availability > enforcement)
- Preimage verification: SHA256(preimage) must equal payment_hash

## Stack

Python port of the Node.js ecosystem:
- [discourse-toll](https://github.com/jeletor/discourse-toll) — Original Node.js package
- [ai-wot](https://github.com/jeletor/ai-wot) — Decentralized trust scores
- [lightning-toll](https://github.com/jeletor/lightning-toll) — L402 protocol

## License

MIT
