0.3.1 · · Release on GitHub
Kernl 0.3.1
Upgrading from 0.3.0 or earlier
- Install this release by hand once. The fixes below are in the updater,
and an installed copy updates itself with the updater it already has — the
one that cannot. On Windows run the new MSI (or unzip over the portable
copy), on macOS open the new dmg, on Linux re-run
install.sh. Every update after that happens from the "Update now" button.
Added
- Restart Kernl from the dashboard (Settings → About). It relaunches the
way the install was started — start.bat, the .app, the systemd unit, the
container's restart policy — so an updated extension takes effect without
closing a console window or hunting for a shortcut.
POST /api/update/restartdoes the same.
Fixed
Every extension page was blank on Windows.
/ext-assetschecked containment withstartsWith(dir + "/"), which never matches a backslash path, so Cinema and every other extension page answered 404. Every path jail in the kernel, the extensions and the dashboard now compares throughrelative(), case-insensitively on Windows, and the Windows release smoke test requests an extension page.Offline subtitles failed on a PC without the Visual C++ runtime. whisper-cli and onnxruntime import MSVCP140, VCRUNTIME140 and VCOMP140; the Windows packages carry them beside the binaries, and the release fails when they are missing.
Cinema on Windows: converted videos played black (
/api/cinema/media/convert/fileanswered 401), whisper aborted under a profile name with accents, an interrupted model download left a truncated model that was trusted forever, and a failed extraction left its WAV in the temp folder.Extension packages did not resolve for ordinary Windows users. The payload link was a directory symlink, which needs administrator rights; it is a junction now.
Claude Code on Windows. The CLI is found without
which; kernel tools reach chat and agents over HTTP with the API token instead of a Unix socket under/tmp; agent permission rules use the absolute//pathform — a single leading slash anchors at the settings source and never matched on any platform — and Docker agents use the stdio bridge that actually ships.POSIX-only tools and paths on Windows: repos exec and search, plugin zip installs, the workspace evolver, filesystem-commander remote copies and its refuse-to-delete-a-root guard, security scans outside the data folder, the IRC certificate message,
kernel_code_read'sdata/block,HOME, CRLF frontmatter,tartaken from PATH, and a closed console window skipping the clean shutdown.The dashboard rejected Windows paths in Filesystem, Workspace, repo registration, Files and the licence page, and showed ⌘K on keyboards without a ⌘ key.
Extension updates take a per-extension lock, update built-in extensions into the data folder instead of the install folder, survive a folder Windows keeps locked, and no longer delete the shipped copy on uninstall.
Sandboxed agents on a named-volume stack wrote into an empty
/workspaceand lost their files with the container. The workspace is mounted from the data volume, and the agent drawer shows the office home an agent actually runs in.macOS could never update from inside the app. The bundle was located two folders above
Contents/Resourcesand then required to end in.app/Contents, which it never does, so every attempt was refused with "could not work out which directory to replace". The check that followed verified the signature of the app already installed rather than the one downloaded, and refused because releases are not signed yet. The downloaded bundle is verified now: a broken signature is refused, an unsigned build is installed with its quarantine attribute removed. An app running translocated from Downloads, from the disk image, or from a folder the account cannot write says so before anything is downloaded.Package installs update from the app. The .deb or .rpm is downloaded, verified and installed through the desktop's administrator prompt (pkexec), then Kernl restarts through systemd, or relaunches when it was started by hand. Without pkexec the message is the exact install command for the file already downloaded. It used to suggest
apt upgrade kernl, which does nothing: no repository publishes Kernl.A failed update left no Kernl running. The kernel exits before the helper swaps anything, and every failure in the helper — a folder it could not move, msiexec failing, a declined UAC prompt — just exited. Every way out now starts Kernl again, and records what happened for the next boot.
An update that installed a version that does not start was final. The helper keeps the previous copy until the new version answers on
/api/health, and puts it back if it does not within five minutes.The dashboard never said whether an update worked. It showed "restarting, this page will reconnect" and stopped listening; losing contact mid-download also read as success. It now waits for Kernl to come back, then says "now on vX", or what went wrong.
Other Windows accounts could not update an MSI install. The MSI's marker lived under the installing user's HKCU, so any other account saw an unzipped copy and tried to move Program Files. The installer writes an HKLM marker with the install folder, and a copy under Program Files is never swapped.
msiexec's "success, reboot required" (3010) counted as a failure.
Installing an rc and then its final build gave two Add/Remove Programs entries, because both carry the same MSI version.
The Windows helper trusted whatever
findandtarcame first on PATH. Git for Windows' GNU find ended the wait loop at once, so the swap ran with the kernel still alive. System tools are called from System32 by full path, and moves are retried while Windows keeps a just-closed file locked.Exiting for an update skipped the kernel's own shutdown and left whisper, ffmpeg and other child processes holding files in the install folder. They are stopped first, and the kernel closes through its normal shutdown.
A portable Linux copy could delete its own database on update. Started from inside the extracted folder, Kernl kept
data/there, and the update replaces that folder. The tarball ships akernllauncher that keeps data in~/.local/share/kernl, and the updater refuses while data is inside the install.Every successful update left its download behind, beside the install or in the temp folder. The next boot removes it.
Docker offered an "Update now" button that could only refuse. It shows the rebuild command instead.
A portable copy could report the version of an unrelated package.json. The lookup started three folders up, which for a tarball under
~/appsis the home folder.install.sh on macOS installed a second, versioned copy on every re-run. It installs
Kernl.app, like the dmg.In-app update could not work on Windows, and said so only after refusing. An install was recognised by the running code sitting in a
bin/directory — the shapestage-payload.shproduces and the macOS bundle and Linux tarball ship. Butbuild-zip.shandbuild-msi.shboth copy the bundled kernel to the package ROOT, so a Windows install runs fromC:\Program Files\Kernland was classified "not an installed copy". The one platform with no package manager behind it was the one that could never update.An MSI install is upgraded by Windows' installer now, not by moving its directory. A swap under Program Files needs permission this process does not have, and even where it works it leaves Add/Remove Programs advertising a version that is no longer on disk. The app downloads the new MSI, verifies it, and hands it to
msiexec /i /qb— one UAC prompt, the installer's own progress, registry and uninstaller kept in step. A portable copy is still swapped, which is right for a directory nobody else owns.The updater asked for asset names the release does not publish. It built them by hand —
Kernl-…-windows-x64.zipagainst a publishedkernl-…-windows-x64.zip. GitHub resolves asset names case-insensitively so the download survived; the checksum lookup, an exact string comparison against SHA256SUMS, did not, and the update failed after transferring 311 MB. The name now comes from the release API, so what gets verified and what gets fetched are the same string by construction.The Windows helper could not sleep, so it never swapped anything. It used
timeout, which exits immediately with "Input redirection is not supported" when stdin is redirected — and the helper is spawned with its stdio ignored. The wait loop burned its sixty tries in milliseconds and gave up, by which point the kernel had already exited: a closed app, no update, no relaunch.A failed first move could nest the new install inside the old one. The move that parks the previous copy was unchecked, and
moveinto an existing directory moves into it — so a denied move dropped the new tree inside the live install, reported success, and deleted a backup that was never made.The relaunch pointed at a directory. Correct for
openon a macOS bundle; on Windows it ranstart "" "C:\Program Files\Kernl", which opens Explorer, and on Linux it tried to execute a folder. Windows relaunchesstart.bat, the documented entry point the Start Menu shortcut already uses, and the Linux portable copy runs its bundled runtime against its bundled kernel.In-app update on Linux returned 404. The portable tarball was built and uploaded as a CI artifact, but the release only attached
Kernl-*-macos.tar.gz, so the file the updater downloads was never published.Two clicks started two updates. Nothing guarded the apply path, so a second click — or a second tab, or a retry after a timeout — began another download, wrote another helper and armed another exit timer, with both helpers racing to move the same directory. The second would find the install already gone.
A portable copy on another drive could never be updated. The download was staged in the system temp directory, and
moveon Windows cannot move a directory across volumes: with the app on D: and TEMP on C:, the helper waited for the kernel to exit and then failed at the one step that matters. Staging now happens beside the install, which on Linux and macOS also turns a cross-device copy of several hundred megabytes into a rename.An MSI installed outside Program Files was swapped instead of upgraded.
INSTALLDIRis user-overridable, so "is this an installer's install?" cannot be answered by the path. It now reads the registry valueproduct.wxsalready writes, and only falls back to the path when that cannot be read at all.Every failed attempt left the download behind. Up to three hundred megabytes per click, in the temp directory, with nothing that would ever clean it up.
Verifying the checksum held the whole archive in memory. The file was read in one piece to be hashed — a 311 MB spike on top of the copy already on disk. It is hashed in chunks now.
Nothing had a timeout. A connection that opened and then went quiet left the progress bar on "downloading" forever, with no cancel button anywhere. A download that receives nothing for sixty seconds is now abandoned and says so, and the calls to the release host give up after fifteen.
A moment offline hid a release for six hours. The check cached failures with the same six-hour lifetime as answers, so one flaky fetch left the UI quiet for the rest of the day — and a quiet UI is exactly when nobody thinks to press "Check for updates". A failure is kept for a minute now, and a GitHub rate limit for five and reported as itself rather than as "offline".
The update check identifies itself to GitHub, which asks callers to and allows sixty anonymous requests an hour per IP.
Added
- A test that checks the asset names the updater asks for against the names a real release published. The previous test asserted the hand-built name against itself, which passes however wrong that name is — and is why all of the above shipped.
- Tests for the update check itself, which had none despite taking an injectable fetch for exactly that purpose: version ordering including the prerelease rule, and what a failure is allowed to do to the cache.
Changed
docs/ARCHITECTURE-MAP.mdis gone. Its contents now opendocs/ARCHITECTURE.md, which is where they were always headed: the two files described the same system at two depths and each began by pointing at the other. A link to the old path will no longer resolve.- The dashboard and desktop manifests carry the kernel's version instead of
sitting at 0.1.0 while the kernel reached 0.3.0, and
scripts/release.shbumps them with it, so a checkout no longer reads as three projects at three different ages.