Plans, seats and tenants are business rules too.
Entitlements live in billing, in feature flags and in your own tables. Downgrades, imports and admin tools are where a customer ends up with more than they pay for, or with someone else’s data. Invarly checks both, continuously.
Why SaaS breaks quietly
A B2B product is a set of promises: this customer has these features, this many seats, and only sees their own data. Those promises are enforced in many places at once, from middleware to background jobs to the admin panel your support team uses.
When one path forgets a check, nothing breaks for the customer. They simply get more than they should, or see a row that isn’t theirs. Both are invisible until an audit, a renewal, or a screenshot on social media.
Five places it breaks
Cross-tenant writes
A bulk import or background job takes the organisation from the wrong place, and rows land in another tenant.
Paid features after a downgrade
The billing webhook updates the plan, but cached entitlements or feature flags keep the old access.
Seats past the plan
An SSO or SCIM sync adds users without checking the seat limit.
Billing and app disagree
A subscription is cancelled at the billing provider but still active in your database.
Tables without row-level security
A new table ships without the policy every other table has.
A walkthrough
A bulk import tool is changed to reuse the organisation from the admin’s session to save a lookup. Support staff import orders on behalf of customers.
- all 3 orders were created by bulk import from one support account
- traced to a1f9c2e “reuse org from session”
importer/bulk.go:88 - blast radius 3 orders · 3 tenants
The rule catches the first misplaced row, not the hundredth, and the investigation shows exactly which code path wrote it.
The starter rule pack
The rules most teams in this space start with. Each one is a query against your own tables, adjusted to your schema during setup.
| rule | protects | runs |
|---|---|---|
| row-org-matches-parent | Tenant isolation | 5 min |
| features-within-plan | Revenue leakage | 15 min |
| seats-within-plan | Seat overages | 1 hour |
| subscription-matches-billing | Billing drift | 1 hour |
| rls-on-every-table | Data exposure | on each deploy |
| admin-in-approved-list | Privilege creep | 1 hour |
How it fits your stack
The agent connects to your application database with a read-only user. Rules about tenancy run on your own tables; rules about billing compare them with the billing data you already store.
Security teams get a continuous check on tenant isolation, not just a yearly pen test.
Common questions
Does it replace a pen test?
No. A pen test looks for ways in. Invarly checks that the data already in your system still respects the rules.
We use row-level security. Is that enough?
It covers reads through the database. Rules also catch writes from jobs and admin tools that bypass it, and tables that never got a policy.
Can it read our billing provider?
Through the data you already sync, for example subscription tables updated by webhooks.