Upgrading
Karsa keeps itself up to date, like a phone's Software Update: every day it checks for a new release, downloads it, and installs it overnight if you let it, or waits for you to tap Install. This page explains what that does, so an update is never a mystery.
One version for Karsa
Karsa's core ships as one release with one version (0.3.0, on one tag v0.3.0): the karsa CLI, the Karsa server, karsa-enforce-security (the one step that needs root, with the helper it installs), the tunnel client, the web UI and this manual. They're built together and installed together, so you never have a 0.3.0.karsa-enforce-security server with a 0.3.0.karsa web UI. karsa version says which you have.
Apps (OpenCode, the Browser, Remote, …) keep their own versions. Each says which Karsa it needs, and an update brings each project's apps up to date too.
The release list, signed
What exists is listed in the release list, registry.json on karsa.app: the release's version, what's new, and every file with its shaundefined. Beside it, registry.json.sig is its signature by Karsa's release key. Karsa carries that key's public half in its code and ignores a list whose signature doesn't verify ("the release list isn't signed by Karsa's key: ignored"), so a changed website can't ship you a file. Each file is then trusted by its shaundefined in the signed list.
The very first install comes over HTTPS from karsa.app and checks shaundefineds (curl -fsSL https://karsa.app/install.sh | sh); from then on karsa install and every update check the signature.
The Updater
Updates are an app in the System project, the Updater (@karsa/updater), listed on its Apps page like any other. karsa install puts it there, and a start that finds it missing installs it again, unless you removed it.
- Every day at undefined:
update_checkyour time its subscriber, Check for updates every day, runsupdate_check. The time zone is this machine's at install. If Karsa was off at undefined:update_check, it checks once when it next starts. update_checkreads the signed list and compares it with what's installed: Karsa's core, and every project's apps. Then it downloads, and in modeinstallinstalls too.update_downloadfetches the release's files for this machine into$KARSA_HOME/packages/.staging/<version>/, each checked against its shaundefined. A finished file isn't fetched again; a half one resumes.update_installinstalls what's downloaded (below).
Each release seen is an update record in the Updater: its version, what's in it (each piece of Karsa, each app with its project, old → new), its size, its state (available → downloading → downloaded → installing → installed, or failed and why) and when each happened. The banner, karsa status and your agents read it.
What you choose
The Updater's Overview says all of this in plain words, so an agent you ask knows what to change:
config.mode:install(the default) downloads and installs by itself;downloaddownloads, then waits for you to tap Install.config.apps:true(the default) also updates each project's apps.- The time: the subscriber's
trigger.daily("03:00"). "Check for updates at undefined am" istrigger.daily: "06:00". An update of the Updater keeps your time. - Pause it: disable the Updater; its daily check pauses, and
karsa upgradeand the banner still work. - Stop it for good: uninstall the Updater.
karsa upgradestill works;karsa package install @karsa/updaterbrings it back.
The banner and karsa upgrade
On every page of the web UI, for you (the owner), a banner says where an update is:
- A new version: "Karsa undefined.undefined.undefined is available." Download and install, Download only, and What's new.
- Downloading: "Downloading Karsa undefined.undefined.undefined… undefined%".
- Downloaded: "Karsa undefined.undefined.undefined is ready." Install: it restarts Karsa; open tabs and running agent turns carry over. An app that updates with it restarts on its new code (the banner names it): when that's the Browser, its open tabs close.
- Needs the sudo step: the exact line to run once in Terminal (below). It turns into Install once that's done.
- Installed by itself: "Karsa updated to undefined.undefined.undefined overnight." (shown once).
- Failed: the reason, and Try again.
Later hides it until the next version. In a terminal:
karsa status # the same state, in one line
karsa upgrade # check
karsa upgrade --download # download (and --install too: install once it's down)
karsa upgrade --install # install what's downloaded
karsa upgrade --undo # put the previous version back
The banner's buttons, karsa upgrade and an agent all run the Updater's same fns.
The sudo step
Karsa never asks for your password. Its one root step is karsa-enforce-security, which installs the root helper /usr/local/libexec/karsa-run-as. Most releases leave the helper as it is. When one changes it, you run that release's script once:
sudo sh ~/.karsa/packages/.staging/0.3.0/karsa-enforce-security
(The banner gives you the exact line.) Karsa knows whether you've done it without asking: the release list carries the shaundefined of the helper its script installs, and Karsa hashes the installed helper each time it's asked. Equal means done. Until then, Install refuses and the update waits at "downloaded"; in mode install, the next daily check installs it. A release's helper keeps everything the release before it uses, so the Karsa still running keeps working with the new helper, and a restart before you install still starts.
What Install does
.previous. Checks first. Every downloaded file again, against the signed list, and the root helper. Nothing changes unless both are right. karsa.previous. Waits for runs that wouldn't outlive a restart to finish, up to undefined minutes. Agent turns run detached and fns marked survives_restart (open browser tabs) carry over the restart; an app that updates in step doc.previous/ restarts anyway. karsa-server.previous. Swaps the files. Each file in use is kept as .previous (karsa.previous, karsa-server.previous, the web UI's dist.previous/, doc.previous/), and each new one lands by a rename, so nothing is ever half-written. The web UI and the manual are unpacked into a new folder, then the folders are renamed: never deleted first. dist.previous/. Restarts. The server starts the new karsa, then stops gracefully, leaving the runs that carry over running. The new karsa waits for it to exit, starts the new server and checks that it answers /health as the new version within undefined seconds. If it doesn't, it puts the .previous files back, starts the previous version, and the update record says why. karsa upgrade --undo does the same by hand. doc.previous/. Updates the apps. Each project's apps that have a newer version are reinstalled from it: their records, config and data stay, and their running fns restart on the new code. A release that only has newer apps installs without a restart.
A run in flight carries over. An agent's turn, or any fn run started detached, is its own process: the stopping server pauses it and leaves it running. Whatever it sends Karsa meanwhile waits and goes through once Karsa is back, which picks the run up where it was. If Karsa isn't back within undefined minutes, those calls fail with "Karsa didn't come back within undefined min". A run whose process died while Karsa was down is marked interrupted, and its chat says so.
The download cache
$KARSA_HOME/packages/ is the download cache, not an installed location: the fetched package bytes (app seeds, the web UI, the manual bundle) and .staging/ (releases being installed), owned by install and update, replaced freely. Installing an app copies out of the cache into the project (ids minted, references rewritten); from then on the project owns its copy. Editing files under packages/ changes nothing that's installed.
Package-owned content refreshes on update; your content stays put. The manual is replaced (you read it, you don't edit it); a project's own files, like its index.md, are never overwritten.
Binaries and karsa doctor
Karsa needs git and deno on your system. Beyond that, a non-native app wraps a program of its own (OpenCode, Claude Code, Codex): its own check fn says whether it's ready, and its install fn installs it. Updates don't touch those programs. Run karsa doctor to see Karsa's prerequisites and each app's check (--fix runs the installers of enabled apps that aren't ready). To refresh one app from its package by hand, use Reinstall on its page.
Where to go next
For what to do with an up-to-date Karsa, read Using Karsa and Building a Karsa app.