Skip to main content
View as Markdown

How Sovrium Is Built

If you cloned Sovrium from GitHub and went looking for tests, you found none. That is a real finding, and it deserves a real answer rather than a reassurance.

The GitHub repository is not where Sovrium is developed. It is the artifact a release produces. This page explains what that means, what the development repository contains, why it is private today, and what happens to your instance if the company behind Sovrium stops.

The GitHub repository is a release artifact

Sovrium is developed in a private monorepo on a self-hosted Forgejo instance. Each release runs scripts/filtered-mirror.sh, which builds a filtered copy of that tree and publishes it to github.com/sovrium/sovrium.

The filter is an allowlist. Nothing reaches GitHub unless a line of that script explicitly copies it. Source code, the @sovrium/types package, build scripts, migrations, templates, the Dockerfile, and the release metadata are copied. Everything else is absent by default, including:

Excluded What it is
specs/ The end-to-end test suite
*.test.ts / *.test.tsx under src/ and packages/ Co-located unit tests, stripped after the copy
.forgejo/ The CI workflows
playwright.config.ts The E2E runner configuration
scripts/check-quality.ts, scripts/check-progress.ts The two quality pipelines
eslint/ The modular lint configuration, including the layer-boundary rules
docs/ Architecture decision records, patterns, and user stories
.claude/, CLAUDE.md Internal development instructions

Those exclusions are enforced, not merely intended. The script carries fail-closed canaries: if any protected path is present in the built tree, the release aborts before anything is pushed. The root package.json is replaced with a curated manifest whose scripts block is reduced to the public build subset, which is why no test: script is visible on GitHub, and a second canary aborts the release if a development-only script target leaks into it.

Publishing is a fast-forward child commit on top of the existing GitHub HEAD. There is no orphan commit and no force-push, so the public history stays continuous and browsable: one commit per release.

Why the development infrastructure is private

The honest reason is competitive, and it is worth stating plainly rather than dressing up as policy.

The engine is free forever in self-hosted mode. The test suite, the CI pipeline, and the internal tooling are a different thing: together with the source, they are a working blueprint for reproducing the product. With current AI assistance, a complete specification suite is close to a build plan. Sovrium is bootstrapped and solo-founded, and that asymmetry is one of the few it has.

So the reproduction kit is held back. The product is not. Every feature works in the self-hosted binary, with no license keys, no feature gates, and no paid edition.

Read this as deferred, not withheld. The continuity commitment below sets the condition under which the private infrastructure is published.

What the suite covers

These figures come from SPEC-PROGRESS.md, which is generated by bun run progress and regenerated by CI on every push. They are a static count of what the suite containsbun run progress reads the spec files, it does not execute them. Whether those tests pass on a given commit is what CI reports, not what this table says.

Spec files 908
E2E tests 7,933 total: 6,902 @spec + 1,031 @regression
Implemented 7,901
Unimplemented placeholders 32 test.fixme(), tracked
Unit test files 826 (787 under src/, 39 under scripts/)
Unit test cases ~10,300, counted statically
User stories 894, tracing 7,426 spec IDs
Feature domains 23 engine domains plus 2 business-app domains

The structure matters more than the count, because it is what ties a test to a requirement.

Each .spec.ts file holds two tiers. The first is a set of @spec tests, one per acceptance criterion, each carrying the ID of the user story it satisfies. The second is a single @regression test that replays those same criteria as test.step() sections in one continuous workflow. So a feature is covered twice: once criterion by criterion, once as a user would actually move through it. The 894 user stories and the 7,426 traced spec IDs are the join between the two, which is what makes coverage a question with an answer rather than a feeling.

What is not measured

Sovrium publishes no code-coverage percentage, because it does not measure one.

The quality pipeline enforces a test-file presence invariant: every source file in the domain layer must have a co-located *.test.ts sibling. That is a structural rule, not a line or branch threshold. No coverage threshold is configured anywhere in the project, and a present-but-empty test file satisfies the gate.

A number would be easy to produce and would not mean much. The limit is stated instead.

What runs, and when

The CI workflow fires on every push and every pull request to main. It runs three jobs on a dedicated 8 vCPU / 32 GB runner, with a 50-minute ceiling on the longest.

  1. Lint. ESLint across the tree, including the layer-boundary rules that keep the architecture's dependency direction enforceable rather than aspirational.
  2. TypeCheck and unit tests. Full tsc type check, the unit suite, validation of every shipped example config through the real sovrium validate command, and a dual-dialect migration drift guard that regenerates both the PostgreSQL and SQLite migration sets and fails if a schema change reached only one of them.
  3. E2E. Gated on the first two jobs succeeding, so the browser suite never runs against a red type check.

One precision, because it is the kind of thing worth getting right rather than rounding in our favor: CI runs playwright test --grep "@regression|@packaging". That is the 1,031-test regression tier plus the packaging tier, which exercises the compiled binary built earlier in the same job. It is not the full 6,902-test @spec corpus, which is too slow to gate every push and is run locally and on demand instead.

Locally, two pipelines sit in front of a commit.

bun run quality runs thirteen gates in sequence, stopping at the first failure: Prettier, ESLint, workflow lint, a consolidated drift check (design tokens, brand identity, generated assets, schema, changelog, documentation links, and more), guide-config validation, a committed-secrets scan, a dependency audit against the lockfile, TypeScript, Effect diagnostics (opt-in), unit tests, Knip for unused code, the test-file presence invariant, and a smart E2E step that analyzes the changed files and runs only the related regression specs.

bun run progress validates the content quality of the specs and user stories themselves, and regenerates SPEC-PROGRESS.md.

Security posture

Vulnerabilities go to security@sovrium.com. You will receive an acknowledgment within 48 hours and an initial assessment within 5 business days. Please do not open a public issue. Reporters are credited in the release notes, with permission.

The codebase is written against five standing rules, applied at the platform layer so every app inherits them without per-app configuration:

  • Authentication before sensitive logic, with object-level authorization checked on every request, and 404 rather than 403 on failure so resource existence stays unobservable.
  • One canonical HTML sanitizer for any content rendered from user or record data. There is deliberately only one, because a second sanitizer is a second set of bugs.
  • Parameter-bound SQL for every runtime query, with validated identifiers quoted separately.
  • No secrets in client bundles or logs, and no raw database rows returned from an API route.
  • GDPR export and erasure wherever a feature touches user data, with erasure a hard delete rather than a soft one.

Security Hardening documents what this produces at runtime: the response header set, CSRF and cross-origin enforcement, rate limits, and the anti-enumeration behavior.

No third-party penetration test has been commissioned to date. The security work above is internal: design rules, code review, and specs that lock the behavior. That is not the same as an independent audit, and it should not be read as one.

Continuity

The reasonable question behind "is this safe to build on" is not really about tests. It is: what happens to my instance if you stop?

The engine is free forever in self-hosted mode. No license keys, no feature gating, no enterprise edition, no phone-home, no kill switch. Sovrium reads your config and runs. If the company disappeared tomorrow, every running instance would keep running, because there is nothing for it to check in with.

The license converts on its own. Sovrium ships under BSL 1.1, which becomes Apache 2.0 on 2030-08-01. That conversion is written into LICENSE.md and needs no action from anyone, including us. See License for what the terms permit before then.

The bus factor is one. Sovrium is solo-founded and bootstrapped. Saying otherwise would be the kind of claim this page exists to avoid. The hedge is documentation discipline: architecture decision records that capture why each choice was made, user stories that state what each feature is for, and a spec suite that pins the behavior. Those exist so a future maintainer can re-derive the context rather than reverse-engineer it.

If the company ceases operations, the private development infrastructure is released publicly. The spec suite, the CI pipeline, and the internal tooling. The reason for holding them back is a going concern that would no longer exist, and the people left running Sovrium would need exactly those artifacts to maintain it. This commitment is recorded in SECURITY.md in the public repository so it does not depend on this page remaining online.

See also

Last updated August 11, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium