> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formae.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Complete reference for the formae Pkl configuration file: server, datastore, discovery, synchronization, logging, telemetry, plugins, and artifacts.

formae is configured using a Pkl configuration file that controls agent behavior, datastore settings, discovery, synchronization, logging, and observability.

## Configuration format

The configuration file uses Pkl syntax and amends the base `formae:/Config.pkl` schema, which provides validation and defaults for all settings.

## Default configuration

Configuration lives under your formae config directory, `$HOME/.config/formae/` by default (it also honors `$XDG_CONFIG_HOME` and `$FORMAE_CONFIG_DIR`). Each configuration is stored as a named **profile** at `profiles/<name>.pkl`, and the active one is recorded in a plain-text `active` file alongside them.

The first time you run a command that needs configuration, formae creates a ready-to-use `default` profile pointed at a local agent (any pre-existing configuration is migrated into this layout automatically). Use the [`formae profile`](/documentation/reference/cli) command to create, switch between, and compare profiles, or `--profile <name>` to target one for a single command. Everything below describes the contents of a single profile file.

A minimal profile file looks like:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
amends "formae:/Config.pkl"

agent {
}

cli {
}
```

## Complete configuration reference

Below is a complete configuration showing all available options with their defaults:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
amends "formae:/Config.pkl"

agent {
    server {
        nodename = "formae"
        hostname = "localhost"
        port = 49684
        secret = "secret"
        tlsCert = null
        tlsKey = null
    }

    datastore {
        datastoreType = "sqlite"
        sqlite {
            filePath = "~/.pel/formae/data/formae.db"
        }
        postgres {
            host = "localhost"
            port = 5432
            user = "postgres"
            password = "admin"
            database = ""
            schema = ""
            connectionParams = ""
        }
        auroraDataAPI {
            clusterArn = ""
            secretArn = ""
            database = ""
            region = ""
        }
        mssql {
            host = ""
            port = 1433
            database = ""
            authMode = "sql"
            user = ""
            password = ""
            encrypt = true
            trustServerCertificate = false
            connectionParams = ""
            maxOpenConns = 4
            connMaxLifetime = 1.h
        }
    }

    synchronization {
        enabled = true
        interval = 5.min
    }

    discovery {
        enabled = true
        labelTagKeys = new Listing {
            "Name"
        }
        interval = 10.min
        resourceTypesToDiscover = new Listing { }
    }

    logging {
        filePath = "~/.pel/formae/log/formae.log"
        fileLogLevel = "debug"
        consoleLogLevel = "info"
    }

    oTel {
        enabled = false
        serviceName = "formae-agent"
        otlp {
            enabled = true
            endpoint = "http://localhost:4317"
            protocol = "grpc"
            insecure = true
            temporality = "delta"
        }
        prometheus {
            enabled = true
        }
    }

    retry {
        statusCheckInterval = 20.s
        maxRetries = 9
        retryDelay = 10.s
    }
}

cli {
    api {
        url = "http://localhost"
        port = 49684
    }
    theme = "quiet"
    appearance = "auto"
}

pluginDir = "~/.pel/formae/plugins"

artifacts {
    repositories {
        new {
            uri = "https://hub.platform.engineering/repos/platform.engineering/pel#stable"
            type = "binary"
        }
        new {
            uri = "https://hub.platform.engineering/repos/platform.engineering/community#stable"
            type = "formae-plugin"
        }
    }
}
```

<Note>
  Several optional sections have no defaults and are omitted above: [Authentication](#authentication) (`agent.auth`, `cli.auth`), [Resource plugins](#resource-plugins) (`agent.resourcePlugins`), and [Network](#network) (top-level `network`). See the respective sections below to configure them.
</Note>

## Configuration reference

### Server settings

Controls the agent's network configuration and cluster identity. Essential for multi-agent deployments.

| Property   | Type    | Default       | Description                                                        |
| ---------- | ------- | ------------- | ------------------------------------------------------------------ |
| `nodename` | String  | `"formae"`    | Unique identifier for this agent in the cluster                    |
| `hostname` | String  | `"localhost"` | Network interface where the agent listens for incoming connections |
| `port`     | Number  | `49684`       | Port number for the agent API                                      |
| `secret`   | String  | `"secret"`    | Cluster-wide authentication token for secure agent communication   |
| `tlsCert`  | String? | `null`        | Path to TLS certificate file for secure connections                |
| `tlsKey`   | String? | `null`        | Path to TLS private key file for secure connections                |

### Datastore

Choose between SQLite (default, suitable for single-agent), PostgreSQL (recommended for production with high availability requirements), Aurora Data API (for AWS deployments), or Microsoft SQL Server (for Azure deployments, including Azure SQL Database).

| Property        | Type   | Default    | Description                                                                         |
| --------------- | ------ | ---------- | ----------------------------------------------------------------------------------- |
| `datastoreType` | String | `"sqlite"` | Type of datastore to use: `"sqlite"`, `"postgres"`, `"auroradataapi"`, or `"mssql"` |

**SQLite configuration**

| Property   | Type   | Default                          | Description                      |
| ---------- | ------ | -------------------------------- | -------------------------------- |
| `filePath` | String | `"~/.pel/formae/data/formae.db"` | Path to the SQLite database file |

**PostgreSQL configuration**

| Property           | Type   | Default       | Description                          |
| ------------------ | ------ | ------------- | ------------------------------------ |
| `host`             | String | `"localhost"` | PostgreSQL server hostname           |
| `port`             | Number | `5432`        | PostgreSQL server port               |
| `user`             | String | `"postgres"`  | Database user for authentication     |
| `password`         | String | `"admin"`     | Database password for authentication |
| `database`         | String | `""`          | Database name to connect to          |
| `schema`           | String | `""`          | PostgreSQL schema to use             |
| `connectionParams` | String | `""`          | Additional connection parameters     |

**Aurora Data API configuration**

Use Aurora Data API for data access via AWS RDS Data API.

| Property     | Type   | Default | Description                                                            |
| ------------ | ------ | ------- | ---------------------------------------------------------------------- |
| `clusterArn` | String | `""`    | ARN of the Aurora Serverless cluster                                   |
| `secretArn`  | String | `""`    | ARN of the Secrets Manager secret containing database credentials      |
| `database`   | String | `""`    | Database name to connect to                                            |
| `region`     | String | `""`    | AWS region where the cluster is located (uses default region if empty) |

**Microsoft SQL Server configuration**

Use Microsoft SQL Server for self-hosted SQL Server instances or Azure SQL Database.

| Property                 | Type     | Default | Description                                                                                                         |
| ------------------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `host`                   | String   | `""`    | SQL Server hostname (for Azure SQL Database, the fully qualified server name, e.g. `myserver.database.windows.net`) |
| `port`                   | Number   | `1433`  | SQL Server port                                                                                                     |
| `database`               | String   | `""`    | Database name to connect to                                                                                         |
| `authMode`               | String   | `"sql"` | Authentication mode: `"sql"` (username and password) or `"workload-identity"` (Azure AD workload identity)          |
| `user`                   | String   | `""`    | Database user for SQL authentication                                                                                |
| `password`               | String   | `""`    | Database password for SQL authentication                                                                            |
| `encrypt`                | Boolean  | `true`  | Encrypt the connection with TLS                                                                                     |
| `trustServerCertificate` | Boolean  | `false` | Accept the server certificate without validation (use only for development and testing)                             |
| `connectionParams`       | String   | `""`    | Additional connection parameters                                                                                    |
| `maxOpenConns`           | Number   | `4`     | Maximum number of open connections in the pool                                                                      |
| `connMaxLifetime`        | Duration | `1.h`   | Maximum lifetime of a pooled connection                                                                             |

<Note>
  With `authMode = "workload-identity"`, the agent authenticates using the Azure AD workload identity available in its runtime environment, and the `user` and `password` properties are ignored. This is the recommended mode for Azure SQL Database when the agent runs on Azure infrastructure with a managed identity.
</Note>

### Synchronization

Enable synchronization of managed resources. When enabled, formae periodically checks if resources have been modified outside of the tool.

| Property   | Type     | Default | Description                                  |
| ---------- | -------- | ------- | -------------------------------------------- |
| `enabled`  | Boolean  | `true`  | Enable synchronization of managed resources  |
| `interval` | Duration | `5.min` | How frequently to check for external changes |

### Discovery

Automatically finds and catalogs infrastructure resources not yet under formae management. Useful for onboarding existing infrastructure.

Discovery scans targets that have been marked as `discoverable = true` in your forma files. See the [discovery documentation](/documentation/concepts/discovery) for details on configuring discoverable targets.

| Property                  | Type          | Default    | Description                                                                    |
| ------------------------- | ------------- | ---------- | ------------------------------------------------------------------------------ |
| `enabled`                 | Boolean       | `true`     | Enable automatic resource discovery                                            |
| `labelTagKeys`            | List\<String> | `["Name"]` | Tag keys to use when building resource labels (concatenated with dashes)       |
| `interval`                | Duration      | `10.min`   | How frequently to run discovery scans                                          |
| `resourceTypesToDiscover` | List\<String> | `[]`       | Specific resource types to discover (empty list discovers all supported types) |

### Logging

Configure log output levels and file locations.

<Tip>
  Keep the file log level at `debug` for troubleshooting support.
</Tip>

| Property          | Type   | Default                          | Description                                                            |
| ----------------- | ------ | -------------------------------- | ---------------------------------------------------------------------- |
| `filePath`        | String | `"~/.pel/formae/log/formae.log"` | Path to the log file                                                   |
| `fileLogLevel`    | String | `"debug"`                        | Log level for file output: `"debug"`, `"info"`, `"warn"`, `"error"`    |
| `consoleLogLevel` | String | `"info"`                         | Log level for console output: `"debug"`, `"info"`, `"warn"`, `"error"` |

### OpenTelemetry

Enable telemetry export to your observability platform. When enabled, metrics, logs, and traces are pushed via OTLP.

| Property      | Type    | Default          | Description                               |
| ------------- | ------- | ---------------- | ----------------------------------------- |
| `enabled`     | Boolean | `false`          | Enable OpenTelemetry integration          |
| `serviceName` | String  | `"formae-agent"` | Service name for telemetry identification |

**OTLP configuration**

| Property      | Type    | Default                   | Description                                                                                                         |
| ------------- | ------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `enabled`     | Boolean | `true`                    | Whether to enable OTLP push export for traces, metrics, and logs                                                    |
| `endpoint`    | String  | `"http://localhost:4317"` | OTLP collector endpoint URL                                                                                         |
| `protocol`    | String  | `"grpc"`                  | Protocol to use: `"grpc"` or `"http"`                                                                               |
| `insecure`    | Boolean | `true`                    | Whether to use insecure connections (disable TLS)                                                                   |
| `temporality` | String  | `"delta"`                 | Metric temporality: `"delta"` (OTel-native) or `"cumulative"` (for Prometheus/Mimir backends without delta support) |

**Prometheus configuration**

| Property  | Type    | Default | Description                                                   |
| --------- | ------- | ------- | ------------------------------------------------------------- |
| `enabled` | Boolean | `true`  | Enable Prometheus `/metrics` endpoint for pull-based scraping |

### Retry

Control how formae handles transient failures during resource operations. These values are the agent-wide defaults and can be overridden per plugin in [Resource plugins](#resource-plugins).

| Property              | Type     | Default | Description                                                  |
| --------------------- | -------- | ------- | ------------------------------------------------------------ |
| `statusCheckInterval` | Duration | `20.s`  | How frequently to check the status of a resource operation   |
| `maxRetries`          | Number   | `9`     | Maximum number of retries for a recoverable failed operation |
| `retryDelay`          | Duration | `10.s`  | How long to wait before retrying a failed operation          |

### Resource plugins

`agent.resourcePlugins` is a list of per-plugin configuration blocks that override defaults published by each plugin. Each installed plugin ships its own typed configuration that you import through the `plugins:/` scheme. Any plugin that is not listed uses its built-in defaults.

Available overrides on every resource plugin:

| Property                  | Type                | Description                                                                                                                                                                                   |
| ------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`                 | Boolean             | Set to `false` to disable the plugin. When disabled no other fields apply. Default: `true`.                                                                                                   |
| `rateLimit`               | RateLimitConfig?    | Cap the request rate this plugin issues to its provider. `maxRequestsPerSecondForNamespace` is the per-target cap.                                                                            |
| `retry`                   | RetryConfig?        | Override the agent-wide [retry](#retry) settings for this plugin.                                                                                                                             |
| `resourceTypesToDiscover` | List\<String>?      | Restrict which resource types this plugin reports during discovery. An empty/absent value means "all supported types".                                                                        |
| `discoveryFilters`        | List\<MatchFilter>? | Exclude specific discovered resources by matching on properties (for example, skipping resources that carry a Kubernetes-owner tag).                                                          |
| `labelConfig`             | LabelConfig?        | Customize how human-readable labels are generated for discovered resources. `defaultQuery` is a JSONPath query; `resourceOverrides` sets per-resource-type queries that win over the default. |

Plugins may also expose **plugin-specific** fields defined in their own `schema/Config.pkl`. These appear alongside the fields above on the same configuration block.

#### Disabling a plugin

Use `enabled = false` to stop the agent from loading a plugin. No other options apply in this case.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
amends "formae:/Config.pkl"

import "plugins:/Azure.pkl" as Azure

agent {
    resourcePlugins {
        new Azure.PluginConfig {
            enabled = false
        }
    }
}
```

#### Tuning an installed plugin

This example uses the SFTP plugin because it exposes plugin-specific fields (`defaultTimeoutSeconds`, `defaultFilePermissions`) in addition to the common overrides.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
amends "formae:/Config.pkl"

import "plugins:/Sftp.pkl" as Sftp

agent {
    resourcePlugins {
        new Sftp.PluginConfig {
            // Common overrides
            rateLimit {
                maxRequestsPerSecondForNamespace = 5
            }
            retry {
                maxRetries = 3
                retryDelay = 30.s
            }

            // Plugin-specific fields defined by the SFTP plugin
            defaultTimeoutSeconds = 60
            defaultFilePermissions = "0600"
        }
    }
}
```

You can combine multiple plugin blocks under a single `resourcePlugins { ... }` listing. The `type` field is automatically set by each plugin's wrapper, so you only need to fill in the overrides you care about.

#### labelConfig

`labelConfig` customizes how human-readable labels are generated for resources discovered by this plugin. The query is a [JSONPath](https://www.rfc-editor.org/rfc/rfc9535.html) expression evaluated against the resource's properties. Each plugin ships a sensible default (for example, the AWS plugin uses the `Name` tag, `$.Tags[?(@.Key=='Name')].Value`). Override per resource type when the plugin's default doesn't suit your environment, or set `defaultQuery` to change the rule for every type:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
amends "formae:/Config.pkl"

import "plugins:/Aws.pkl" as Aws

agent {
    resourcePlugins {
        new Aws.PluginConfig {
            labelConfig {
                resourceOverrides {
                    ["AWS::EC2::Instance"] = "$.Tags[?(@.Key=='env')].Value"
                }
            }
        }
    }
}
```

<Note>
  When the query returns nothing, formae falls back to the resource's provider identifier as the label. See [Label: Labels for discovered resources](/documentation/concepts/label#labels-for-discovered-resources) for the full resolution order and collision behavior.
</Note>

### CLI

Configure how the CLI connects to the agent API, and how its output is themed. See [Authentication](#authentication) for `cli.auth`.

| Property     | Type   | Default              | Description                                                                                                                                                                                                           |
| ------------ | ------ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api.url`    | String | `"http://localhost"` | Base URL for the agent API                                                                                                                                                                                            |
| `api.port`   | Number | `49684`              | Port number for the agent API                                                                                                                                                                                         |
| `theme`      | String | `"quiet"`            | CLI theme: a built-in (`"quiet"`, `"rich"`, `"colorblind"`) or the filename of a theme in `~/.config/formae/themes/`. Unknown names warn and fall back to `quiet`. See [CLI themes](/documentation/reference/themes). |
| `appearance` | String | `"auto"`             | Which color variant to use: `"light"`, `"dark"`, or `"auto"` (detect the terminal background). Orthogonal to `theme`. The `FORMAE_APPEARANCE` environment variable overrides it.                                      |

### Authentication

Authentication is configured separately for the agent and CLI. The `agent.auth` block controls server-side validation; the `cli.auth` block provides client-side credentials. Both fields are optional. If unset, the agent serves unauthenticated requests.

Each auth plugin ships its own typed configuration that you import through the `plugins:/` scheme, the same pattern used for [Resource plugins](#resource-plugins). The example below uses the [auth-basic](https://github.com/platform-engineering-labs/formae-plugin-auth-basic) plugin, which is the default HTTP Basic Authentication option.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
amends "formae:/Config.pkl"

import "plugins:/AuthBasic.pkl" as AuthBasic

agent {
    auth = new AuthBasic.AgentConfig {
        authorizedUsers {
            new AuthBasic.AuthorizedUser {
                username = "alice"
                // bcrypt hash, generate with:
                //   htpasswd -bnBC 10 "" yourPassword | tr -d ':'
                password = "$2y$10$ki1wCrM94EViuTv0dRNEVuP3ujj2/uu2Zh8/FyFvExjZyrsdtr1SS"
            }
        }
    }
}

cli {
    auth = new AuthBasic.CliConfig {
        username = "alice"
        password = "mySecretPass"
    }
}
```

See [Security and networking](/documentation/reference/security-and-networking#authentication) for more detail on the auth-basic setup.

### Network

Top-level network configuration. The `type` field selects the network plugin.

| Property            | Type             | Default | Description                              |
| ------------------- | ---------------- | ------- | ---------------------------------------- |
| `network.type`      | String           | none    | Network plugin type (e.g. `"tailscale"`) |
| `network.tailscale` | TailscaleConfig? | `null`  | Tailscale-specific configuration         |

See [Tailscale](/documentation/reference/security-and-networking#tailscale-experimental) for setup instructions.

### Plugin directory

Top-level setting for where formae discovers installed plugins.

| Property    | Type   | Default                   | Description                                                                                               |
| ----------- | ------ | ------------------------- | --------------------------------------------------------------------------------------------------------- |
| `pluginDir` | String | `"~/.pel/formae/plugins"` | Directory where installed plugins live. Can also be set via the `FORMAE_PLUGIN_DIR` environment variable. |

### Artifact repositories

Top-level `artifacts` block configures the orbital repositories formae consults to install and update its binary, plugins, and tooling. Both the agent and the CLI share this configuration; `formae plugin install`, `formae update`, and `formae plugin upgrade` all resolve packages against the listed repositories.

| Property                 | Type                  | Default                                                                                              | Description                                                                                                                                                               |
| ------------------------ | --------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `artifacts.repositories` | `Listing<Repository>` | `pel` (binary) and `community` (formae-plugin) on `hub.platform.engineering` at the `stable` channel | List of orbital repositories to consult for packages. The defaults cover both binary and plugin distribution; only override when running against a private or custom hub. |

Each entry in `artifacts.repositories` is a `Repository` with two fields:

| Property | Type                            | Description                                                                                                                                                                             |
| -------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `uri`    | String                          | Orbital repository URI; the optional `#channel` fragment selects the release channel (typically `stable` or `dev`).                                                                     |
| `type`   | `"binary"` \| `"formae-plugin"` | Discriminator. `binary` repos serve the formae binary and tooling (consulted by `formae update`); `formae-plugin` repos serve plugins (consulted by `formae plugin install / upgrade`). |

The default value is equivalent to:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
artifacts {
    repositories {
        new {
            uri = "https://hub.platform.engineering/repos/platform.engineering/pel#stable"
            type = "binary"
        }
        new {
            uri = "https://hub.platform.engineering/repos/platform.engineering/community#stable"
            type = "formae-plugin"
        }
    }
}
```

A typical override that points at a private hub mirroring the same shape:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
artifacts {
    repositories {
        new {
            uri = "https://hub.example.com/repos/example.com/pel#stable"
            type = "binary"
        }
        new {
            uri = "https://hub.example.com/repos/example.com/community#stable"
            type = "formae-plugin"
        }
    }
}
```

#### Deprecated flat fields

Earlier releases configured a single repository via flat `artifacts.url`, `artifacts.username`, and `artifacts.password` fields. Those fields are still accepted for backwards compatibility but emit deprecation warnings on agent startup, and they cover only the binary repository: plugin installation requires the new `repositories` listing. Migrate by replacing:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
artifacts {
    url = "https://hub.platform.engineering/repos/platform.engineering/pel#stable"
}
```

with the explicit listing shown above. The `username` and `password` fields have no like-for-like replacement in 0.85; per-repository credentials will be reintroduced in a later release.

### Deprecated `plugins` block

Earlier releases configured authentication, network access, and the plugin directory under a single top-level `plugins { ... }` block. That block is still accepted for backwards compatibility but emits deprecation warnings at startup. Migrate as follows:

| Old location             | New location              |
| ------------------------ | ------------------------- |
| `plugins.authentication` | `agent.auth` + `cli.auth` |
| `plugins.network`        | top-level `network`       |
| `plugins.pluginDir`      | top-level `pluginDir`     |
