# `Arc.Tenancy`
[🔗](https://github.com/Cratis/Arc.Elixir/blob/main/lib/arc/tenancy.ex#L4)

Resolves which tenant a request is for.

Tenancy has three separate decisions. **Selection** (this module) resolves the tenant id.
**Membership authorization** checks that the caller may use it, and is yours to enforce.
**Storage isolation** decides where data lives: `ArcMongo` names the database after the tenant,
and Arc maps the tenant to the Chronicle event store namespace. None of these replaces the
others, and a resolver only selects: a caller-controlled header, query string or host is not
membership evidence.

The resolved tenant is `ctx.tenant`: a string, or `nil` when nothing selected one. `nil` and
`"Default"` are both the *default* tenant (see `default?/1`) and select the default database
and the default Chronicle namespace; they are not rejected.

## Configuration

The `:tenancy` option of the Arc application selects one resolver:

    config :my_app, MyApp.Arc,
      tenancy: [resolver: :header, header: "x-cratis-tenant-id"]

| Resolver | Options | Default |
| --- | --- | --- |
| `:header` | `:header` | `"x-cratis-tenant-id"` (or the `:tenant_header` option) |
| `:query` | `:query_parameter` | `"tenantId"` |
| `:claim` | `:claim_type` | `"tenant_id"` |
| `:subdomain` | `:base_domain` (required), `:header` | falls back to the header |
| `:fixed` | `:tenant_id` | `"development"` |
| `:development` | `:tenant_id` | `"development"`, same behavior as `:fixed` |

`resolver` may also be a module implementing this behaviour, for example one that consults a
session. The `:claim` resolver reads the principal, so authentication must have run; the
`:subdomain` resolver's fallback header is client-supplied, so strip it at your ingress.

A subdomain resolves a tenant only when it is exactly one DNS label in front of `:base_domain`:
`acme.myapp.com` is `acme`, while `myapp.com`, `acme.staging.myapp.com`, IP literals and
unrelated hosts such as `evil-myapp.com` fall back to the header. The base domain is required
and must be a registrable domain you own (at least two letter-digit-hyphen labels, never an
address literal); the application refuses to start otherwise, because without it every request
would silently take its tenant from the client-supplied header.

Hosts are matched after Unicode compatibility mapping (NFKC), the ideographic and fullwidth
full stops count as label separators and zero-width characters are ignored. Internationalized
labels are not converted to punycode: write them as `xn--` labels in configuration and traffic.

# `resolve`

```elixir
@callback resolve(Plug.Conn.t(), Arc.Principal.t(), keyword()) :: String.t() | nil
```

Selects the tenant id for a request.

`principal` is the authenticated caller (or anonymous) and `conn` the request.

# `chronicle_options`

```elixir
@spec chronicle_options(keyword(), String.t() | nil) :: keyword()
```

The Chronicle options that scope a call to the tenant: `namespace: tenant` for a named tenant,
nothing for the default one. An explicit `:namespace` in `chronicle` wins.

# `default?`

```elixir
@spec default?(String.t() | nil) :: boolean()
```

Whether a tenant is the default: not set, empty, or `"Default"`.

# `options`

```elixir
@spec options(keyword()) :: keyword()
```

The normalized tenancy options of an application configuration.

Raises `ArgumentError` when the configuration cannot select a tenant safely, for example a
subdomain resolver without a usable base domain.

# `resolve`

```elixir
@spec resolve(Plug.Conn.t(), Arc.Principal.t(), keyword()) :: String.t() | nil
```

Resolves the tenant of a request with the configured resolver.

# `validate!`

```elixir
@spec validate!(keyword()) :: :ok
```

Validates the tenancy configuration, raising `ArgumentError` when it is unusable.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
