System Sources
Data components normally bind to a table. Some do not: a runs grid, an audit log, or a global-search result list reads from a platform endpoint that is not one of your tables.
You can bind such a component inline with dataSource: { system: { endpoint: … } }. The moment two components read the same endpoint, that raw path is duplicated in your config. app.systemSources declares each endpoint once, under a name:
systemSources:
- name: runs
endpoint: /api/admin/automations/runs
- name: failed-runs
endpoint: /api/admin/automations/runs
query:
status: failedComponents then reference the name:
- type: data-table
dataSource:
systemSource: runsEntry Properties
| Property | Description |
|---|---|
name |
Reference name used by { systemSource: <name> }. Lowercase kebab-case, unique within the catalog. |
endpoint |
The read endpoint to fetch rows from (e.g. /api/admin/automations/runs). |
rowsKey |
Key of the rows array in the response envelope. Defaults to items. |
idKey |
Key of each row's unique id. Defaults to id. |
totalKey |
Key of the total count in the envelope. Falls back to the number of rows returned when absent. |
query |
Static query parameters merged into every request to the endpoint. |
A catalog entry is a drop-in for the inline system form — a referencing component reads exactly the same fields it would have read inline, so switching between the two never changes the component.
Two Sources, One Endpoint
query is what makes named sources worth declaring. The same endpoint with different static parameters becomes two distinct, self-describing sources:
systemSources:
- name: open-tickets
endpoint: /api/admin/tickets
query:
status: open
- name: closed-tickets
endpoint: /api/admin/tickets
query:
status: closedEach is then one word at the binding site, and the filter lives in one place rather than being restated in every component.
Which Components Accept One
Any data-bound component: data-table, list, gallery, kanban, calendar, chart, kpi and data-timeline. See Data Components for what each renders.
Cursor Feeds
Some platform endpoints paginate by cursor rather than by page number: each response carries a token for the rows after the ones it returned, and reports no total. Automation runs, the audit log and agent conversations all read this way — a feed still being written to has no stable count to report.
A grid bound to such a source gets a different surface from the numbered pager:
| Page-numbered source | Cursor feed |
|---|---|
| Numbered pager, previous and next | A single Load more control |
| Each page replaces the one before | Each page is appended below the rows already on screen |
An x of N total |
No total, and no x of N |
This holds even when the component declares pagination. A pageSize still sets how many rows each request asks for, but the pager it would otherwise draw is not rendered and no total is shown. That is not a gap to work around: a cursor endpoint never reports how many rows exist, so any x of N on screen would be a number the server never sent — and an operator reads a displayed total as a count.
Appending rather than replacing follows from the same shape. A cursor only moves forward, so swapping the first page out for the second would put rows the reader has already seen out of reach for the rest of the session.
Changing the search term, the sort or a filter starts a different sequence, so the accumulated rows and the token are both dropped and the feed restarts from the top. Load more carries the active search and sort into the next request, so a continuation never silently widens back to the unfiltered feed.
Page Size
query can set the page size for the endpoint itself:
systemSources:
- name: recent-runs
endpoint: /api/admin/automations/runs
query:
limit: '5'A limit declared this way wins over the component's own pagination.pageSize. Naming it on the source is the author saying what a page of this endpoint is, which is the more specific statement — and it applies to every component bound to the source, so two grids reading the same feed cannot disagree about how much they ask for.
Leave limit out and the component's pageSize is used instead.
Validation
- The catalog must declare at least one source when present.
- Every
namemust be unique — a duplicate would make a reference ambiguous. - Every
{ systemSource: <name> }reference must point at a declared entry.
All three are checked when the config is decoded, so sovrium validate catches a typo'd reference offline, before the app boots.
System sources are read-only. They fetch rows for display. Writes go through the records API or an automation — see CRUD & Upsert.
Related Pages
- Data Binding — the
dataSourcemodule, table binding, and the inlinesystemform. - Data Components — the components that consume a source.
- Pages Overview — where a bound component sits in the page tree.
- API Reference — the endpoints a system source can read.
- Admin Dashboard — the operator console built from these bindings.
Last updated September 1, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.