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

# Configuration

Copper reads configuration from TOML files in the `./config` directory. A new project ships with `base.toml`, `dev.toml`, and `prod.toml`. Shared values go in `base.toml`, while each environment's file overrides what it needs.

### Extending Files

Config files declare what they build on with the `extends` key:

```toml config/dev.toml theme={null}
extends = ["base.toml"]

[rockets]
launch_site = "canaveral"
enable_telemetry = true
```

Running with `dev.toml` loads everything from `base.toml` plus the values above. The extending file itself wins when keys collide with an extended file; between files in the `extends` list, earlier ones win.

<Warning>
  Avoid top-level configuration values. Group everything under a section so that each package can load its own config without a shared "global" struct.
</Warning>

### Choosing a Config File

The `-config` flag selects which file to load. It defaults to `./config/dev.toml`:

```
./build/app.out -config=./config/prod.toml
```

For one-off overrides, you may use `-set` with TOML syntax. Separate multiple overrides with `;`:

```
./build/app.out -set "chttp.port=5902;clogger.format='json'"
```

### Reading Config in Your Package

Each package loads its own section with `cconfig.Loader`. By convention this lives in `config.go`:

```go pkg/rockets/config.go theme={null}
package rockets

import (
	"github.com/gocopper/copper/cconfig"
	"github.com/gocopper/copper/cerrors"
)

type Config struct {
	LaunchSite      string `toml:"launch_site"`
	EnableTelemetry bool   `toml:"enable_telemetry"`
}

func LoadConfig(configs cconfig.Loader) (Config, error) {
	var config Config

	err := configs.Load("rockets", &config)
	if err != nil {
		return Config{}, cerrors.New(err, "failed to load rockets config", nil)
	}

	return config, nil
}
```

Add `LoadConfig` to the package's `WireModule` and take `Config` as a field on any params struct that needs it.

If the section is missing from the config file, `Load` is a no-op and your struct keeps its zero values. For keys omitted from a section that is present, you may set defaults with the `default` tag. If you would like to cover a missing section as well, apply defaults after loading:

```go theme={null}
type Config struct {
	MaxCrew int `toml:"max_crew" default:"6"`
}
```

### Environment Variables

Config files are rendered as Go templates before parsing, with all environment variables available under `.EnvVars`:

```toml config/prod.toml theme={null}
[csql]
dsn = "{{ .EnvVars.DATABASE_URL }}"
```

### Secrets

There are two patterns for keeping secrets out of your repository.

**For development**, put secrets in a gitignored file and pull it in via `extends`:

```toml config/dev.toml theme={null}
extends = ["base.toml", "local.toml"]
```

Consider checking in a `local_example.toml` with the shape of the file and blank values, so that new teammates know what to fill in.

**For production**, use the `exec` template function to fetch secrets from a secret manager at startup. The command runs through `sh -c` and its trimmed stdout is substituted in:

```toml config/prod.toml theme={null}
[csql]
dsn = "{{ exec "gcloud secrets versions access latest --secret=DATABASE_URL" }}"
```

Since the secret is fetched when the app boots, it never touches your repository or your process environment.
