
# Changelog

Highlights from each Sovrium release, newest first. For the full commit-level
history — every fix and internal change — see
[GitHub Releases](https://github.com/sovrium/sovrium/releases), the canonical
source. Sovrium follows [semantic versioning](https://semver.org); while it is
pre-1.0, minor versions can still introduce changes.

**Latest: v0.22.1 — 11 August 2026.** · [Subscribe via RSS](/feed.xml)

Every release below is listed in the **On this page** rail — jump straight to any
version from there.

## v0.22.1 — 11 August 2026

`sovrium build` is about twenty-five seconds faster. The build crawls every route
your app registers, and it was crawling the development live-reload stream along
with them. That stream is built to stay open until its twenty-five second
lifetime runs out, so every build sat waiting on it before writing anything. The
development-only route is now left out of the crawl, and a minimal build finishes
in about a second instead of about twenty-six.

## v0.22.0 — 11 August 2026

**A config property Sovrium does not recognise now fails validation instead of
being ignored.** This is the breaking part, and it finishes what v0.21.0 started.
A misspelled property used to be accepted in silence, so a typo in a field name
or a permissions block looked like it had been applied and did nothing at all.
Sovrium now refuses the config and names the property, the path it sits at, and
the file it came from. A config that validates and then configures nothing no
longer starts either. `validate`, `start` and `build` read your file through the
same code, so the three of them can no longer disagree about what it means.

**Aliases and inert options are removed.** Gone are the legacy config-shorthand
rewriting pass, the short `--template` CLI aliases, the bare `S3_*` environment
variable names, the `off` / `on` / `auto` spellings of `ECO_MODE`, the
`meta.priority` and `meta.changefreq` sitemap fields, the `currentDateTime`
automation helper, the boolean comment-moderation shape, the batch-delete route
alias and its permanent query flag, and the `/_admin/connect-ai` alias. Run
`sovrium validate` before upgrading: together with the change above, it now names
anything in your config that no longer exists.

**Sovrium provisions its own encryption key on first start.** It used to refuse
to boot without one, which turned the very first run into a configuration
exercise before it could be anything else. A new instance now generates a key,
stores it, and derives the authentication secret from it, so a fresh install
starts with no environment variables set at all. If you already have a key,
`sovrium secret adopt` persists it into the new arrangement. A server still
refuses to start with no key rather than running unprotected, while CLI commands
that never touch encrypted data run without one. Connections written under a
different key now report that plainly instead of appearing to have lost their
tokens, and an unreadable signing key is regenerated at boot rather than failing
every request that needs it.

**The compiled binary no longer embeds local runtime data.** A build swept in
whatever was sitting in the local runtime directory, which on a working machine
can mean a database file and your environment. If you build and distribute your
own binaries, rebuild them on this version.

**Permissions hold on the paths that were skipping them.** A table operation your
permissions block does not mention is now denied instead of allowed. A view's
read permission is honoured at every level rather than only the outermost one,
and an AI agent is refused a knowledge table it is not allowed to read. Admin API
routes are gated even when an app configures no authentication, the
active-assignment cookie is checked against what the signed-in user may actually
see, and role changes can no longer lock the last admin out of an instance or let
one admin impersonate another. Where your app relied on one of those gaps,
requests that used to succeed will now be refused.

**The data grid became an editor.** Every field type can be edited in place,
including the eight that had no cell of their own and fell back to a raw value. A
grid can group by up to three fields at once and summarise each group, a row opens
into its full record in one line of config, and a select field can draw its
choices from another table's rows. Kanban, calendar and gallery are reachable
from the view switcher. The colour a select option declares is now painted
wherever that option appears, including on kanban, calendar and timeline, and a
grid can tint whole rows by a select field. A frozen column finally stays put
when the grid scrolls sideways.

**Two migration faults that moved or dropped data are fixed.** Adding a table to
the middle of your table list could move existing rows into a different table,
and adding a field mid-list could drop a column you had declared, because both
identities were taken from position in the file rather than from the name. SQLite
foreign keys now also survive a table rebuild. Upgrading applies the corrected
behaviour, but it does not undo a move an earlier version already made, so check
your data if you added a table or a field mid-list on a previous version.

**SQLite and PostgreSQL agree in more places.** Formula fields compute on SQLite,
a declared view works on the default engine, and the `ARRAYUNIQUE` rollup runs
there. Array and attachment fields read back as arrays on SQLite rather than as
JSON strings, list-valued fields store correctly when a PostgreSQL record is
updated, and a stored duration is read as the seconds its schema declares.

**The records API answers with the status code it means.** A batch write reports
a unique collision as 409 and an empty required value as 400, matching what a
single write already did and using the same wording. Batch creates return a
usable record id on tables with computed fields, store array values correctly,
and no longer discard flat records without saying so. Search text is matched as
text rather than as a pattern.

**A new `sovrium seed` command loads relational sample data.** A fresh app can be
filled with linked records to develop against, and when the database rejects a
seeded row the failure names the field and the value that caused it.

Also in this release: pages serve the language the URL asks for; the remaining
engine-supplied control labels are translated; an AI field shows when it still
holds a locally computed fallback, and a hand-written AI value is no longer
reported as a failed computation; AI conversations keep their turns in the order
they happened, and agents no longer appear as people in the users console; chart
series take their colours from the app theme; and the rich-text and code editors
load only on the pages that use them.

## v0.21.0 — 1 August 2026

**Eight page-component types and twelve config properties have been removed —
this is the breaking part, and it takes nothing away.** All twenty validated
happily and then did nothing: the components never rendered, and the properties
configured no behaviour at any point. If your config names one of them it was
already inert, and the only change you will notice is that Sovrium now tells you
so at validation time instead of accepting it in silence. An option you cannot
observe is a promise the binary does not keep, and we would rather break the file
than keep the promise.

**A table's button field is now a real button.** It renders as a control on the
record, runs the automation it names, and can be gated per record so it only
appears where it applies. Its action vocabulary is closed — a typo is rejected
rather than ignored — the listing refreshes and reports the outcome once the run
finishes, and the audit log records who pressed it. Automation runs now also carry
the user who triggered them, whatever the trigger was.

**Code blocks and tabs got the chrome they were missing.** Every code block draws
a header with a copy button, and can name the file it belongs in or present itself
as a terminal session. Tab triggers now show which one is active — the active
state was keyed on an attribute the underlying primitive never emitted, so until
now no tab anywhere looked selected — and a trigger can carry a subtitle under its
caption. Tab and accordion captions are translated like the rest of your copy.
There is also a new marquee component, for a band of logos or notices that scrolls
on its own.

**`theme.darkColors` reaches the stylesheet.** The schema has accepted it for some
time and then discarded it; a dark palette declared there now compiles and
applies.

**Permissions and validation are enforced on the paths that were skipping them.**
Field-level write permissions and multi-select limits now hold on bulk updates as
well as single ones. Rich text is sanitised on every write path, not only on
create. Email and URL columns are validated on update. Delete and field-read
permissions are enforced on every query path rather than on some of them. Rate
limits can no longer be bypassed by forging a forwarding header, and a failed
batch write reports what was wrong with the request instead of leaking database
internals. If your data relied on any of those gaps, writes that used to succeed
will now be refused — which is the point.

**Erasing an account now erases all of it.** Form submissions are covered by both
export and erasure, three lingering traces are gone, and a user assigned on
someone else's record can finally be erased at all: a foreign key made that
impossible before.

**SQLite and PostgreSQL agree in more places.** Dynamic tables store timestamps as
`timestamptz`, SQLite soft-delete timestamps are written in ISO-8601, and indexes
are removed reliably when a table name contains spaces.

## v0.20.0 — 27 July 2026

**Collection pages and page data only worked if you set `DATABASE_URL`.** The
data-source reader opened its own connection instead of using the one the app
runs on, and returned an empty result whenever that variable was unset — which
is the shipped zero-config default, where Sovrium runs on SQLite. Nothing was
thrown and nothing was logged, so the failure was silent: collection pages
answered 404 as though the record did not exist, page data sources rendered
empty, and row-level permission overlays granted nothing. Reads now go through
the app's own connection on either engine. `GREATEST` and `LEAST` formulas also
translate to SQLite, instead of working only on PostgreSQL.

**Attachment fields now honour the bucket they declare — this is the breaking
part.** A field declaring `bucket: documents` had its URLs bound to `default`
anyway, and the two documented attachment types were never enriched at all.
Fixing both changes the shape of the value you read back: `single-attachment`
returned a bare key string and `multiple-attachments` an array of them, and both
now return objects carrying `key` and a bucket-bound signed URL — in records API
responses, in exports, and over MCP. Rows that already stored metadata keep
their existing fields.

That binding also closes a real exposure. The signed URL was minted without ever
checking who is allowed to sign, so on a bucket gated `sign: ['admin']` any
reader of a record received an admin-equivalent, session-free download URL.
Buckets now enforce the upload, download and delete permissions they declare,
which config previously accepted and then ignored.

**Forms say what is actually wrong.** A request with several invalid fields
reports every one of them instead of stopping at the first. Single-character
values are accepted where the schema allows them, field errors surface on the
form itself, and a record saves when a constrained field is simply left
untouched. An upload rejected for being too large now says so, rather than
failing without a reason. Forms served at a path you chose enforce the same
access and availability rules as forms at their default path.

**Comments.** `autoApprove.previouslyApproved` is honoured, so a commenter you
have already approved is not queued for review again. A rejected comment answers
with a rejection instead of `201 Created`.

Also in this release: a redirect can set `localizeTarget: false` when its
destination should not follow the visitor's language prefix; `deleted-by` is
accepted by `sovrium validate`, so the soft-delete audit pair passes validation;
a field named `user_id` is no longer discarded by the records API; database
failures answer with the status code they mean; reported errors carry their full
cause chain to stderr and to your error backend; and the admin tables overview
runs one query per table instead of fanning out.

## v0.19.0 — 25 July 2026

**Redirects.** A config can now declare the URLs it used to serve. Add a
`redirects` list naming each retired path and where it went, and Sovrium answers
those requests with a real HTTP redirect instead of a 404 — so bookmarks and
inbound links survive a restructure, and search engines carry the old URL's
standing over to the new one. Path targets follow the visitor's language prefix,
so one rule covers every locale.

**Password recovery in the operator console.** Signing in to the admin space now
offers a reset flow, so losing an operator password no longer means editing the
database by hand. Reset emails point at a path the app actually serves, which
they previously did not.

**Automations tidy up after themselves.** File actions now reclaim their aged
temporary files rather than leaving them to accumulate. Run history also redacts
secrets from step output, so a token passed into a step no longer appears in the
record of that step.

**The default look changed — this is the breaking part.** If your app sets its
own `theme` colours, nothing moves. If it takes the defaults, it will look
different after upgrading.

- **Colour is reserved for consequence.** The default palette is achromatic, and
  `error` is the only ramp that keeps a hue. Success, warning and information
  states are carried by weight, position and wording instead. When colour appears
  in a Sovrium interface, something has gone wrong.
- **Self-hosted typography**: IBM Plex Sans and JetBrains Mono now ship with the
  binary as inlined variable fonts. No Google Fonts request is made — by your app
  or by any starter template. The retired serif companion is removed.
- **The decorative accent is gone from the engine.** An app that wants an accent
  declares its own colour key in `app.theme.colors`; the shipped default no longer
  imposes one.
- **Fixes surfaced along the way**: the command palette now follows your theme and
  is legible in dark mode; subtle text meets WCAG AA on every render path; and
  `interactions.click.animation: 'pulse'` / `'bounce'` play their intended click
  feedback instead of being overridden by a same-named framework animation.

Also fixed along the way: the command palette renders from theme tokens instead
of hardcoded colour, so it is legible in dark mode; and
`interactions.click.animation: 'pulse'` / `'bounce'` play their intended
animation instead of colliding with Tailwind's built-ins of the same name.

## v0.18.1 — 25 July 2026

Corrections to the observability release and to two surfaces that were reporting
the wrong thing.

- **Errors keep their status code.** A reported error now arrives with the HTTP
  status it actually returned, so a 404 is no longer indistinguishable from a 500
  in your issue tracker. Secrets are stripped from reports before they leave the
  instance.
- **Debug logs reach your collector.** With `LOG_LEVEL=debug`, debug lines are
  now included in the OTLP export instead of printing to stdout only.
- **Admin overview counts are correct.** Record, form-submission and storage
  totals on the operator overview were miscounted; they now match the data.
- **AI chat works on SQLite.** Chat record queries and mutations failed on the
  default SQLite dialect and now behave as they do on PostgreSQL.

## v0.18.0 — 24 July 2026

Sovrium can now report its own errors, logs and performance to an observability
platform you run. Everything in this release is **off by default** and enabled
per environment variable: no telemetry leaves an instance unless an operator
points it somewhere.

- **Error reporting**: uncaught errors can be sent to any Sentry-protocol
  endpoint — self-hosted GlitchTip, Sentry, or another compatible collector. The
  startup banner states plainly whether reporting is on and where it goes.
- **Structured log export**: application logs can be exported over OTLP-HTTP
  while continuing to print to stdout, so adding a collector never costs you the
  logs you already read.
- **Performance transactions**: HTTP requests can be sampled as timed
  transactions, at a rate you choose, to see where real time is spent.
- **Trace export**: an OTLP trace layer ships dormant, ready for distributed
  tracing without another upgrade.
- **Configuration**: every observability variable is documented in
  `.env.example`, so the full surface is visible before you turn any of it on.

## v0.17.0 — 21 July 2026

Documentation pages gained clearer chrome and real syntax highlighting, and
existing databases now survive schema evolution more reliably on both dialects.

- **Docs navigation**: documentation breadcrumbs now anchor to the docs tab
  root, and every docs page ends with a contribution footer for suggesting
  edits or reporting issues.
- **Syntax highlighting**: the code component renders with Shiki highlighting
  out of the box, and author-set attributes on code blocks are preserved
  through the highlighter.
- **Steadier live reload**: watch mode debounces config reloads so a file save
  is never read mid-write.
- **Schema evolution fixes**: an app now boots cleanly against existing
  PostgreSQL data when a CHECK constraint tightens, and against an existing
  SQLite database after schema changes.

## v0.16.0 — 20 July 2026

The self-hosted engine sharpened its focus, and published demo instances gained a
friendlier front door.

- The managed-Cloud feature domain and the agent-templates feature were removed
  from the binary, keeping the self-hosted product config-code-only.
- **Publish a demo**: instances published as demos can show an opt-in demo notice
  with one-click sign-in prefill, so visitors try a real app without hunting for
  credentials.
- **Smarter form redirects**: post-submit redirects now resolve translation keys
  (`$t:`), `$record.<column>` variables, and embedded or hidden `$query` prefill.
- Multi-select member fields build their validation on the option value, and the
  admin overview roll-up is bounded so it no longer times out under load.

## v0.15.2 — 19 July 2026

A rendering and caching fix for the compiled binary — popup surfaces built from
runtime theme variables now paint correctly, and asset cache-control headers no
longer let a stale island chunk stick around.

## v0.15.1 — 18 July 2026

Repeated upgrades are safe: re-initializing a dynamic table's schema on boot is
now idempotent, so restarting on a newer version never re-runs work it already did.

## v0.15.0 — 18 July 2026

A correctness and theming pass.

- The default theme now ships shadcn-convention alias utilities, so components
  written against that vocabulary style correctly out of the box.
- `read: all` tables are readable by anonymous visitors, as configured.
- The data-table "new record" control is fully localized.
- Kanban boards group correctly by a colored single-select field, and production
  hydration no longer breaks on empty island split-chunks.

## v0.14.0 — 17 July 2026

Templates became a first-class way to start, and the navbar and docs grew up.

- **Start from a template**: `sovrium init` can scaffold directly from a remote
  GitHub template repository, and every template ships with its own deploy and
  mirror files.
- New business-app templates — projects, helpdesk, content calendar, people,
  events, assets, expenses, a knowledge base, automation recipes — plus the
  **company-os** flagship that ties them together.
- The **"Built with Sovrium" badge**, on by default with a documented one-line
  removal.
- Navbar hover-to-open dropdowns with badges and a rotating chevron.
- The documentation sub-navigation was re-sliced into eight product tabs.
- AI agents boot inert when no provider is configured — no key, no crash.

## v0.13.0 — 14 July 2026

A hardening release across tables, forms, auth, and delivery.

- Many-to-many fields split and resolve correctly on record create, update, and
  read, including on view-backed tables.
- Admin-equivalent roles bypass field-level write permissions, and a resolved top
  custom role no longer gets caught by row-level scoping.
- Embedded forms localize per host locale, and record actions are attributed to
  the triggering user.
- The RSS feed's channel identity is driven by the page's own meta, and the docs
  "last updated" date renders in the active locale.
- An opt-in, env-gated 301 redirect maps a legacy host to its canonical path.

## v0.12.1 — 12 July 2026

Restored the released baseline migration's identity, undoing an illegal history
squash — the upgrade path from earlier versions is safe again.

## v0.12.0 — 12 July 2026

The native admin dashboard arrived, and every data component learned to read from
the platform's own APIs.

- **Native admin dashboard**: an auto-mounted, admin-only operational console at
  `/_admin` for viewing and operating on your app's runtime data — records,
  automation runs, form submissions, connected accounts, uploaded files, agent
  conversations, and page analytics. It is read-and-operate over data only;
  configuration stays code, edited in `app.ts` / `app.yaml` and re-deployed.
- **System data bindings**: lists, tables, calendars, kanban boards, galleries,
  timelines, charts, KPIs, and record drawers can now bind directly to a platform
  read endpoint, so a page can render live operational data with no custom code.
- A global, indexed admin search (⌘K) that deep-links straight to any record.
- **Docs authoring**: an "Edit this page" link (`contentDir.editUrl`), a
  per-page markdown export, "Copy / View as Markdown", and a "Last updated" stamp.
- **GDPR self-service**: data export and account erasure that work on the SQLite
  default, not only Postgres.
- RSS feed items can be sourced from a markdown-file page.

## v0.11.0 — 17 June 2026

The marketing site and the release engine both grew up.

- Rebuilt the marketing website on Sovrium's own design system, with an ambitious
  hero and a real customer trust band.
- Added a competitor-comparison SEO library at `/compare` — a single consolidated
  page family covering how Sovrium stacks up against common alternatives.
- **RAG Phase 2**: opt-in `sqlite-vec` approximate-nearest-neighbour search plus
  FTS5 hybrid retrieval, so AI features can ground themselves in your own data on
  SQLite as well as Postgres.
- **Approval-gated automations**: an automation run can now pause for a human
  decision and resume once someone approves or rejects it, and an alert-dialog
  confirmation on a page can dispatch its configured automation action.
- Documentation overhaul: a new navbar and search, a collapsible sidebar, a
  "View as Markdown" link on every article, and table-of-contents scroll-spy.
- Auto-generated `/llms.txt` and `/llms-full.txt` so AI agents can read the whole
  docs corpus in one request.
- **Cloud**: usage rollups and monthly invoicing went live.
- A theme toggle with a light-first default and no flash of the wrong theme on load.

## v0.10.0 — 2 June 2026

AI came to SQLite, and the MVP reached feature-complete.

- **AI on SQLite**: retrieval-augmented generation now works on SQLite via BLOB
  embeddings with app-side cosine similarity, and AI-compute fields run on both
  database engines — no Postgres requirement to use AI.
- **Public comments** with moderation: guest identity is captured on submit, and
  an admin queue lists pending, approved, and rejected comments.
- Cross-engine correctness: aligned SQLite and Postgres value serialization and
  ISO-8601 timestamps, and mapped numeric precision to SQL scale.
- Sovrium reached a feature-complete MVP — the remaining work is production
  hardening, not net-new features.

## v0.9.0 — 31 May 2026

A stabilization release, consolidating the large capability wave from v0.8.0.

## v0.8.0 — 25 May 2026

The broadest single release so far — most of the page and form toolkit, the full
CLI, and the admin consoles landed here.

- **Record detail pages**: breadcrumbs, related-record lists, and previous/next
  navigation between records.
- **Runtime views**: sort and column-visibility controls that readers can change
  without editing config.
- **Form controls**: date and time pickers, combobox, slider, switch, checkbox,
  radio group, number input, and a drag-and-drop file dropzone.
- **Page layout components**: tabs, accordion, callout/alert, dividers and spacers,
  an app shell with sidebar, a drawer, and a hover card.
- **The complete CLI**: `init`, `start`, `build`, `schema`, `validate`, `agents`,
  `version`, and `help`, each fully specified.
- **Admin consoles** for buckets, forms, and automation runs, plus an
  operator-controlled low-data render mode.
- Database groundwork: SQLite dynamic tables now use INTEGER ids, and view-backed
  tables get INSTEAD OF triggers so computed columns stay writable.

**v0.8.1** followed on the same day, preserving auto-generated lookup views across
restarts.

## v0.7.0 — 23 May 2026

Maintenance and release-tooling releases (through v0.7.2 on 24 May).

## v0.6.0 — 22 May 2026

Markdown pages arrived — the same engine this changelog and the docs site run on.

- **Markdown pages**: GitHub-flavoured markdown, Shiki server-side code
  highlighting, `:::container:::` directives that render as real components,
  `$t:` translations inside the body, and collection navigation with
  previous/next links.
- **CLI**: three starter templates, with a paired AI agent auto-installed on
  `sovrium init`, and TypeScript config files supported in build and schema loading.
- Runtime artifacts consolidated under a single `.sovrium/` data directory.
- Developer experience: live reload injected in dev (and absent in production),
  and cache bypass flags for fast iteration.

Patch releases v0.6.1 and v0.6.2 hardened the release pipeline.

## v0.5.0 — 21 May 2026

Sovrium became a single self-contained binary.

- Database migrations, client and island bundles, and the `init` example configs
  are all embedded in the compiled binary — no external files to ship.
- The engine resolves its own version from a build-time define, so `--version` and
  the OpenAPI document are correct in a compiled binary.

Patch releases through v0.5.3 fixed declaration-emit and packaging details.

## v0.4.0 — 14 May 2026

The first public distribution channels.

- Homebrew tap, Scoop bucket, and checksummed release archives.
- The zero-dependency `@sovrium/types` package published to npm for typed config
  authoring with `defineConfig`.

A run of same-day patch releases (v0.4.1–v0.4.10) stabilized the new release CI.

## v0.3.0 — 8 May 2026

- A "coming soon" feature-flagging system for marking work-in-progress capabilities
  (later retired once the platform reached feature-complete).
- A firmer runtime contract for code automation actions.

## v0.2.5–v0.2.11 — March 2026

The first tagged releases. The schema engine, dynamic tables, and the
configuration interpreter took their initial shape.
