Skip to content

L402 micropayment middleware for discourse (Python) — trust-weighted, progressive pricing for forums and APIs

License

Notifications You must be signed in to change notification settings

jeletor/discourse-toll-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

discourse-toll (Python)

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

Python port of discourse-toll (Node.js).

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

Install

pip install discourse-toll

With FastAPI support:

pip install discourse-toll[fastapi]

FastAPI Server

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

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

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:

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:

License

MIT

About

L402 micropayment middleware for discourse (Python) — trust-weighted, progressive pricing for forums and APIs

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages