§ Platform / Built to last
Your work outlives our releases.
Scellis separates what ships from what lasts: the app itself is a small, versioned engine — everything you build is immutable content that declares exactly what it needs. Content the engine cannot run is refused loudly instead of mis-executed, a newer engine must run everything an older one ran, and an offline device keeps computing on its cached engine indefinitely.
An engine that ships, work that endures
The application you download is deliberately small — an engine, and nothing else. Every capability you actually use — blocks, kernels, models, viewers — arrives as content from the catalog, identified by the hash of its bytes. Your work never lives inside our binary, so our release cycle cannot hold it hostage: the two move on separate clocks, joined by one explicit contract.
The contract has two halves. The engine carries a version number and a list of what it can do — f16 training, secure aggregation, and so on — and content tests for capabilities, never version numbers. Content carries the list of what it needs. When the running engine cannot satisfy a declaration, the loader refuses loudly: a clear diagnostic and a “needs a newer Scellis” badge in the catalog — never a crash, and never a silent mis-execution, because a wrong result ages far worse than a refused one. How the engine declares what it can run is in the guide.
A newer engine runs everything the old one ran
| Change | Release class | Your existing work |
|---|---|---|
| A new capability | additive | Runs untouched — your content never referenced the new value. |
| Any new engine release | verified before release | The frozen golden corpus must re-execute successfully before a release exists. |
| Retiring a capability, changing its meaning | major | A migration path is required — never a silent drop. |
| A new version of a block or op | coexists | Pins keep resolving the old version — published versions are immutable. |
| Content newer than your engine | refused loudly | A “needs a newer Scellis” badge — never a mis-execution; you update when you choose. |
The requirement is mechanical: automated checks re-execute the frozen corpus against every engine change and keep every declaration honest — before anything ships.
Backward compatibility here is a tested requirement, not a changelog courtesy. A frozen golden corpus — sample ops, blocks, and models from every earlier engine — is re-executed by the current engine, within its declared tolerances, before a release can exist. If a change would alter an old op's numbers or break its execution, the release is blocked: correctness is versioned the same way it is proven.
The engine grows by adding, never by rewriting: a new capability is a value your existing content never referenced, so that content runs untouched. Retiring a capability or changing what an existing one means is a major release with a migration path, never a silent drop. And published content is immutable by identity: a version is a contract, new versions coexist beside old ones, and anything pinned keeps resolving the exact bytes it always did.
Updates are atomic, verified, and taken on your terms
An engine update comes down the same path as content. The new app shell is fetched in the background and its bytes are verified against a signed hash before it is activated, then swapped in one step — a half-updated app never runs, and your local database is untouched. What you see is a calm “reload to update” signal carrying release notes: the new version and exactly what it changed (changelog) — never an app that quietly changed under your feet overnight.
Decline the reload — or lose the network — and nothing degrades. An offline device keeps computing on its cached engine indefinitely: the local-first working set is complete on the device, so a laptop in the field can stay on one engine for a year and keep drawing, running, and training the whole time. An update here is something you take, not something that happens to you.
An update never bricks a returning tab
| On your device | Across an update | Mechanism |
|---|---|---|
| Drafts & un-synced edits | preserved, always | Durable storage only ever grows; every automatic repair keeps it. |
| Committed work | byte-identical | Addressed by hash — the bytes at a hash never change. |
| Your copy of the catalog | re-derived in seconds | Cleared and rebuilt through normal sync when the stored shape changes. |
| The app itself | swapped in one step | Verified against a signed hash before it is activated. |
| Settings & profiles | preserved | Durable state — it only ever grows, never destructively. |
| A wedged tab | one line to recover | A documented reset in the address bar reloads clean — no developer tools required. |
Releases are proven, not hoped: a returning-user check loads the fresh app against an old database and a stale cache, and must repair itself to a working Studio.
Local-first makes “coming back with old state” the normal case, not the edge: a new release routinely meets a browser still holding the previous one's caches and local database. So the shape of everything stored on your device carries its own fingerprint. On startup, before any stored data is touched, a changed fingerprint clears exactly what can be re-derived — the catalog copy, the caches, the app shell — and rebuilds it through normal sync in seconds. Invalidated and re-derived, never migrated in place, never left half-consistent.
What only your device holds — drafts, edits still waiting to sync, your settings — is preserved by every automatic repair. Only you can clear it, with one documented reset in the address bar; that same one-liner is the recovery for any wedged tab, with no developer tools required. Even a misbehaving background worker is recoverable by design, so a bad update can never become a permanent wedge.
A run remembers the engine that produced it
Reproducibility across releases is recorded, not remembered. Every run pins everything it used by hash *and* the engine version that executed it, at a declared strictness — so reproducing a result years later starts from a machine-readable statement of what ran, on what, and how strictly. Cited, committed work resolves to identical bytes for as long as the link exists, and a reproduction replays on any engine that satisfies the recorded contract.
Where the guarantee has an edge, the record says so instead of rounding up: GPU floats re-execute within the tolerance the run declared, and bit-exactness across devices takes the CPU reference path — boundaries we print in honest limits. The result is reproduction that stays honest across years: the claim travels with the run, calibrated, rather than decaying into folklore. The working practice is in the reproducibility guide.
Longevity that does not depend on trusting us
A longevity promise is weakest exactly when you need it most — so the exits are built in. Everything you make exports as portable, documented .scellis-* files — workflows, Packs, models, datasets — reproducible byte for byte, verifying their own hash on import; plus a device-local backup of everything on your device (credentials excluded by construction: they never leave it). What you author is yours, and it leaves whenever you say so.
The formats survive without our goodwill: the schemas, the export files, the conformance corpus, and the CPU reference are published permissively — the interchange ring — so anyone can read, verify, and build on your work without asking us. The engine itself is published as source under fair-code terms, and each release converts, in time, to the permissive license. We plan decades of releases; the point of the architecture is that your work does not have to bet on that.