| title | Packages and runtimes |
|---|---|
| sidebar_label | Packages & runtimes |
Install a package when the native backend can own its lifecycle. Use mise for versioned runtimes and supported developer tools; use the OS manager for distro packages and application backends for desktop apps. A single setup can use several backends, but each declared package has one manager and ownership identity.
import { tools } from "@dovocode/workstation";
tools.mise({ node: "lts", go: "latest", "npm:t3": "nightly" });
tools.brew(["git", "jq"]);
tools.brewCask(["ghostty"], { greedy: true, force: true });
tools.apt(["git", "jq"]);
tools.dnf(["git", "jq"]);
tools.yum(["git", "jq"]);
tools.pacman(["git", "jq"]);
tools.flatpak(["org.mozilla.firefox"]);
tools.mas([497799835]); // Xcode; acquire it in the App Store first
tools.system(["git", "jq"]);Put declarations in a configuration's resources array. system selects the
configured platform backend, defaulting to Homebrew on macOS and detecting
APT, DNF, YUM, then pacman on PATH on Linux. Set managers: { linux: "dnf" },
managers: { linux: "yum" }, or managers: { linux: "pacman" } to choose explicitly. Package names must match
the chosen distribution's repositories; Workstation does not translate names.
Only mise accepts version selectors in declarations. force affects a cask
upgrade command; it does not independently trigger an upgrade.
Npm dist-tags such as latest, next, and nightly are resolved with
npm view <package>@<tag> version --json, honoring npm registry and authentication
configuration. The lock records the exact returned version, and mise installs
that version. Scoped npm package names are supported. Node.js and npm are
bootstrapped through mise when needed for tag resolution, including native CLI
invocations. Numeric version selectors and non-npm tools keep mise resolution.
Compatible packages automatically install, upgrade, and uninstall in native batches, with per-package verification and ownership tracking. No extra helper or flag is required; see batch behavior and recovery.
By default, Workstation adopts pre-existing packages without permission to remove them. To manage existing Homebrew formulae and casks on every configured machine:
import { defineConfig, tools } from "@dovocode/workstation";
export default defineConfig({
packageOwnership: { brew: "own", "brew-cask": "own" },
resources: [
tools.brew(["fzf", "ripgrep"]),
tools.brewCask(["ghostty"]),
{ kind: "package", manager: "brew", name: "git", ownership: "adopt" },
],
});Run workstation plan to preview, then workstation build to record ownership.
"own" claims both previously adopted packages and existing installations discovered
on a first run. Matching packages require no reinstall; packages needing an update
can be claimed and upgraded together. Only declared packages are claimed, not every
Homebrew dependency or unrelated installation. Ownership-only changes retain lock pins.
packageOwnership accepts any concrete package manager. Defaults merge by manager
across fragments; later values win and apply to all resolved declarations.
tools.system uses the policy of its resolved manager. A package's ownership
overrides that default; omitted managers use "adopt".
Claimed packages can be uninstalled when removed from the configuration. Switching
the policy to "adopt" or removing it does not relinquish ownership already recorded.
Packages installed by Workstation remain owned under either policy. Ownership is
saved in each machine's private state and survives interrupted upgrades.
| Backend | Pin behavior | Bootstrap during build | Important limit |
|---|---|---|---|
| mise | Exact resolved version | Yes | Snapshot rollback needs the old exact version available |
| Homebrew formula | Available tap version | Yes | Arbitrary historical downgrades unsupported |
| Homebrew cask | Available cask version; some remain unpinned | Yes | greedy affects explicit upgrades; no snapshot rollback |
| APT | Exact available repository version | No | Repository must retain the pin |
| DNF / YUM | Exact RPM version including epoch/release/architecture | No | Available-version downgrade supported; no snapshot rollback |
| pacman | Available sync-database version | No | No AUR, archive downloads, or automatic full upgrade |
| Flatpak | OSTree commit | No | Fresh install needs the remote's current commit to match |
| MAS | No version pin | No | Account must already have acquired the app |
packageCapabilities exports the corresponding machine-readable information.
Frozen builds validate recorded declarations/pins, but cannot give MAS or
unpinned casks historical-version support. tools.system chooses a backend;
it does not translate distribution-specific package names.
Use upgrade workflows for all or selected pins. Add repositories through provisioning before depending on their packages. Optional backend CLIs must exist before version resolution; a package declaration alone does not bootstrap Flatpak or mas.
tools.flatpak accepts exact application IDs, not search terms. Defaults are
user scope, the flathub remote, and the stable branch:
tools.flatpak(["org.mozilla.firefox"]);
tools.flatpak(["org.example.App"], {
scope: "system",
remote: "testing",
branch: "beta",
});The remote must already exist in the chosen installation. User installs do not use sudo; system mutations do. Scope and branch have separate ownership identities. An app from a different remote is a conflict, requiring an explicit migration. Removal retains application data and does not request removal of unused runtimes.
tools.pacman accepts repository package names. AUR helpers, package groups,
and archived package downloads are not included. Queries use the existing sync
database. Run your normal sudo pacman -Syu before applying Workstation;
Workstation does not run -Sy or perform a full system upgrade automatically.
tools.mas accepts numeric app IDs as numbers or decimal strings. Find IDs
with mas search or mas list. Workstation uses mas install for missing apps
and mas upgrade <id> for outdated ones; it never calls mas get/purchase.
Acquire free or paid apps in the App Store first and sign in with the matching
Apple Account. Installed app detection depends on Spotlight indexing.
MAS declarations follow available updates and have no version pin: the store
cannot supply an arbitrary historical version. Installed versions are recorded
in ownership state. Previously installed apps are adopted and retained if their
declaration is removed; owned apps use mas uninstall <id> on removal.
See the mas documentation for account requirements.
- Add a package to the relevant platform fragment.
- Run
planif the required manager/repository already exists, thenbuild. - Confirm the resource with
statusand the tool's own version command. - Review and commit the resulting lock alongside the declaration.
- Use
upgrade package:MANAGER:NAMEwhen you intend to refresh its pin. - Remove its declaration and review a plan when you no longer want it managed.
A fresh first build can bootstrap mise/Homebrew, but not every optional backend. A pinned install does not automatically activate a runtime in your terminal. Pair mise declarations with generated activation.
Unless explicitly claimed with ownership: "own" or packageOwnership, an adopted package is retained on removal. Some
backends permit adopted updates without taking removal ownership; others reject
updates that require an explicit migration. Read the reported plan before changing
ownership. Operations explains pin availability and removal limits.
Use tracked dotfiles for live source links.
Choose a file policy before replacing an existing document or adopting app-managed settings.
Use the JSONC document builder for ordered comments and nested objects/arrays.
Follow the custom executable tutorial for source hashing, staged builds, compiler dependencies, and drift detection.
Follow shells and activation to select startup files, render typed statements, and keep installed runtime versions aligned with active ones.