tenant

package
v0.1.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MiddlewareWithBareHosts

func MiddlewareWithBareHosts(bare []string) func(http.Handler) http.Handler

MiddlewareWithBareHosts is TenantMiddleware for a server that KNOWS its own hostnames (the fleet runtimes: an app's manifest `domains`). A request whose Host is EXACTLY one of bare (no tenant label in front) passes through with no TenantCtx — like a bare "localhost" — instead of mis-reading the domain's first label as a tenant. Before this, `GET erp.example.com/admin` recorded a phantom tenant "erp" in observability (the S1 finding); the domain's OWN first label is not a tenant.

With bare empty this is byte-identical to the historical middleware — the single-engine chain (which has no domain knowledge) is unchanged, and the fleet chain adds one map lookup on a pre-parsed string (~ns, pre-auth).

func RequireForTenantAPI added in v0.1.3

func RequireForTenantAPI(next http.Handler) http.Handler

RequireForTenantAPI answers the tenant-scoped surfaces (/api/*, /graphql, /auth/*) with a NAMED 400 when the request carries no tenant — field report T3: `curl localhost:8080/api/x` used to reach the generated handlers, panic in MustFromCtx and surface as a masked `500 {"error":"internal error"}`, blaming the server for a malformed request (ADR-024: a bad input is a named 400, never a 500). Tenant-agnostic surfaces (/admin, /editor, /docs, probes, the fleet console) keep passing through untouched, which is why the tenant middleware itself must NOT reject a dotless host.

Install it AFTER MiddlewareWithBareHosts in the chain.

func TenantMiddleware

func TenantMiddleware(next http.Handler) http.Handler

TenantMiddleware extracts the tenant from the request's Host subdomain.

  • "acme.localhost:8080" → injects TenantCtx{ID:"acme", PGSchema:"tenant_acme"}
  • "ACME.localhost:8080" → the same tenant (the host is case-insensitive)
  • "localhost:8080" → passes through with no TenantCtx (health / control plane)
  • anything invalid → 400 naming the offending label and the rule

func WithContext

func WithContext(ctx context.Context, tc *TenantCtx) context.Context

WithContext returns a new context carrying tc. Used by TenantMiddleware and in tests.

Types

type SchemaCache

type SchemaCache struct {
	// contains filtered or unexported fields
}

SchemaCache holds the compiled API schemas for active tenants in memory. Updated by the migration worker after a successful migration; invalidated by pg_notify events so the data plane reloads on the next request.

func NewSchemaCache

func NewSchemaCache() *SchemaCache

func (*SchemaCache) Get

func (c *SchemaCache) Get(tenantID string) (*schema.APISchema, bool)

func (*SchemaCache) Invalidate

func (c *SchemaCache) Invalidate(tenantID string)

Invalidate removes a tenant's cached schema so the next request reloads it from DB.

func (*SchemaCache) Set

func (c *SchemaCache) Set(tenantID string, s *schema.APISchema)

type TenantCtx

type TenantCtx struct {
	ID       string // e.g. "acme"
	PGSchema string // "tenant_" + ID
}

TenantCtx holds the resolved tenant identity for a request.

func FromCtx

func FromCtx(ctx context.Context) *TenantCtx

FromCtx returns the TenantCtx from ctx, or nil if none was set.

func MustFromCtx

func MustFromCtx(ctx context.Context) *TenantCtx

MustFromCtx returns the TenantCtx from ctx, panicking if none was set. Use in handlers that are guaranteed to run after TenantMiddleware.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL