Skip to content

JDLib successor programme — current state

Measured at commit 15a7058dd2c1bb75ca853be6d8e6bfc30104e280 (test(audit): the doubles follow the protocol they stand in for), branch main, working tree clean apart from this programme's own .gitignore addition. Every number in this document was produced by a command run against that revision; nothing is copied from JDLib's own documentation. Method, raw commands and raw outputs: .recon/current-state.md (untracked by design — it names the archived library, which §1.2 of this programme forbids in published documentation).

This document is the baseline the rest of Phase 0 reasons from. It is deliberately a statement of what exists, not a plan: the plan is 05-roadmap.md.

1. What JDLib is today

A private, tenant-isolated enterprise foundation library for Python 3.12+, layered ports-and-adapters, async-first, consumed by applications as a path or private-wheel dependency. It is not a data-access toolkit and has never shipped: version 0.1.0, __version__ in src/jdlib/__init__.py.

Property Value
Public top-level API 13 symbols (jdlib.__all__): AccessControl, ContextFactory, JdlibError, TenancyConfig, TenantContext, TenantRegistry, TenantRepository, UnitOfWork, authorize, current_principal, current_tenant, errors, requires
Source size 101 Python files under src/jdlib/
Test size 112 Python files under tests/
Unit tests collected 869
Integration tests collected 331
Public namespaces 17 (see §3)
Extension points (ports) 34 (see §5)
CI jobs test, security

2. Repository state

  • Branch main; HEAD 15a7058; tree clean at the measured revision.
  • JDLib's own CI is green on this SHA (run 36020808274, both jobs).
  • The suite was not executed by this assessment — collection counts only. Full-suite execution belongs to the phase gates, and the integration layer needs Docker.
  • No coverage measurement exists anywhere in the tree: no [tool.coverage], no --cov in CI. Coverage percentage is therefore unknown, not zero and not high.

3. Public namespaces

Each namespace below is verified present in src/jdlib/; the description is derived from the module's own source, not from docs.

Namespace Provides
jdlib re-exports only; the 13-symbol surface above
jdlib.authn Authenticator port, OIDC/JWKS implementation, API-key authenticator, composite, wiring
jdlib.authz permissions catalog, resource types, scopes, access reader, PDP, request-scoped cache, guards, AccessControl, ownership
jdlib.control control-plane models, registry, purge, audit sink, provisioning/seed hooks
jdlib.integrations FastAPI wiring, CLI (typer)
jdlib.migrations programmatic Alembic runner, control and tenant baselines
jdlib.models tenancy ORM mixins, org/team, settings, audit, tenant-plane base
jdlib.persistence TenantSession, raw-SQL validator (pglast), SessionRouter, SecretProvider, UnitOfWork, TenantRepository, ORM events, isolation strategies
jdlib.persistence.strategies IsolationStrategy, shared / schema-per-tenant / database-per-tenant, RLS install and verify
jdlib.security the security-hardening programme's surface: audit, authn, authz, compliance, gateway, error taxonomy, tracing, config, metrics
jdlib.security.audit security event emitters, sinks, export
jdlib.security.authn JWT verification, signing-key source, provider
jdlib.security.authz PDP interfaces, enforcement point, transports
jdlib.security.compliance compliance control catalogue and reporting
jdlib.security.gateway gateway identity assertion handling
jdlib.tenancy request resolution chain, middleware, jobs
jdlib.testing fixtures, fakes, assertions for consumers

4. The pinned-surface constraint (binding on every phase)

tests/unit/security/test_security_public_surface.py asserts frozenset(jdlib.security.__all__) == EXPECTED_SURFACE by exact equality, and line 95 additionally asserts "SecurityConfig" not in jdlib.__all__. The top-level __all__ is a curated 13-symbol list.

Consequence for this programme: new public API must be added in new submodule namespaces, never by appending to jdlib.__all__ or jdlib.security.__all__. This makes every adoption additive by construction, and it is the reason §7 of this programme's naming document proposes new namespaces rather than extending existing pinned ones.

5. Extension points (ports) — the seams new capability must attach to

34 protocols are declared. The ones the successor phases touch:

Port Location Role
IsolationStrategy persistence/strategies/base.py:19 per-placement isolation adapter
SecretProvider persistence/secrets.py:7 secret reference → connection handle
PolicyDecisionPoint authz/pdp.py:30 and security/authz/interfaces.py:25 authorization evaluation
AccessReader authz/reader.py:28 actor authority reads for anti-escalation
Authenticator authn/base.py:10 credential verification
TenantResolver tenancy/resolution.py:24 one step of the resolution chain
PrincipalProvider tenancy/middleware.py:57 principal identification in middleware
PlatformAudit control/audit.py:27 control-plane audit sink
SecurityEventSink security/audit/emitters.py:44, security/audit/export.py:64 security event sinks (async emit)
WriteFence persistence/models.py:33 write admission fence
RawSqlAuditor persistence/session.py:26 raw-SQL observation in TenantSession
SeedHook control/registry.py:73 provisioning seed step
Clock context.py:113 injectable time source
TracerLike security/tracing.py:158 duck-typed OTel tracer
TokenValidator / TokenProvider / GatewayAdapter / RateLimiter security/interfaces.py:48/75/105/141 gateway and token seams
SigningKeySource security/authn/jwt.py:37 public signing keys by kid
ServabilityChecker / EntitlementChecker / OperatorAuthorizer / PrivilegeAudit context.py:166/170/174/178 ContextFactory collaborators
ScopeLookup / ResourceLookup / TenantWriteSession authz/access.py:53/73/85 ownership and anti-escalation surface
PrincipalDirectory / TenantDirectory context.py:158/162 identity and tenancy lookups

Two namespaces declare a PolicyDecisionPoint and two declare a SecurityEventSink. They serve different programmes (foundation vs security) and are not duplicates to be merged in this work — see 04-adoption-decisions.md ADR-9.

6. Dependencies

Core dependencies, each optional extra, and the declared entry points are as pyproject.toml states them (build backend, requires-python >= 3.12, [project.scripts], pytest11 plugin). One real defect was found by comparing declared dependencies against actual imports:

src/jdlib/integrations/fastapi.py:21 imports starlette.responses directly, and starlette is declared nowhere — neither in [project.dependencies] nor in any extra. It arrives transitively through fastapi today, so the integration works and the declaration is wrong.

This is the same class of defect the capability inventory found in the archived library (undeclared orjson, an undeclared pulsar). It is scheduled as a fix in 05-roadmap.md §1, because the successor phases add optional integrations and would multiply the pattern.

7. Documentation that exists

  • docs/jdlib/ — the approved design documents, and the constraint set for this programme: 02-relational-schema.md, 03-isolation-invariants.md, 04-authorization-model.md, 05-tenant-lifecycle.md, 06-strategy-matrix.md, 07-implementation-design.md.
  • docs/security/ — the hardening programme's phase documents and its certification.
  • docs/operations/, docs/compliance/, docs/threat-model/, docs/superpowers/.
  • docs/jdlib/successor/ — this programme's documents (created by Phase 0).

8. Known limitations carried into this programme

# Limitation Where recorded
L1 ~~Tyk OSS 5.3.1 has no coprocess runtime; its harness tests always skip.~~ Resolved 2026-09-25: the six Tyk tests pass. The gateway's 404s were TYK_GW_HOSTNAME (Tyk's default domain), the middleware never loaded because the fixture named an unknown driver (javascript rather than otto). Kong 3.6 and Tyk 5.3.1 are both verified. docs/security/gateway-hardening.md §6
L2 ~~The error-envelope gap (G10) is frozen by decision, not implemented.~~ Resolved 2026-09-25: both HTTP edges answer the documented envelope (code, message, request_id, correlation_id, plus the RFC 6750 challenge on a 401), a 5xx no longer echoes its own text, and the mapping the reference application carried itself moved into the library. docs/security/error-responses.md
L3 Integration-layer tests need live infrastructure (Cerbos, Kong, ZITADEL, collector) via JDLIB_INFRA_*; without them they skip. tests/infra/
L4 ~~No coverage measurement exists; the figure is unknown.~~ Resolved: scripts/ci-local.sh runs the suite with coverage (reported, never a threshold); the figure is the gate's own output. this document
L5 ~~starlette is imported but undeclared.~~ Resolved: declared in the fastapi extra (fastapi = ["fastapi>=0.111", "starlette>=0.37"]), because integrations/fastapi.py imports it directly. pyproject.toml
L6 ~~tests/integration has no infrastructure skip guard: without Docker the fixtures raise rather than skip.~~ Resolved: the directory skips cleanly with reasons and exit 0 (roadmap §1). .recon/current-state.md

9. What this programme must not redo

JDLib already implements, and this programme therefore keeps (no re-implementation, no parallel twin): multi-strategy tenancy and RLS; authentication (OIDC/JWKS/API-key) and the one-evaluator authorization model with anti-escalation rules; TenantSession / TenantRepository / UnitOfWork; SecretProvider; platform and tenant audit with fail-closed mandatory events; the security programme's audit/authz/tracing/compliance surface; migrations; the FastAPI and CLI integrations; the testing toolkit.

The successor phases address the capability areas JDLib does not have — resource hydration, caching, credential and cryptographic provider abstractions, connectors, object storage, a bounded query specification, background-job context envelopes, delegation — and they attach them at the seams in §5 rather than beside them.