Files
silo/docs/CONFIGURATION.md
Forbes de8370481f chore: fix stale docs, add read_only to config example
- Update COMPONENT_AUDIT.md: replace htmx reference with React SPA
- Add server.read_only to config.example.yaml and CONFIGURATION.md
2026-02-08 16:07:05 -06:00

365 lines
12 KiB
Markdown

# Configuration Reference
**Last Updated:** 2026-02-06
---
## Overview
Silo is configured via a YAML file. Copy the example and edit for your environment:
```bash
cp config.example.yaml config.yaml
```
The server reads the config file at startup:
```bash
./silod -config config.yaml # default: config.yaml
go run ./cmd/silod -config config.yaml
```
YAML values support environment variable expansion using `${VAR_NAME}` syntax. Environment variable overrides (listed per-key below) take precedence over YAML values.
---
## Server
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `server.host` | string | `"0.0.0.0"` | Bind address |
| `server.port` | int | `8080` | HTTP port |
| `server.base_url` | string | — | External URL (e.g. `https://silo.example.com`). Used for OIDC callback URLs and session cookie domain. Required when OIDC is enabled. |
| `server.read_only` | bool | `false` | Start in read-only mode. All write endpoints return 503. Can be toggled at runtime with `SIGUSR1`. |
```yaml
server:
host: "0.0.0.0"
port: 8080
base_url: "https://silo.example.com"
read_only: false
```
---
## Database
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `database.host` | string | — | `SILO_DB_HOST` | PostgreSQL host |
| `database.port` | int | `5432` | — | PostgreSQL port |
| `database.name` | string | — | `SILO_DB_NAME` | Database name |
| `database.user` | string | — | `SILO_DB_USER` | Database user |
| `database.password` | string | — | `SILO_DB_PASSWORD` | Database password |
| `database.sslmode` | string | `"require"` | — | SSL mode: `disable`, `require`, `verify-ca`, `verify-full` |
| `database.max_connections` | int | `10` | — | Connection pool size |
**SSL mode guidance:**
- `disable` — development only, no encryption
- `require` — encrypted but no certificate verification (default)
- `verify-ca` — verify server certificate is signed by trusted CA
- `verify-full` — verify CA and hostname match (recommended for production)
```yaml
database:
host: "localhost"
port: 5432
name: "silo"
user: "silo"
password: "" # use SILO_DB_PASSWORD env var
sslmode: "require"
max_connections: 10
```
---
## Storage (MinIO/S3)
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `storage.endpoint` | string | — | `SILO_MINIO_ENDPOINT` | MinIO/S3 endpoint (`host:port`) |
| `storage.access_key` | string | — | `SILO_MINIO_ACCESS_KEY` | Access key |
| `storage.secret_key` | string | — | `SILO_MINIO_SECRET_KEY` | Secret key |
| `storage.bucket` | string | — | — | S3 bucket name (created automatically if missing) |
| `storage.use_ssl` | bool | `false` | — | Use HTTPS for MinIO connections |
| `storage.region` | string | `"us-east-1"` | — | S3 region |
```yaml
storage:
endpoint: "localhost:9000"
access_key: "" # use SILO_MINIO_ACCESS_KEY env var
secret_key: "" # use SILO_MINIO_SECRET_KEY env var
bucket: "silo-files"
use_ssl: false
region: "us-east-1"
```
---
## Schemas
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `schemas.directory` | string | `"/etc/silo/schemas"` | Path to directory containing YAML schema files |
| `schemas.default` | string | — | Default schema name for part number generation |
Schema files define part numbering formats, category codes, and property definitions. See `schemas/kindred-rd.yaml` for an example.
```yaml
schemas:
directory: "./schemas"
default: "kindred-rd"
```
---
## FreeCAD
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `freecad.uri_scheme` | string | `"silo"` | URI scheme for "Open in FreeCAD" links in the web UI |
| `freecad.executable` | string | — | Path to FreeCAD binary (for CLI operations) |
```yaml
freecad:
uri_scheme: "silo"
executable: "/usr/bin/freecad"
```
---
## Odoo ERP Integration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `odoo.enabled` | bool | `false` | Enable Odoo integration |
| `odoo.url` | string | — | Odoo server URL |
| `odoo.database` | string | — | Odoo database name |
| `odoo.username` | string | — | Odoo username |
| `odoo.api_key` | string | — | Odoo API key |
The Odoo integration currently supports configuration and sync-log CRUD. Push/pull sync operations are stubs.
```yaml
odoo:
enabled: false
url: "https://odoo.example.com"
database: "odoo"
username: "silo-service"
api_key: ""
```
---
## Authentication
Authentication has a master toggle and three independent backends. When `auth.enabled` is `false`, all routes are accessible without login and a synthetic admin user (`dev`) is injected into every request.
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `auth.enabled` | bool | `false` | — | Master toggle. Set `true` for production. |
| `auth.session_secret` | string | — | `SILO_SESSION_SECRET` | Secret for signing session cookies. Required when auth is enabled. |
### Local Auth
Built-in username/password accounts stored in the Silo database with bcrypt-hashed passwords.
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `auth.local.enabled` | bool | — | — | Enable local accounts |
| `auth.local.default_admin_username` | string | — | `SILO_ADMIN_USERNAME` | Default admin account created on first startup |
| `auth.local.default_admin_password` | string | — | `SILO_ADMIN_PASSWORD` | Password for default admin (bcrypt-hashed on creation) |
The default admin account is only created if both username and password are set and the user does not already exist. This is idempotent.
### LDAP / FreeIPA
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `auth.ldap.enabled` | bool | `false` | — | Enable LDAP authentication |
| `auth.ldap.url` | string | — | — | LDAP server URL (e.g. `ldaps://ipa.example.com`) |
| `auth.ldap.base_dn` | string | — | — | Base DN for the LDAP tree |
| `auth.ldap.user_search_dn` | string | — | — | DN under which to search for users |
| `auth.ldap.bind_dn` | string | — | — | Service account DN for user lookups (optional; omit for direct user bind) |
| `auth.ldap.bind_password` | string | — | `SILO_LDAP_BIND_PASSWORD` | Service account password |
| `auth.ldap.user_attr` | string | `"uid"` | — | LDAP attribute for username |
| `auth.ldap.email_attr` | string | `"mail"` | — | LDAP attribute for email |
| `auth.ldap.display_attr` | string | `"displayName"` | — | LDAP attribute for display name |
| `auth.ldap.group_attr` | string | `"memberOf"` | — | LDAP attribute for group membership |
| `auth.ldap.role_mapping` | map | — | — | Maps LDAP group DNs to Silo roles (see example below) |
| `auth.ldap.tls_skip_verify` | bool | `false` | — | Skip TLS certificate verification (testing only) |
**Role mapping** maps LDAP group DNs to Silo roles. Groups are checked in priority order: admin, then editor, then viewer. The first match wins.
```yaml
auth:
ldap:
enabled: true
url: "ldaps://ipa.example.com"
base_dn: "dc=example,dc=com"
user_search_dn: "cn=users,cn=accounts,dc=example,dc=com"
role_mapping:
admin:
- "cn=silo-admins,cn=groups,cn=accounts,dc=example,dc=com"
editor:
- "cn=silo-users,cn=groups,cn=accounts,dc=example,dc=com"
- "cn=engineers,cn=groups,cn=accounts,dc=example,dc=com"
viewer:
- "cn=silo-viewers,cn=groups,cn=accounts,dc=example,dc=com"
```
### OIDC / Keycloak
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `auth.oidc.enabled` | bool | `false` | — | Enable OIDC authentication |
| `auth.oidc.issuer_url` | string | — | — | OIDC provider issuer URL (e.g. Keycloak realm URL) |
| `auth.oidc.client_id` | string | — | — | OAuth2 client ID |
| `auth.oidc.client_secret` | string | — | `SILO_OIDC_CLIENT_SECRET` | OAuth2 client secret |
| `auth.oidc.redirect_url` | string | — | — | OAuth2 callback URL (typically `{base_url}/auth/callback`) |
| `auth.oidc.scopes` | []string | `["openid", "profile", "email"]` | — | OAuth2 scopes to request |
| `auth.oidc.admin_role` | string | — | — | Keycloak realm role that grants admin access |
| `auth.oidc.editor_role` | string | — | — | Keycloak realm role that grants editor access |
| `auth.oidc.default_role` | string | `"viewer"` | — | Fallback role when no role claim matches |
Roles are extracted from the Keycloak `realm_access.roles` claim. If the user has the `admin_role`, they get admin. Otherwise if they have `editor_role`, they get editor. Otherwise `default_role` applies.
```yaml
auth:
oidc:
enabled: true
issuer_url: "https://keycloak.example.com/realms/silo"
client_id: "silo"
client_secret: "" # use SILO_OIDC_CLIENT_SECRET env var
redirect_url: "https://silo.example.com/auth/callback"
admin_role: "silo-admin"
editor_role: "silo-editor"
default_role: "viewer"
```
### CORS
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `auth.cors.allowed_origins` | []string | — | Origins allowed for cross-origin requests from browser-based clients |
FreeCAD and other non-browser clients use direct HTTP and are not affected by CORS. This setting is for browser-based tools running on different origins.
```yaml
auth:
cors:
allowed_origins:
- "https://silo.example.com"
```
---
## Environment Variables
All environment variable overrides. These take precedence over values in `config.yaml`.
| Variable | Config Key | Description |
|----------|-----------|-------------|
| `SILO_DB_HOST` | `database.host` | PostgreSQL host |
| `SILO_DB_NAME` | `database.name` | PostgreSQL database name |
| `SILO_DB_USER` | `database.user` | PostgreSQL user |
| `SILO_DB_PASSWORD` | `database.password` | PostgreSQL password |
| `SILO_MINIO_ENDPOINT` | `storage.endpoint` | MinIO endpoint |
| `SILO_MINIO_ACCESS_KEY` | `storage.access_key` | MinIO access key |
| `SILO_MINIO_SECRET_KEY` | `storage.secret_key` | MinIO secret key |
| `SILO_SESSION_SECRET` | `auth.session_secret` | Session cookie signing secret |
| `SILO_ADMIN_USERNAME` | `auth.local.default_admin_username` | Default admin username |
| `SILO_ADMIN_PASSWORD` | `auth.local.default_admin_password` | Default admin password |
| `SILO_LDAP_BIND_PASSWORD` | `auth.ldap.bind_password` | LDAP service account password |
| `SILO_OIDC_CLIENT_SECRET` | `auth.oidc.client_secret` | OIDC client secret |
Additionally, YAML values can reference environment variables directly using `${VAR_NAME}` syntax, which is expanded at load time via `os.ExpandEnv()`.
---
## Deployment Examples
### Development (no auth)
```yaml
server:
host: "0.0.0.0"
port: 8080
base_url: "http://localhost:8080"
database:
host: "localhost"
port: 5432
name: "silo"
user: "silo"
password: "silodev"
sslmode: "disable"
storage:
endpoint: "localhost:9000"
access_key: "minioadmin"
secret_key: "minioadmin"
bucket: "silo-files"
use_ssl: false
schemas:
directory: "./schemas"
default: "kindred-rd"
auth:
enabled: false
```
### Local Auth Only
```yaml
auth:
enabled: true
session_secret: "change-me-to-a-random-string"
local:
enabled: true
default_admin_username: "admin"
default_admin_password: "change-me"
```
### LDAP / FreeIPA
```yaml
auth:
enabled: true
session_secret: "${SILO_SESSION_SECRET}"
local:
enabled: false
ldap:
enabled: true
url: "ldaps://ipa.example.com"
base_dn: "dc=example,dc=com"
user_search_dn: "cn=users,cn=accounts,dc=example,dc=com"
role_mapping:
admin:
- "cn=silo-admins,cn=groups,cn=accounts,dc=example,dc=com"
editor:
- "cn=engineers,cn=groups,cn=accounts,dc=example,dc=com"
viewer:
- "cn=silo-viewers,cn=groups,cn=accounts,dc=example,dc=com"
```
### OIDC / Keycloak
```yaml
auth:
enabled: true
session_secret: "${SILO_SESSION_SECRET}"
local:
enabled: false
oidc:
enabled: true
issuer_url: "https://keycloak.example.com/realms/silo"
client_id: "silo"
client_secret: "${SILO_OIDC_CLIENT_SECRET}"
redirect_url: "https://silo.example.com/auth/callback"
admin_role: "silo-admin"
editor_role: "silo-editor"
default_role: "viewer"
```