Multi-File Configs with $ref
One file stops being readable somewhere around the third table. $ref splits a YAML or JSON config across as many files as you like, with no change to the resulting object.
Any object whose only key is $ref, and whose value is a relative path, is replaced by the parsed contents of that file.
# app.yaml
name: crm-workspace
version: 2.0.0
auth:
$ref: ./config/auth.yaml
theme:
$ref: ./config/theme.yaml
tables:
- $ref: ./config/tables/companies.yaml
- $ref: ./config/tables/contacts.yaml
- $ref: ./config/tables/deals.yaml
pages:
- $ref: ./config/pages/sign-in.yaml
- $ref: ./config/pages/companies.yaml
agents:
- $ref: ./config/agents/records-assistant.yaml
# config/tables/companies.yaml
id: 1
name: Companies
fields:
- { id: 1, name: name, type: single-line-text, required: true }
- { id: 2, name: website, type: url }
Rules
| Rule | Behavior |
|---|---|
| Path resolution | $ref paths resolve relative to the file that contains them, not the working directory. |
| Mixed formats | A YAML root file may $ref a JSON partial and vice versa — each file is parsed by its extension. |
| Array elements | A $ref can stand in for a whole array element (- $ref: ...) or a whole object value. |
| Resolution timing | All $refs are resolved into one object before schema validation, so cross-section checks see the full app. |
Resolving before validation is what makes splitting safe: a rule such as "a record automation must reference an existing table" is still checked across the whole app, even when the automation and the table live in different files.
The config/ convention
sovrium init scaffolds — and the bundled templates follow — one shape: one file per collection entity, one file per singleton, scalars stay inline.
app.yaml # name, version, and the $ref map
config/
auth.yaml # singleton
theme.yaml # singleton
tables/
companies.yaml # one entity per file
contacts.yaml
pages/
sign-in.yaml
Nothing enforces this layout — $ref accepts any relative path. It earns its keep by making the root file a table of contents: what the app contains is legible without opening anything else.
When to split
Keep a config in one file while it is small. Split once a section grows big enough to deserve its own name — in practice, past the first couple of tables or pages, or as soon as a theme gets substantial.
Validation attributes errors back to the file they came from, so a mistake in a partial is reported against that partial rather than against the root:
Validation failed:
companies.yaml: Unknown field type "web-site" in field "website"
$ref is YAML and JSON only. TypeScript configs compose with ordinary imports instead — see TypeScript Configs.
Related Pages
- Config Files: YAML & JSON — formats and resolution order.
- Validating a Config — checking a split config in one command.
- Templates & Examples — real configs using this layout.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.