# Configuration Reference **Last Updated:** 2026-03-01 --- ## 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 (Filesystem) Files are stored on the local filesystem under a configurable root directory. | Key | Type | Default | Description | |-----|------|---------|-------------| | `storage.backend` | string | `"filesystem"` | Storage backend (`filesystem`) | | `storage.filesystem.root_dir` | string | — | Root directory for file storage (required) | ```yaml storage: backend: "filesystem" filesystem: root_dir: "/opt/silo/data" ``` Ensure the directory exists and is writable by the `silo` user: ```bash sudo mkdir -p /opt/silo/data sudo chown silo:silo /opt/silo/data ``` --- ## 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: "" ``` --- ## Approval Workflows | Key | Type | Default | Description | |-----|------|---------|-------------| | `workflows.directory` | string | `"/etc/silo/workflows"` | Path to directory containing YAML workflow definition files | Workflow definition files describe multi-stage approval processes using a state machine pattern. Each file defines a workflow with states, transitions, and approver requirements. ```yaml workflows: directory: "/etc/silo/workflows" ``` --- ## Solver | Key | Type | Default | Env Override | Description | |-----|------|---------|-------------|-------------| | `solver.default_solver` | string | `""` | `SILO_SOLVER_DEFAULT` | Default solver backend name | | `solver.max_context_size_mb` | int | `10` | — | Maximum SolveContext payload size in MB | | `solver.default_timeout` | int | `300` | — | Default solver job timeout in seconds | | `solver.auto_diagnose_on_commit` | bool | `false` | — | Auto-submit diagnose job on assembly revision commit | The solver module depends on the `jobs` module being enabled. See [SOLVER.md](SOLVER.md) for the full solver service specification. ```yaml solver: default_solver: "ondsel" max_context_size_mb: 10 default_timeout: 300 auto_diagnose_on_commit: true ``` --- ## Modules Optional module toggles. Each module can be explicitly enabled or disabled. If not listed, the module's built-in default applies. See [MODULES.md](MODULES.md) for the full module system specification. ```yaml modules: projects: enabled: true audit: enabled: true odoo: enabled: false freecad: enabled: true jobs: enabled: false dag: enabled: false solver: enabled: false sessions: enabled: true ``` The `auth.enabled` field controls the `auth` module directly (not duplicated under `modules:`). The `sessions` module depends on `auth` and is enabled by default. --- ## 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_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 | | `SILO_SOLVER_DEFAULT` | `solver.default_solver` | Default solver backend name | 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: backend: "filesystem" filesystem: root_dir: "./data" 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" ```