Skip to content

With and without JDLib

What the library is doing for you, side by side with the code you would otherwise write.

The "without" snippets are illustrative. They are marked as such deliberately, and they are never runnable production paths: the directive forbids shipping deliberately vulnerable code as an example, so nothing here is importable and no test executes it. They exist to make the difference legible, not to be copied. The "with JDLib" column names the seam that does the work — every entry is a real module, and the feature guides link to the page that owns it.

Authenticating a caller

Without — the shape a hand-written app drifts into:

# ILLUSTRATIVE, NOT RUNNABLE: parses a credential, compares it in a request handler,
# and trusts whatever the comparison says.
token = request.headers["authorization"].removeprefix("Bearer ")
row = db.execute("SELECT * FROM api_keys WHERE prefix = %s", (token.split(".")[0],)).fetchone()
if row and row["secret"] == token.split(".")[-1]:   # a plaintext comparison
    tenant = request.headers.get("x-tenant")        # a claim the caller controls

With JDLib — jdlib.authn + jdlib.tenancy: build_authenticator verifies the credential against its hash, the ResolverChain decides the tenant, and the ContextFactory builds a TenantContext that no request field can influence.

Authorizing an operation

Without — a check inside the handler, or a role read from the request:

# ILLUSTRATIVE, NOT RUNNABLE: the check is a code path someone can forget to take,
# and it runs after the operation has started.
if "admin" in request.headers.get("x-roles", ""):
    do_the_write()

With JDLib — require("resource:write") (jdlib.integrations.fastapi) declares the permission on the route; AuthorizationPEP asks the engine before the handler is entered, refuses a degraded answer, and the refusal is the canonical envelope.

Isolating tenants

Without — a filter per query, remembered at every call site:

# ILLUSTRATIVE, NOT RUNNABLE: one forgotten `WHERE tenant_id = ...` is a cross-tenant read.
rows = db.execute("SELECT * FROM resources WHERE kind = %s", (kind,)).fetchall()

With JDLib — jdlib.persistence.TenantRepository opens a tenant-bound session, install_rls puts the same rule in the database, and an unbound session reads nothing.

Answering a failure

Without — every layer inventing a shape:

# ILLUSTRATIVE, NOT RUNNABLE: the client gets a traceback, a table name, or both.
except Exception as error:
    return JSONResponse({"error": str(error)}, status_code=500)

With JDLib — install_error_envelope: one envelope (four keys), 4xx messages are the error's own text made printable and capped, 5xx never echoes its own text, and a 401 carries the RFC 6750 challenge.

Reaching a graph

Without — a driver in a handler, a label from input:

# ILLUSTRATIVE, NOT RUNNABLE: the tenant is a parameter, the label is interpolated,
# and the driver is the application's problem forever.
session.run(f"MATCH (n:{label} {{tenant: '{tenant_id}'}}) RETURN n")

With JDLib — jdlib.graph.GraphRepository (tenant-bound), a closed vocabulary from graph/mapping.py, and GraphTenantViolationError for a statement that would cross tenants.

Exposing a tool surface

Without — a second entry point with its own shortcuts:

# ILLUSTRATIVE, NOT RUNNABLE: the tool trusts an argument for identity and never asks policy.
@tool
def get_resource(tenant_id: str, resource_id: str) -> dict:
    return db.fetch_resource(tenant_id, resource_id)

With JDLib — McpTool declarations with a required capability, McpSecurityBoundary turning the credential into a SecurityContext, and the same engine answering the same question the HTTP route asks.

The honest summary

Concern What the library provides What remains yours
identity verification, the context, the failure vocabulary choosing the methods, the directory, the issuer
authorization the guard, the query, the PEP, fail-closed writing the policies, and testing them
tenancy resolution, the bound session, RLS choosing placement, provisioning tenants
errors one envelope, no internals mapping your own domain errors to it
audit the vocabulary and the writers the action names, and where the trail lives
reliability breaker, budget, gate, shutdown the thresholds, and what may degrade

The library does not decide your policies, your placement or your thresholds — those are deployment decisions. What it does is make the mechanism the same everywhere, so a decision cannot differ by protocol, by surface, or by whoever wrote the route.