AshAge.Multitenancy (AshAge v1.0.0)

Copy Markdown View Source

Resolves the AGE graph name for a :context-multitenant resource + tenant.

The resolved name is a Postgres schema (AGE graph), so it must be a valid AGE identifier, at most 63 bytes, injective (distinct tenants map to distinct graphs — a hard isolation invariant), and deterministic.

Default two-branch encoder:

  • pass-through — a tenant whose stringified form is identifier-body-clean ([A-Za-z0-9_]+) becomes "t_" <> tenant; the t_ prefix supplies the required letter start, so a leading digit (ULID, integer id) is fine. Keeps ULID/integer/slug tenants readable.
  • encode — any other tenant (hyphens/exotic, e.g. a UUID) becomes "g" <> Base.encode32(tenant, :lower, no-padding); alphabet [a-z2-7] is all valid, the g prefix guarantees a letter start, and a 36-byte UUID string fits at 59 bytes.

Injectivity: branch A always starts t, branch B always starts g, so the two namespaces are disjoint; each branch is a constant prefix over an injective input. Anything that will not fit 63 bytes fails closed with a value-free error, steering the host to the tenant_graph MFA override. Injectivity is over the stringified tenant (integer 42 and string "42" collide) — harmless in a homogeneous tenant space; the MFA is the escape hatch otherwise.

Summary

Functions

Returns the AGE graph name for resource and tenant.

Functions

graph_name(resource, tenant)

@spec graph_name(Ash.Resource.t(), term()) :: String.t()

Returns the AGE graph name for resource and tenant.

Uses the resource's tenant_graph MFA if configured, otherwise the default two-branch encoder. Always returns a valid AGE identifier or raises a value-free ArgumentError (fail-closed).