# Configuration reference shithubd reads configuration from four sources, in increasing precedence: 1. **Built-in defaults** (`internal/infra/config/Defaults()`). 2. **TOML file** at `$SHITHUB_CONFIG` (default `/etc/shithub/config.toml`). Absent is fine; bad syntax is fatal. 3. **Environment variables.** `SHITHUB___` — note double underscore between nesting levels. 4. **CLI flags** passed by the caller. After merging, a small alias table maps backward-compat names (e.g., `SHITHUB_DATABASE_URL` → `db.url`); then the resolved config is validated. Any failure exits non-zero with a one-line error pointing at the offending key. ## Inspecting the active configuration ```sh shithubd config print # writes resolved config as TOML, secrets redacted shithubd config validate # exits non-zero if invalid shithubd version # one-line summary of which sinks are configured ``` `config print` redacts any field whose name contains `password`, `pass`, `secret`, `key`, `token`, `dsn`, or `url` — URL fields are redacted because they often carry credentials in the userinfo component. ## Reference | Key | Type | Default | Notes | |---|---|---|---| | `env` | string | `dev` | One of `dev`, `staging`, `prod`. | | `web.addr` | string | `:8080` | Listen address. | | `web.read_timeout` | duration | `30s` | Per-request read timeout. | | `web.write_timeout` | duration | `30s` | Per-request write timeout. | | `web.shutdown_timeout` | duration | `10s` | Graceful drain on SIGTERM. | | `db.url` | string | `""` | Postgres DSN. Aliased by `SHITHUB_DATABASE_URL`. | | `db.max_conns` | int | `10` | pgxpool max conns. | | `db.min_conns` | int | `0` | pgxpool min conns. | | `db.connect_timeout` | duration | `5s` | | | `log.level` | string | `info` | One of `debug`, `info`, `warn`, `error`. | | `log.format` | string | `text` | One of `text`, `json`. Use `json` in prod. | | `metrics.enabled` | bool | `true` | Mounts `/metrics`. | | `metrics.basic_auth_user` | string | `""` | Together with `pass`, gates `/metrics`. | | `metrics.basic_auth_pass` | string | `""` | Redacted on print. | | `tracing.enabled` | bool | `false` | When true, `tracing.endpoint` is required. | | `tracing.endpoint` | string | `""` | OTLP HTTP endpoint. | | `tracing.sample_rate` | float | `0.05` | Parent-based ratio sampler in [0, 1]. | | `tracing.service_name` | string | `shithubd` | OTel resource attribute. | | `error_reporting.dsn` | string | `""` | Sentry-protocol DSN. Empty disables. | | `session.key_b64` | string | `""` | Base64 32-byte AEAD key. **Required in prod.** | | `session.max_age` | duration | `720h` | Cookie session lifetime (30 days). | | `session.secure` | bool | `false` | Set `Secure` cookie attribute. **True in prod.** | | `storage.repos_root` | string | `/data/repos` | Filesystem root for bare repos. | | `storage.s3.endpoint` | string | `""` | S3-compatible endpoint host[:port]. Empty disables S3. | | `storage.s3.region` | string | `us-east-1` | Region for SigV4 signing. | | `storage.s3.access_key_id` | string | `""` | | | `storage.s3.secret_access_key` | string | `""` | Redacted. | | `storage.s3.bucket` | string | `""` | One bucket per environment. | | `storage.s3.use_ssl` | bool | `false` | True for Spaces, false for MinIO. | | `storage.s3.force_path_style` | bool | `true` | True for MinIO, false for Spaces. | | `auth.require_email_verification` | bool | `true` | Login rejected until primary email verified. | | `auth.base_url` | string | `http://127.0.0.1:8080` | Used in transactional email links. **Set to your public URL in prod.** | | `auth.site_name` | string | `shithub` | Branding token in email subjects. | | `auth.email_from` | string | `shithub ` | Envelope From. | | `auth.email_backend` | string | `stdout` | One of `stdout`, `smtp`, `postmark`. | | `auth.smtp.addr` | string | `127.0.0.1:1025` | Required when backend=`smtp`. | | `auth.smtp.username` | string | `""` | Optional. | | `auth.smtp.password` | string | `""` | Optional. Redacted. | | `auth.postmark.server_token` | string | `""` | Required when backend=`postmark`. Redacted. | | `auth.argon2.memory_kib` | uint32 | `65536` | argon2id memory cost (KiB). | | `auth.argon2.time` | uint32 | `3` | argon2id iterations. | | `auth.argon2.threads` | uint8 | `2` | argon2id parallelism. | | `auth.totp_key_b64` | string | `""` | Base64 32-byte AEAD for TOTP secrets. **Required if 2FA is in use.** | ## Examples ```sh # Listen elsewhere export SHITHUB_WEB__ADDR=:9090 # Postgres export SHITHUB_DATABASE_URL=postgres://shithub:****@127.0.0.1:5432/shithub?sslmode=require # JSON logs for prod export SHITHUB_LOG__FORMAT=json export SHITHUB_LOG__LEVEL=info # Tracing export SHITHUB_TRACING__ENABLED=true export SHITHUB_TRACING__ENDPOINT=http://otel-collector:4318 export SHITHUB_TRACING__SAMPLE_RATE=0.05 # Session signing key export SHITHUB_SESSION_KEY=$(openssl rand -base64 32) # Metrics behind Basic auth export SHITHUB_METRICS__BASIC_AUTH_USER=prom export SHITHUB_METRICS__BASIC_AUTH_PASS= ``` ## Secrets handling - Never commit secrets. `.env` is gitignored; production secrets live in a systemd `EnvironmentFile=` with mode `0600`. - The Ansible roles read sensitive values from the inventory (which you keep encrypted with `ansible-vault` or your secret store) and template them into `/etc/shithub/web.env` and `worker.env`. - Log-line redaction is independent of `config print` redaction; both layers exist on purpose. ## Adding a key If you fork shithub and add a config key: 1. Add the field to the appropriate struct in `internal/infra/config/config.go` with a `toml:` tag. 2. Set its default in `Defaults()`. 3. Add validation in `Validate()` if it has invariants. 4. If it carries a secret, name it so the redactor matches. 5. Document it here.