Releases: tenantlayer-io/tenantlayer
Release list
0.1.0 — first release
The tenant isolation layer for Spring Boot + Postgres.
Your query has no WHERE tenant_id. It returns only your tenant's rows anyway.
<dependency>
<groupId>io.tenantlayer</groupId>
<artifactId>tenantlayer-spring-boot-starter</artifactId>
<version>0.1.0</version>
</dependency>Getting started ·
Row-level security ·
All docs ·
API
Read Row-level security before deploying.
Isolation depends on your application connecting as a role that is neither a superuser
nor the table owner. Both bypass row-level security by design, and a suite written
against a superuser connection passes whether or not your policies work.
First release. Thirty of the thirty-four features on the v0.1 roadmap.
Verified on Java 17 and 21, Hibernate 6.6 and 7.0, Spring Boot 3.5, Postgres 16. Every
isolation claim is mutation-tested — the implementation was broken deliberately and the
test confirmed to fail.
Isolation
- Leak-proof Postgres RLS wiring: the tenant is set on every connection checkout rather
than reset on return, so a missed reset cannot ride a tenant back into the pool - No tenant bound resolves to no rows, never all rows
- One-shot RLS policy generation, emitting
FORCE ROW LEVEL SECURITY, anullifguard and
the index the predicate needs - Tenant-scoped entity scanning, with include/exclude overrides
- Hibernate
@TenantIddiscriminator strategy
Resolution and authorisation
- Header, subdomain, path-segment and JWT-claim resolvers, plus a pluggable
TenantResolverSPI - Ordered resolver chain where order is precedence
- Strict mode: an unresolvable tenant is rejected, never silently defaulted
- Membership verification — the resolved tenant is checked against the authenticated
principal, so a claimed tenant is not a granted one - Servlet filter ordering adapts automatically when resolution needs an authenticated
principal
Propagation
@Asyncand Spring task executors- Virtual threads, including the
SimpleAsyncTaskExecutorthat Boot substitutes when
spring.threads.virtual.enabled=true CompletableFutureand arbitrary executors viaTenantExecutors@Scheduledjobs viaTenantTasks.forEachTenant, which attempts every tenant and names
the ones that failed- Outbound
RestTemplate,RestClient,WebClientand Feign - Kafka produce and consume, including batch listeners, retries and recoverers
Registry, observability, testing
- Table-backed tenant registry carrying status, region, group, datasource reference and
metadata - MDC log enrichment
@WithTenant,IsolationAssertions.assertTenantCannotSee, andTenantPostgres
Testcontainers fixtures that hand out a least-privileged connection rather than a
superuser
Not included
- ScopedValue context backing. The storage SPI ships with the ThreadLocal
implementation;ScopedValueis a preview API until JDK 25 and shipping it would force
--enable-previewon every consumer. - Schema-per-tenant and database-per-tenant routing (v0.2 / v0.3)
- Reactor context propagation and Spring Batch (v0.2)
Known limitations
- Code that unwraps a pooled connection to a raw
PgConnectionis outside enforcement from
that point on - A superuser, or a table owner without
FORCE ROW LEVEL SECURITY, bypasses row-level
security by design — connect as a least-privileged role
Verifying this release
Every artifact is signed. To check it yourself:
gpg --keyserver keyserver.ubuntu.com --recv-keys 6464619381B491C9
gpg --verify tenantlayer-spring-boot-starter-0.1.0.jar.asc \
tenantlayer-spring-boot-starter-0.1.0.jarExpected: Good signature from "Suchait Gaurav <suchaitgaurav@gmail.com>",
key fingerprint 787D91F1 EAEB FAC8 265A 3AFA 6464 6193 81B4 91C9.
On the version number
0.1.0, not 1.0.0, and deliberately. Schema-per-tenant and database-per-tenant routing
are still to come, along with a configuration-driven strategy switch — and those will
change the shape of what you compile against. 0.x is the range that permits that
honestly. The API stabilises at 1.0, once all three isolation strategies exist and have
stress-tested the abstraction.
Found an isolation failure? Report it privately.