zhortein/multi-tenant-bundle
A multi-tenant bundle for PHP applications that helps isolate tenants, resolve them per request, and manage tenant-aware configuration and database connections. Useful for building SaaS platforms with clean tenant separation and less boilerplate.
This is a prerelease (v1.0.0-rc.12) of the multi-tenant bundle. Pin the exact version rather than floating on ^1.0 — a stable tag will renumber things.
composer require zhortein/multi-tenant-bundle:v1.0.0-rc.12. If your root minimum-stability is stable, add an explicit constraint for this package or a prefer-stable allowlist.docs/object-storage-audit.md (the public audit contract — this defines the interfaces you'll code against) and docs/migration-rc11-to-rc12.md (adoption and rollback — read the rollback half before you enable anything).TenantObjectStorageAuditInterface.The first real use case is a tenant location inventory report: walk the lazy paginated inventory for one tenant, collect structured per-object observations, and render them. Do this against a non-production bucket before you point it at anything you care about — see the pagination-failure behavior in Gotchas.
Paginated inventory as a generator, not an array. The interface is explicitly lazy and paginated. Consume it in a foreach/cursor loop and flush per page (queue job per page, or stream to a report writer). Do not call ->all() mentally — treat the page size as the unit of memory and the unit of retry.
Keep two read paths during rollout. RC11 objects remain readable, but they will surface as identity_absent. Design your reporting layer to accept three states: identity present, identity_absent (historical, expected), and anomaly-sanitized. Do not collapse identity_absent into an error or a null-provenance row — the package is deliberately refusing to invent provenance, and your downstream schema should mirror that refusal.
Write with explicit content plus identity. New writes should construct a versioned LogicalObjectIdentity and use the explicit content-plus-identity write path rather than the legacy implicit path. Version the identity type in your own domain model too, so a future RC13 identity bump doesn't silently change meaning in your stored metadata.
Use the authenticated claim for correlation only. Thread your application correlation ID through the claim to join audit observations back to your own request/job records. Do not use the claim as an integrity attestation — the release notes are explicit that it does not attest content and does not prevent replay by a storage administrator. If your design needs tamper-evidence, that is a separate control you must build.
Scope history explicitly. Historical scopes are opt-in and named. Wrap historical queries in a separate service or repository method so that production "current state" reads never accidentally inherit a historical scope.
Integration seams. The bundle continues to compose with Messenger/Scheduler and the lifecycle hooks; audit is a sibling capability, not a replacement for lifecycle. Feature-flag audit at the tenant level if your tenancy model allows it — the interface is tenant-scoped, and a per-tenant flag gives you a staged rollout without redeploying.
Testing. The release notes call out that critical object-storage/MinIO tests and consumer recipes reject skips, while the wider suite still contains disclosed placeholder skips and PHPUnit mock notices. Mirror that discipline: fail your CI on skipped audit tests, and treat any mock-notice noise in the audit path as a signal to inspect rather than suppress.
No migrations, in either direction. No SQL, object, or metadata migration runs automatically, and none is shipped. That's convenient for adoption and dangerous for rollback: if you start writing identity-bearing objects, rolling back to RC11 leaves those objects in place with no automated unwinding. The rollback guide tells you what that means; the code will not protect you from it.
Invalid pagination or tenant scope fails the whole page. Deliberate design choice — no partial pages, no silent truncation. Catch and handle at the page boundary, and make sure your loop is idempotent so a failed page can be retried without duplicating report rows. Contrast with isolable entry anomalies, which are sanitized rather than fatal; know which of the two failure classes you're looking at before you add a retry wrapper.
identity_absent is not an error. It is the correct answer for every RC11-era object. Alerting on it will page you constantly on day one of a backfill-free rollout.
The claim is weaker than it looks. Authenticated claim ≠ content attestation ≠ replay protection against a storage administrator. If a compliance or audit stakeholder reads "audit" and assumes cryptographic provenance, correct them now, not at review time.
Nothing operational is included. No downstream integration, no deployment logic, no repair, and no orphan deletion. If you were hoping this release would clean up dangling objects, it does not, and the notes say so deliberately.
Distribution and provenance. Only GitHub-generated source archives are attached — no additional build asset. Verify against the exact SHAs recorded in the promotion PR rather than trusting the tag name. Note also that release review is author self-review with no independent human approval, and the release notes disclose it themselves. For a prerelease that's tolerable; for the eventual stable tag, treat independent review as a precondition on your side before adoption.
Config quirks to check on first boot: confirm the audit flag's actual key name in your installed version's config reference rather than copying from docs — RC docs and shipped config have drifted in prerelease cycles before, and a silently-defaulted-off flag is the most common "feature doesn't work" report here.
Extension points. LogicalObjectIdentity is versioned by design, so the intended extension point is your own domain layer wrapping it, not subclassing it. For custom observation handling, decorate the audit interface in your container rather than forking the bundle — that keeps rollback to RC11 a pure config and compose change.
How can I help you explore Laravel packages today?