Contributing
TenantLayer is Apache 2.0 and open to contributions. There are 26 unclaimed issues, and seven marked as a good way in.
The one rule that is not negotiable
Every isolation claim is mutation-tested. After a test goes green, break the implementation and confirm it goes red. A pull request that adds an isolation assertion should say, in its description, what you broke and that the test caught it.
This is a security library, and the failure mode is a test that passes for the wrong reason. Real examples from this codebase: a BUILD SUCCESS with zero tests executed; an @Async test that would have passed if the work never left the calling thread; an isolation assertion that passed with the library switched off entirely; and an entity scanner whose every lookup threw and was swallowed, so “no unprotected tables” and “the scan is broken” looked identical.
What will be rejected
A change can be well written, well tested and useful, and still be declined. The bar is not “does it work” but “can it fail open”.
It lets data be read with no tenant bound
No tenant must mean no rows, never all rows. Fail-closed is the whole promise.
It weakens a fail-closed default
Or adds a setting whose insecure value is the default. Insecure has to be something a person typed on purpose.
It adds a non-optional dependency
Spring Security, Kafka, WebFlux, Feign and Testcontainers are all optional and stay that way. A test enforces this by removing them from the classpath.
A test connects to Postgres as a superuser
Superusers bypass row-level security, so such a test passes whether or not isolation works — including after someone deletes the policy.
It asserts only the negative
"Tenant A sees none of B’s rows" is satisfied by an empty table. Pair it with "A sees its own rows".
None of these are hypothetical. Every one is a bug that has been found in this codebase and is now guarded by a test written to catch it.
How it works
- 1.Open an issue first — for anything beyond a typo. Scope here is opinionated, and it is kinder to hear “that belongs in the paid tier” before you write it than after.
- 2.Fork, branch, work — Branch naming is not policed.
- 3.Sign your commits off — git commit -s. It is the Developer Certificate of Origin, and CI checks it.
- 4.Open a pull request — The template asks what you broke to prove the test works. That is not a formality.
- 5.CI must be green — JDK 17 and 21, Hibernate 6 and 7, plus a build of the example against the packaged artifact.
- 6.A maintainer squash-merges — Your branch history stays yours; main stays readable.
There is no CLA, deliberately
Open-core projects usually require a contributor licence agreement so the company can move contributed code into its proprietary tier. This project does not, because its governing rule makes that unnecessary: free is correctness, paid is operations, and the free core is never crippled to sell the paid one.
Free-core code does not migrate into Pro. Pro ships from a separate repository as a separate artifact. So a CLA would buy nothing a Developer Certificate of Origin does not, while measurably deterring contributors.
Your contribution stays Apache 2.0, and it will not turn up behind a paywall.
Who decides
Suchait Gaurav is the maintainer and has the final say on scope and design. Worth stating rather than leaving you to infer it: you should know whose opinion you are arguing with, and that a well-reasoned disagreement is welcome and may still end in a no. Decisions about the free/paid boundary are made against the published placement rule and nothing else — if one looks inconsistent with it, saying so in an issue is a fair challenge.