Tenancy Configuration
You can configure tenancy programmatically or through configuration files. Both approaches map to the same options and resolver types.
Programmatic configuration
Section titled “Programmatic configuration”This is a configuration fragment for an existing ArcApplicationBuilder (Core) or WebApplicationBuilder (ASP.NET Core), before Build(). Import Cratis.Arc and Cratis.Arc.Tenancy; each host uses builder.AddCratisArc(...), followed by app.UseCratisArc() on the built app.
builder.AddCratisArc(options =>{ options.UseHeaderTenancy("X-Custom-Tenant");
// options.UseQueryTenancy("tenant"); // options.UseClaimTenancy("tenant_id"); // options.UseSubdomainTenancy("myapp.com", "X-Custom-Tenant"); // options.UseFixedTenancy("acme"); // options.UseDevelopmentTenancy("test-tenant");});Configuration File (appsettings.json)
Section titled “Configuration File (appsettings.json)”{ "Cratis": { "Arc": { "Tenancy": { "ResolverType": "Header", "HttpHeader": "X-Custom-Tenant" } } }}Membership and trusted ingress
Section titled “Membership and trusted ingress”Register an application-owned ITenantMembership when Arc must verify that the effective caller may use a request-selected tenant. There is no default implementation. Without a hook, header, query and subdomain resolution behaves as before and logs one startup warning. Claim, fixed, development and custom resolvers do not produce this warning.
Set Cratis:Arc:Tenancy:IngressValidatesTenant to true only when a trusted ingress actually enforces caller-to-tenant membership and direct backend bypass is prevented. Cratis AuthProxy’s optional tenant verification checks existence, not caller membership; deploying behind it alone does not satisfy this condition. The setting is an explicit trusted operator assertion, not topology autodetection. Its default remains false, and it only suppresses the startup diagnostic; it does not validate requests, change resolution or bypass a registered hook. The warning is not automatic protection. See tenant membership for registration, backend isolation assumptions, synchronous execution and default-tenant handling.
Resolver-specific settings
Section titled “Resolver-specific settings”The following JSON fragments belong inside Cratis:Arc, not at the root of appsettings.json. The complete options tree lists defaults. Resolver configuration selects a tenant; membership authorization and storage isolation remain separate responsibilities.
Header Resolver
Section titled “Header Resolver”{ "Tenancy": { "ResolverType": "Header", "HttpHeader": "X-Custom-Tenant" }}Query Resolver
Section titled “Query Resolver”{ "Tenancy": { "ResolverType": "Query", "QueryParameter": "tenant" }}Claim Resolver
Section titled “Claim Resolver”{ "Tenancy": { "ResolverType": "Claim", "ClaimType": "tenant_id" }}Subdomain Resolver
Section titled “Subdomain Resolver”BaseDomain is the domain the application itself is served from. A host resolves a tenant only when it is exactly one label in front of it; every other host falls back to HttpHeader.
BaseDomain is required and must be the registrable domain you own — at least two letter-digit-hyphen labels, never an address literal. Leaving it out, or setting it to something no host could be matched against, throws BaseDomainIsNotADomainName and the host does not start — UseSubdomainTenancy throws where you call it, and a value that arrives from configuration is validated while the host is starting. Neither one lets a request through to take its tenant from the client-supplied HttpHeader instead. See the subdomain resolver for the full rules.
{ "Tenancy": { "ResolverType": "Subdomain", "BaseDomain": "myapp.com", "HttpHeader": "X-Custom-Tenant" }}Fixed Resolver
Section titled “Fixed Resolver”{ "Tenancy": { "ResolverType": "Fixed", "FixedTenantId": "acme" }}Development Resolver
Section titled “Development Resolver”{ "Tenancy": { "ResolverType": "Development", "DevelopmentTenantId": "local-tenant" }}FixedTenantId and DevelopmentTenantId are two names for the same value, so either key configures either resolver
type. Set only one of them - when a configuration source supplies both, whichever key the binder visits last wins.