Tenant-core: a native multi-tenancy toolkit for Go

Hi everyone,

I’d like to share tenant-core, a Go toolkit for multi-tenant SaaS applications.

**The problem it addresses:** in a multi-tenant system, every request needs to resolve which tenant it belongs to, and every subsystem (cache, rate limits, permissions, metrics) needs to respect that boundary consistently. Most teams end up building this incrementally and inconsistently.

**What it provides:**

- Tenant resolution (subdomain-based resolver included), propagated via `context.Context`

- A `Store`/`CachedStore` abstraction with a singleflight-based cache to avoid stampedes

- Real-time ban propagation across instances via an `EventBus` (in-memory or Redis)

- Per-tenant RBAC (roles are scoped per tenant, not global)

- Per-tenant rate limiting, with an optional Redis-backed distributed variant

- A framework-agnostic Admin API with pluggable authentication

- Adapters for `net/http`, Gin, Echo, and Chi — the core has zero framework dependency

**Design choices worth mentioning:**

- Interfaces live in the core package; concrete implementations (Redis, Prometheus) live in separate Go submodules, so consumers only pull in what they use

- Concurrency-sensitive paths (singleflight under cache misses, `sync.Map` contention for the rate limiter) were benchmarked and profiled rather than assumed

- The architecture doc is explicit about current limitations (e.g. the event retry queue is best-effort in-memory, not a durable Outbox)

It’s `v0.2.0`, about a month old — not claiming API stability yet, and I’m already working on a breaking change (`Permission` as a defined type instead of `string`) based on feedback from r/golang.

Repository: GitHub - sylvinhio676-ux/tenant-core: tolkit pour l'integration du multi tenant dans des plateforme en go · GitHub

Getting started: tenant-core/GETTING_STARTED.md at main · sylvinhio676-ux/tenant-core · GitHub

Architecture doc: tenant-core/docs/ARCHITECTURE.md at main · sylvinhio676-ux/tenant-core · GitHub

Feedback and criticism very welcome.

1 Like

This looks like a thoughtful approach to a problem that often becomes messy as a SaaS application grows. I particularly like keeping the core framework-agnostic and making Redis/Prometheus Official Website integrations optional. Being explicit about limitations like the best-effort event queue is also a strong sign of a practical project. I’ll be interested to see how the API evolves as more people use it.

Thank you, Robert — glad the “best-effort, not durable” framing on the retry queue came through clearly. That distinction mattered a lot to me; I didn’t want to call something an Outbox pattern if it doesn’t actually give that guarantee.

The core-agnostic-with-optional-adapters split has been the one architectural rule I’ve tried hardest not to compromise on, even when it would’ve been simpler to just bake Redis in directly. Good to hear it reads as intentional rather than just extra indirection.

Already got some good feedback elsewhere (Gophers Slack) that led to a couple of API changes in v0.3.0 — typed Permission/Role instead of raw strings, plus fuzz testing on the RBAC invariants. If you spot anything that feels off as you look closer, I’d genuinely welcome the pushback.