This release includes 2 breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
+1 more
ReleasePort's take
Moderate signal`Daemons.dynamic` now accepts an `effects` parameter and returns a `DaemonReconciler`, breaking prior usage patterns.
Why it matters: This change breaks existing code that relied on the previous signature; developers must update calls to supply `effects` and handle the new `DaemonReconciler` return type in start-sdk v2.0.4.
Summary
AI summaryDaemons.dynamic now takes effects and returns a reconciler, breaking previous usage patterns.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Breaking | High |
`Daemons.dynamic` now takes `effects` and returns a `DaemonReconciler` instead of replacing `main`. `Daemons.dynamic` now takes `effects` and returns a `DaemonReconciler` instead of replacing `main`. Source: llm_adapter@2026-07-14 Confidence: high |
— |
| Breaking | Medium |
'setupMain' callback now accepts any DaemonBuildable rather than requiring a Daemons. 'setupMain' callback now accepts any DaemonBuildable rather than requiring a Daemons. Source: granite4.1:30b@2026-07-14-audit Confidence: low |
— |
Full changelog
What's Changed
Fixed
-
Daemons.dynamicnow composes withsetupMaininstead of replacing it.mainis alwayssdk.setupMain(...); what varies is theDaemonBuildableyou return from it — a staticsdk.Daemons.of(...)chain, or the reconciler for a runtime-varying daemon set. The OS ABI has always said as much (ExpectedExports.mainreturns aDaemonBuildable, and bothDaemonsandDaemonsReconcilerimplement it), but two signatures made that composition unwritable:setupMaindemanded aDaemonsrather than the ABI'sDaemonBuildable, andDaemons.dynamic(fn)returned amainexport — burying theDaemonsReconcilerit constructs inside a closure. The only shape that compiled was the wrong one: replacemainwithDaemons.dynamic(...), then nestDaemons.of()inside its builder. Packages adopting dynamic daemons were funnelled straight into it. Now:- Breaking —
Daemons.dynamictakeseffectsand returns the reconciler:sdk.Daemons.dynamic(effects, fn): DaemonsReconciler(wassdk.Daemons.dynamic(fn): main). Return it fromsetupMain. setupMain's callback is widened to the ABI: it accepts anyT.DaemonBuildable.
Migration — wrap the builder in
setupMainand passeffects:// before (2.0.0–2.0.3) export const main = sdk.Daemons.dynamic(async ({ effects }) => { return sdk.Daemons.of(effects).addDaemon(...) }) // after export const main = sdk.setupMain(async ({ effects }) => { return sdk.Daemons.dynamic(effects, async ({ effects }) => { return sdk.Daemons.of(effects).addDaemon(...) }) })Reconcile semantics are unchanged: inside the builder,
constRetryreruns-and-reconciles rather than firingeffects.restart(), so a watched-state change touches only the daemons whoseconfigHashmoved and the service staysrunning. Reported in #3470 - Breaking —
Breaking Changes
- `Daemons.dynamic` signature changed from `sdk.Daemons.dynamic(fn): main` to `sdk.Daemons.dynamic(effects, fn): DaemonsReconciler`; it now returns a reconciler instead of constructing a `main` export.
- `setupMain` callback widened to accept any `DaemonBuildable` rather than requiring a `Daemons` instance.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About StartOS
Browser-based, graphical Operating System (OS) that makes running a personal server as easy as running a personal computer.
Related context
Related tools
Earlier breaking changes
- v0.4.0-beta.9 Previous backups incompatible with v0.4.0; create fresh backup after updating.
Beta — feedback welcome: [email protected]