Python SDK for LIME site backends. Auto SSE dispatcher, on_login handlers, JWT passport verify. Async-first, JWKS caching.
Project description
LIME Sites SDK
Official Python SDK for LIME site backends. Async-first: create login requests, receive agent passports over a background SSE dispatcher, verify JWTs via JWKS.
Repository: lime-site-sdk · PyPI:
lime-sites-sdk· Import:from lime_sites import LimeSite
Pair with lime-agents-sdk on the agent worker side.
Installation
pip install lime-sites-sdk
Latest from GitHub (if PyPI lags):
pip install "git+https://github.com/Mawyxx/lime-site-sdk.git@v1.0.1"
Requirements: Python 3.10+
Background SSE dispatcher (v1.0+)
v1.0 removed wait_for_login(). Instead, LimeSite starts an internal SiteEventDispatcher when you construct it inside a running asyncio loop:
- Opens perpetual
GET /modules/agent-login/events(text/event-stream,X-Site-Token). - Parses SSE frames (
approved,expired,keepalive) with automatic reconnect and exponential backoff. - On each terminal event, calls your registered
on_loginhandlers:(request_id, passport | None). - Stops on
await site.aclose()(process shutdown).
You map request_id → user session in the handler. The dispatcher delivers all login events for this site token — not one blocking wait per HTTP request.
┌─────────────────────────────────────────────────────────────┐
│ Your site backend process (one LimeSite per site token) │
│ │
│ LimeSite() ──► SiteEventDispatcher (background task) │
│ │ │ │
│ │ └──► GET /agent-login/events (loop) │
│ │ │ approved / expired │
│ ▼ ▼ │
│ create_login_request() @site.on_login handlers │
│ │ │ │
│ └── request_id ───────► verify_passport → session │
└─────────────────────────────────────────────────────────────┘
Rules:
| Rule | Why |
|---|---|
One LimeSite per site token per process |
One SSE connection per site |
Construct only inside running loop (lifespan, asyncio.run) |
Dispatcher is asyncio.create_task(...) |
| Handlers must be fast | They run sequentially on the dispatcher thread |
passport=None means expired |
Clear pending login for that request_id |
Not public API: no start() / stop() — lifecycle is LimeSite() + aclose().
Migration from 0.1.0 (wait_for_login)
| 0.1.0 | 1.0+ |
|---|---|
site = LimeSite() anywhere |
site = LimeSite() inside running event loop |
login = await site.wait_for_login(req.request_id) |
@site.on_login receives JWT when agent approves |
| Blocking wait per request | Map request_id in handler to your session store |
TimeoutError |
Use your own timeout on pending session state |
Quick start (FastAPI)
from contextlib import asynccontextmanager
from fastapi import FastAPI
from lime_sites import LimeSite
site: LimeSite
@asynccontextmanager
async def lifespan(app: FastAPI):
global site
site = LimeSite() # LIME_SITE_TOKEN; loop running → dispatcher starts
@site.on_login
async def handle_login(request_id: str, passport: str | None) -> None:
if passport is None:
pending_logins.pop(request_id, None) # expired
return
verified = await site.verify_passport(
passport,
expected_request_id=request_id,
)
pending_logins[request_id] = verified.claims # or issue session cookie
yield
await site.aclose()
app = FastAPI(lifespan=lifespan)
# In-memory map: request_id → claims (use Redis/DB in production)
pending_logins: dict[str, object] = {}
@app.post("/login/start")
async def start_login() -> dict[str, str]:
req = await site.create_login_request()
# Pass req.request_id to agent worker (lime-agents-sdk approve + PoW)
return {"request_id": req.request_id}
Minimal example (asyncio.run)
import asyncio
from lime_sites import LimeSite
pending: dict[str, str | None] = {}
async def main() -> None:
site = LimeSite() # must be inside async main (running loop)
@site.on_login
async def on_login(request_id: str, passport: str | None) -> None:
pending[request_id] = passport
req = await site.create_login_request()
# hand req.request_id to agent; after approve, pending[req.request_id] is JWT
await asyncio.sleep(120) # replace with your app loop
await site.aclose()
asyncio.run(main())
End-to-end with lime-agents-sdk
import asyncio
from lime_agents import LimeAgent
from lime_sites import LimeSite
async def demo() -> None:
received = asyncio.Event()
box: dict[str, str] = {}
site = LimeSite()
@site.on_login
async def on_login(request_id: str, passport: str | None) -> None:
if passport:
box["jwt"] = passport
received.set()
req = await site.create_login_request()
async with LimeAgent() as agent:
await agent.approve(req.request_id) # agent solves PoW internally
await asyncio.wait_for(received.wait(), timeout=120)
verified = await site.verify_passport(
box["jwt"],
expected_request_id=req.request_id,
)
assert verified.valid
await site.aclose()
Handler contract
SSE type |
passport arg |
Your action |
|---|---|---|
approved |
agent JWT string | verify_passport(passport, expected_request_id=request_id) → session |
expired |
None |
Drop pending state for request_id |
keepalive |
(not delivered to handlers) | ignored by SDK |
Production deployment
- Create one
LimeSiteat process startup — not per HTTP request. - Optional shared
httpx.AsyncClientviahttp_clientfor connection pooling. - nginx
proxy_read_timeoutonGET .../eventsmust be ≥ 310s (LIME production default). SDK uses a dedicated 310s SSE read timeout and reconnects on drops. - Call
await site.aclose()only on shutdown.
import httpx
from lime_sites import LimeSite
http_client = httpx.AsyncClient(timeout=30.0)
site = LimeSite(http_client=http_client)
@site.on_login
async def on_login(request_id: str, passport: str | None) -> None:
...
Authentication
Site HTTP calls use X-Site-Token.
- Constructor
site_token="st_..." - Else env
LIME_SITE_TOKEN
Missing token → AuthenticationError at construct time (before loop check).
Configuration
| Variable | Purpose |
|---|---|
LIME_SITE_TOKEN |
Site integration token (required) |
LIME_API_BASE |
API root with /api/v1 (default https://lime.pics/api/v1) |
API reference
LimeSite
Constructor (keyword-only, inside running asyncio loop)
| Parameter | Default | Description |
|---|---|---|
site_token |
None |
Falls back to LIME_SITE_TOKEN |
base_url |
None |
Falls back to LIME_API_BASE |
timeout |
30.0 |
Per HTTP request timeout (seconds) |
max_retries |
3 |
Retries on 5xx / transport errors |
sse_backoff_base |
0.5 |
Initial SSE reconnect backoff (seconds) |
http_client |
None |
Inject httpx.AsyncClient for tests / pooling |
Methods
| Method | Description |
|---|---|
on_login(handler) |
Register async handler; use as @site.on_login decorator |
create_login_request() |
POST /modules/agent-login/requests → LoginRequestResult |
verify_passport(jwt, *, expected_request_id=None) |
JWKS RS256 verify → PassportVerificationResult |
aclose() |
Stop background dispatcher + close HTTP client |
Supports async with LimeSite(...) as site: (still requires running loop at __aenter__).
Types
| Type | Fields |
|---|---|
LoginRequestResult |
request_id, status, expires_at |
PassportVerificationResult |
valid, claims |
Errors
| Exception | When |
|---|---|
AuthenticationError |
Missing/invalid site token |
RequestExpiredError |
JWT / binding expired on verify |
InvalidPassportError |
JWT verify failure |
RateLimitError |
HTTP 429 |
ApiError |
Other API errors |
LimeError |
Base class |
RuntimeError if LimeSite() is called without a running event loop.
Development
pip install -e ".[dev]"
ruff check src tests
mypy src/lime_sites
pytest --cov=lime_sites --cov-fail-under=100
Live integration:
pip install lime-agents-sdk lime-sites-sdk
LIME_INTEGRATION=1 pytest tests/integration -v --ignore-glob='*'
Full cycle on production VPS (LIME monorepo): python scripts/_run_both_sdks_integration_remote.py --runs 3
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lime_sites_sdk-1.0.1.tar.gz.
File metadata
- Download URL: lime_sites_sdk-1.0.1.tar.gz
- Upload date:
- Size: 21.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73c7535f0514b0ecd24c6592b76b38a6bae5806be508788bf33455dc5a9e09a6
|
|
| MD5 |
880f711efb70c7af656b049b7b25d8bf
|
|
| BLAKE2b-256 |
6adbc71bc1c77b5f6c5844ebd853c28e0d0441c9f9ad371861271f8f5f947ed1
|
File details
Details for the file lime_sites_sdk-1.0.1-py3-none-any.whl.
File metadata
- Download URL: lime_sites_sdk-1.0.1-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3c35793fb927301ff3c402488cd7fe179af749b0a09b86d3ef842583622c9cf
|
|
| MD5 |
c463c42f122cf0215359ebc5d6199d1c
|
|
| BLAKE2b-256 |
0a01e09aa3c8bc21f3cdd45764d2cbd42531c874bdce0a4b9b799f1f241e17ac
|