Configuration reference
shithubd reads configuration from four sources, in increasing precedence:
- Built-in defaults (
internal/infra/config/Defaults()). - TOML file at
$SHITHUB_CONFIG(default/etc/shithub/config.toml). Absent is fine; bad syntax is fatal. - Environment variables.
SHITHUB_<area>__<key>— note double underscore between nesting levels. - 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
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 <noreply@shithub.local> |
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
# 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=<long-random>
Secrets handling
- Never commit secrets.
.envis gitignored; production secrets live in a systemdEnvironmentFile=with mode0600. - The Ansible roles read sensitive values from the inventory
(which you keep encrypted with
ansible-vaultor your secret store) and template them into/etc/shithub/web.envandworker.env. - Log-line redaction is independent of
config printredaction; both layers exist on purpose.
Adding a key
If you fork shithub and add a config key:
- Add the field to the appropriate struct in
internal/infra/config/config.gowith atoml:tag. - Set its default in
Defaults(). - Add validation in
Validate()if it has invariants. - If it carries a secret, name it so the redactor matches.
- Document it here.
View source
| 1 | # Configuration reference |
| 2 | |
| 3 | shithubd reads configuration from four sources, in increasing |
| 4 | precedence: |
| 5 | |
| 6 | 1. **Built-in defaults** (`internal/infra/config/Defaults()`). |
| 7 | 2. **TOML file** at `$SHITHUB_CONFIG` (default |
| 8 | `/etc/shithub/config.toml`). Absent is fine; bad syntax is |
| 9 | fatal. |
| 10 | 3. **Environment variables.** `SHITHUB_<area>__<key>` — note |
| 11 | double underscore between nesting levels. |
| 12 | 4. **CLI flags** passed by the caller. |
| 13 | |
| 14 | After merging, a small alias table maps backward-compat names |
| 15 | (e.g., `SHITHUB_DATABASE_URL` → `db.url`); then the resolved |
| 16 | config is validated. Any failure exits non-zero with a one-line |
| 17 | error pointing at the offending key. |
| 18 | |
| 19 | ## Inspecting the active configuration |
| 20 | |
| 21 | ```sh |
| 22 | shithubd config print # writes resolved config as TOML, secrets redacted |
| 23 | shithubd config validate # exits non-zero if invalid |
| 24 | shithubd version # one-line summary of which sinks are configured |
| 25 | ``` |
| 26 | |
| 27 | `config print` redacts any field whose name contains `password`, |
| 28 | `pass`, `secret`, `key`, `token`, `dsn`, or `url` — URL fields |
| 29 | are redacted because they often carry credentials in the |
| 30 | userinfo component. |
| 31 | |
| 32 | ## Reference |
| 33 | |
| 34 | | Key | Type | Default | Notes | |
| 35 | |---|---|---|---| |
| 36 | | `env` | string | `dev` | One of `dev`, `staging`, `prod`. | |
| 37 | | `web.addr` | string | `:8080` | Listen address. | |
| 38 | | `web.read_timeout` | duration | `30s` | Per-request read timeout. | |
| 39 | | `web.write_timeout` | duration | `30s` | Per-request write timeout. | |
| 40 | | `web.shutdown_timeout` | duration | `10s` | Graceful drain on SIGTERM. | |
| 41 | | `db.url` | string | `""` | Postgres DSN. Aliased by `SHITHUB_DATABASE_URL`. | |
| 42 | | `db.max_conns` | int | `10` | pgxpool max conns. | |
| 43 | | `db.min_conns` | int | `0` | pgxpool min conns. | |
| 44 | | `db.connect_timeout` | duration | `5s` | | |
| 45 | | `log.level` | string | `info` | One of `debug`, `info`, `warn`, `error`. | |
| 46 | | `log.format` | string | `text` | One of `text`, `json`. Use `json` in prod. | |
| 47 | | `metrics.enabled` | bool | `true` | Mounts `/metrics`. | |
| 48 | | `metrics.basic_auth_user` | string | `""` | Together with `pass`, gates `/metrics`. | |
| 49 | | `metrics.basic_auth_pass` | string | `""` | Redacted on print. | |
| 50 | | `tracing.enabled` | bool | `false` | When true, `tracing.endpoint` is required. | |
| 51 | | `tracing.endpoint` | string | `""` | OTLP HTTP endpoint. | |
| 52 | | `tracing.sample_rate` | float | `0.05` | Parent-based ratio sampler in [0, 1]. | |
| 53 | | `tracing.service_name` | string | `shithubd` | OTel resource attribute. | |
| 54 | | `error_reporting.dsn` | string | `""` | Sentry-protocol DSN. Empty disables. | |
| 55 | | `session.key_b64` | string | `""` | Base64 32-byte AEAD key. **Required in prod.** | |
| 56 | | `session.max_age` | duration | `720h` | Cookie session lifetime (30 days). | |
| 57 | | `session.secure` | bool | `false` | Set `Secure` cookie attribute. **True in prod.** | |
| 58 | | `storage.repos_root` | string | `/data/repos` | Filesystem root for bare repos. | |
| 59 | | `storage.s3.endpoint` | string | `""` | S3-compatible endpoint host[:port]. Empty disables S3. | |
| 60 | | `storage.s3.region` | string | `us-east-1` | Region for SigV4 signing. | |
| 61 | | `storage.s3.access_key_id` | string | `""` | | |
| 62 | | `storage.s3.secret_access_key` | string | `""` | Redacted. | |
| 63 | | `storage.s3.bucket` | string | `""` | One bucket per environment. | |
| 64 | | `storage.s3.use_ssl` | bool | `false` | True for Spaces, false for MinIO. | |
| 65 | | `storage.s3.force_path_style` | bool | `true` | True for MinIO, false for Spaces. | |
| 66 | | `auth.require_email_verification` | bool | `true` | Login rejected until primary email verified. | |
| 67 | | `auth.base_url` | string | `http://127.0.0.1:8080` | Used in transactional email links. **Set to your public URL in prod.** | |
| 68 | | `auth.site_name` | string | `shithub` | Branding token in email subjects. | |
| 69 | | `auth.email_from` | string | `shithub <noreply@shithub.local>` | Envelope From. | |
| 70 | | `auth.email_backend` | string | `stdout` | One of `stdout`, `smtp`, `postmark`. | |
| 71 | | `auth.smtp.addr` | string | `127.0.0.1:1025` | Required when backend=`smtp`. | |
| 72 | | `auth.smtp.username` | string | `""` | Optional. | |
| 73 | | `auth.smtp.password` | string | `""` | Optional. Redacted. | |
| 74 | | `auth.postmark.server_token` | string | `""` | Required when backend=`postmark`. Redacted. | |
| 75 | | `auth.argon2.memory_kib` | uint32 | `65536` | argon2id memory cost (KiB). | |
| 76 | | `auth.argon2.time` | uint32 | `3` | argon2id iterations. | |
| 77 | | `auth.argon2.threads` | uint8 | `2` | argon2id parallelism. | |
| 78 | | `auth.totp_key_b64` | string | `""` | Base64 32-byte AEAD for TOTP secrets. **Required if 2FA is in use.** | |
| 79 | |
| 80 | ## Examples |
| 81 | |
| 82 | ```sh |
| 83 | # Listen elsewhere |
| 84 | export SHITHUB_WEB__ADDR=:9090 |
| 85 | |
| 86 | # Postgres |
| 87 | export SHITHUB_DATABASE_URL=postgres://shithub:****@127.0.0.1:5432/shithub?sslmode=require |
| 88 | |
| 89 | # JSON logs for prod |
| 90 | export SHITHUB_LOG__FORMAT=json |
| 91 | export SHITHUB_LOG__LEVEL=info |
| 92 | |
| 93 | # Tracing |
| 94 | export SHITHUB_TRACING__ENABLED=true |
| 95 | export SHITHUB_TRACING__ENDPOINT=http://otel-collector:4318 |
| 96 | export SHITHUB_TRACING__SAMPLE_RATE=0.05 |
| 97 | |
| 98 | # Session signing key |
| 99 | export SHITHUB_SESSION_KEY=$(openssl rand -base64 32) |
| 100 | |
| 101 | # Metrics behind Basic auth |
| 102 | export SHITHUB_METRICS__BASIC_AUTH_USER=prom |
| 103 | export SHITHUB_METRICS__BASIC_AUTH_PASS=<long-random> |
| 104 | ``` |
| 105 | |
| 106 | ## Secrets handling |
| 107 | |
| 108 | - Never commit secrets. `.env` is gitignored; production secrets |
| 109 | live in a systemd `EnvironmentFile=` with mode `0600`. |
| 110 | - The Ansible roles read sensitive values from the inventory |
| 111 | (which you keep encrypted with `ansible-vault` or your secret |
| 112 | store) and template them into `/etc/shithub/web.env` and |
| 113 | `worker.env`. |
| 114 | - Log-line redaction is independent of `config print` redaction; |
| 115 | both layers exist on purpose. |
| 116 | |
| 117 | ## Adding a key |
| 118 | |
| 119 | If you fork shithub and add a config key: |
| 120 | |
| 121 | 1. Add the field to the appropriate struct in |
| 122 | `internal/infra/config/config.go` with a `toml:` tag. |
| 123 | 2. Set its default in `Defaults()`. |
| 124 | 3. Add validation in `Validate()` if it has invariants. |
| 125 | 4. If it carries a secret, name it so the redactor matches. |
| 126 | 5. Document it here. |