The minimal example¶
The smallest application that is correct: FastAPI → JDLib → PostgreSQL, one tenant-scoped route, one denial answered, and a probe that does not need a token.
Run it¶
# 1. the database
docker run -d --name jdlib-minimal -p 5433:5432 \
-e POSTGRES_PASSWORD=... -e POSTGRES_DB=jdlib_minimal postgres:16
# 2. the environment (see examples/minimal/README.md for the exact variables)
export JDLIB_CONTROL_DSN=postgresql://...
# 3. the operator step, then the app
python -m examples.minimal.app.bootstrap --slug acme --name "Acme Inc"
uvicorn examples.minimal.app.asgi:app --host 127.0.0.1 --port 8000
What it contains¶
examples/minimal/
├── app.py the application: install(...), one guarded route, one unguarded probe
├── asgi.py uvicorn examples.minimal.app.asgi:app
├── bootstrap.py the operator step (the tenant, its tables, its record)
├── settings.py the environment, validated at startup
└── tests/
├── test_app.py the shape: the mount, the routes, the guard's declaration
├── test_denial_live.py the denial over a real chain, with an allowing control
└── test_bootstrap_live.py the operator step against a real database
What it demonstrates, and what it deliberately leaves out¶
It demonstrates the parts an application cannot get wrong: the install (middleware, guard, error envelope), one route whose permission is declared rather than checked inside the handler, a denial answered in the canonical envelope, and a probe that answers without credentials.
It leaves out everything an application can add later: a policy engine (the example's enforcer is a small in-process double, and the tests say so), a second tenant, a graph, MCP, and background jobs. Adding those is what the enterprise example is for.
What writing it found¶
Two library defects, both fixed in the library rather than worked around here: a ConnectionError
from the async driver was surfacing as a 500 instead of a 503, and the guard's jdlib_permission
declaration was not readable by the route walk the tests use. Both were found because the example
was run rather than described — which is the whole argument for having examples at all.