# `Lotus.Config`
[🔗](https://github.com/typhoonworks/lotus/blob/v0.16.6/lib/lotus/config.ex#L1)

Configuration management for Lotus.

Handles loading and validating configuration from the application environment
using `NimbleOptions`.

## Required Configuration

Lotus requires a storage repository where it will store query definitions:

    config :lotus,
      ecto_repo: MyApp.Repo  # Where Lotus stores its query definitions

## Data Repositories Configuration

Configure named data repositories that Lotus can execute queries against:

    config :lotus,
      data_repos: %{
        "primary" => MyApp.Repo,
        "analytics" => MyApp.AnalyticsRepo,
        "warehouse" => MyApp.WarehouseRepo
      }

## Visibility Configuration

Control which schemas and tables are accessible through Lotus with visibility rules:

    config :lotus,
      # Schema-level rules (higher precedence)
      schema_visibility: %{
        postgres: [
          allow: ["public", ~r/^tenant_/],  # Only public + tenant schemas
          deny: ["legacy"]                  # Block legacy schema
        ],
        mysql: [
          # In MySQL, schemas = databases
          allow: ["app_db", "analytics_db"],
          deny: ["staging_db"]
        ]
      },

      # Table-level rules (lower precedence)
      table_visibility: %{
        default: [
          deny: ["user_passwords", "api_keys", ~r/^audit_/]
        ],
        postgres: [
          allow: [
            {"public", ~r/^dim_/},      # Dimension tables only
            {"analytics", ~r/.*/}       # All analytics tables
          ]
        ]
      }

**Key Principle**: Schema visibility gates table visibility. If a schema is denied,
all tables within it are blocked regardless of table-level rules.

**Database-Specific Schema Behavior**:

- **PostgreSQL**: True namespaced schemas within a database (`public`, `reporting`, etc.)
- **MySQL**: Schemas = Databases (when you connect to MySQL, you can access multiple databases)
- **SQLite**: Schema-less (visibility rules don't apply)

See the [Visibility Guide](guides/visibility.html) for detailed configuration examples.

## Optional Configuration

    config :lotus,
      default_repo: "primary",       # Default data repo for queries
      unique_names: false,           # Defaults to true
      read_only: false               # Defaults to true; set to false to allow writes

# `cache_config`

```elixir
@type cache_config() :: %{
  optional(:cachex_opts) =&gt; keyword(),
  adapter: module() | nil,
  namespace: String.t(),
  profiles: %{required(atom()) =&gt; keyword()},
  compress: boolean(),
  max_bytes: non_neg_integer(),
  lock_timeout: non_neg_integer(),
  default_ttl_ms: non_neg_integer(),
  default_profile: atom()
}
```

# `t`

```elixir
@type t() :: %{
  ecto_repo: module(),
  read_only: boolean(),
  unique_names: boolean(),
  data_repos: %{required(String.t()) =&gt; module()},
  default_repo: String.t() | nil,
  default_page_size: pos_integer() | nil,
  table_visibility: map(),
  column_visibility: map(),
  schema_visibility: map(),
  cache: cache_config()
}
```

# `ai`

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

Returns AI configuration keyword list.

# `ai_enabled?`

```elixir
@spec ai_enabled?() :: boolean()
```

Returns whether AI features are enabled.

# `all`

```elixir
@spec all() :: t() | keyword()
```

Returns the entire validated configuration as a map.

Useful for debugging or inspection.

# `cache_adapter`

```elixir
@spec cache_adapter() :: {:ok, module()} | :error
```

Returns cache adapter module if configured.

# `cache_config`

```elixir
@spec cache_config() :: cache_config() | nil
```

Returns the cache configuration.

# `cache_namespace`

```elixir
@spec cache_namespace() :: String.t()
```

Returns the cache namespace.

# `cache_profile_settings`

```elixir
@spec cache_profile_settings(atom()) :: keyword()
```

Returns cache settings for a specific profile.

Falls back to built-in defaults for :results, :schema, and :options profiles.
Users can override these defaults in their configuration.

# `column_rules_for_repo_name`

```elixir
@spec column_rules_for_repo_name(String.t()) :: list()
```

Returns column visibility rules for a specific repository.

Falls back to default rules if repo-specific rules are not configured.

# `data_repos`

```elixir
@spec data_repos() :: %{required(String.t()) =&gt; module()}
```

Returns the configured data repositories.

# `default_cache_profile`

```elixir
@spec default_cache_profile() :: atom()
```

Returns the globally configured default cache profile.

Falls back to :results if none configured.

# `default_data_repo`

```elixir
@spec default_data_repo() :: {String.t(), module()}
```

Returns the default data repository as a {name, module} tuple.

- If default_repo is configured, returns that repo
- If default_repo is not configured, returns the first available repo
- If no data repos are configured, raises an error

# `default_page_size`

```elixir
@spec default_page_size() :: pos_integer() | nil
```

Returns the globally configured default page size for windowed pagination, if any.

When nil, Lotus uses its built-in default page size.

# `get`

```elixir
@spec get(atom()) :: any()
```

Gets a configuration value by key.

Returns the configuration for the given key from the application environment.

# `get_data_repo!`

```elixir
@spec get_data_repo!(String.t()) :: module()
```

Gets a data repository by name.

Returns the repo module or raises if not found.

# `list_data_repo_names`

```elixir
@spec list_data_repo_names() :: [String.t()]
```

Lists the names of all configured data repositories.

# `load!`

```elixir
@spec load!(keyword()) :: t() | keyword()
```

Loads and validates the Lotus configuration.

Raises `ArgumentError` if the configuration is invalid.

# `middleware`

```elixir
@spec middleware() :: map()
```

Returns middleware configuration map, or empty map if not configured.

# `read_only?`

```elixir
@spec read_only?() :: boolean()
```

Returns whether queries are restricted to read-only operations.

# `repo!`

```elixir
@spec repo!() :: module()
```

Returns the configured Ecto repository.

# `rules_for_repo_name`

```elixir
@spec rules_for_repo_name(String.t()) :: keyword()
```

Returns table visibility rules for a specific repository.

Falls back to default rules if repo-specific rules are not configured.

# `schema_rules_for_repo_name`

```elixir
@spec schema_rules_for_repo_name(String.t()) :: keyword()
```

Returns schema visibility rules for a specific repository.

Falls back to default rules if repo-specific rules are not configured.

# `unique_names?`

```elixir
@spec unique_names?() :: boolean()
```

Returns whether unique query names are enforced.

---

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