NixOS Integration
This guide covers the nixosModules.default NixOS module: enabling the dsh
CLI, declaring bundles and profiles, and running the web profile as a system
service. For bundle and preset details, see
Bundles and Presets. For advanced package overrides, secret
injection, and reverse proxy setup, see Advanced Usage.
Add the Flake Input
Section titled “Add the Flake Input”{ inputs.deepseek-harness.url = "github:moraxyc/deepseek-harness.nix";}If this repository is declared under another input name, such as dsh,
replace inputs.deepseek-harness in the examples with that name.
Enable the Module
Section titled “Enable the Module”Import nixosModules.default in a NixOS configuration:
{ inputs, pkgs, ... }:{ imports = [ inputs.deepseek-harness.nixosModules.default ];
programs.dsh = { enable = true; profiles.tui.bundles = [ pkgs.dsh.bundles.tui ]; defaultProfile = "nix-tui"; };}The module:
- sets
nixpkgs.overlaysso thepkgs.dsh.*scope is available; - adds a composed
dshpackage toenvironment.systemPackages; - exposes the
programs.dshandservices.dshoption groups.
Set DSH Home
Section titled “Set DSH Home”Set programs.dsh.home to export an explicit DSH_HOME through the system
environment:
programs.dsh.home = "/var/lib/dsh-cli";When unset, dsh keeps its per-user $HOME/.dsh default.
Manage the Home-Level Patch
Section titled “Manage the Home-Level Patch”programs.dsh.patch accepts a structured list of Cordis patch entries and
manages it as $DSH_HOME/cordis.patch.yml:
programs.dsh.patch = [ { id = "agent-default-model"; config = { provider = "deepseek-official"; model = "deepseek-v4-flash"; }; }];This layer applies after each profile’s own patch and therefore affects every
profile. Module-composed CLI and service packages restore the declared file
before launch. The default null leaves the file unmanaged.
Declare Profiles
Section titled “Declare Profiles”Profiles are declared as attributes of programs.dsh.profiles. A profile
named tui is materialized as $DSH_HOME/profiles/nix-tui, where
$DSH_HOME defaults to ~/.dsh.
programs.dsh.package defaults to pkgs.dsh.dsh and can be overridden when
you need a different composition or package source.
Each profile supports:
bundles: a list of bundle packages, e.g.pkgs.dsh.bundles.tui;agentPreset: an ID fromprograms.dsh.agentPresets; the referenced shipped preset is copied to$DSH_HOME/.agent-presets;patch: a list of Cordis patch operations or raw YAML applied after all bundle layers ascordis.patch.yml;mode:managed(default) ormutable;- read-only
rawNameandmaterializedName.
Example:
programs.dsh.profiles = { tui = { bundles = [ pkgs.dsh.bundles.tui pkgs.dsh.bundles.web-ui ]; mode = "managed"; };};Declare an Agent Preset under programs.dsh.agentPresets and reference it from
the profile. enableTools removes disabled only from the listed preset rows;
it does not install a provider bundle. See Advanced Usage
for a complete subagent example.
Use the materialized name for defaults instead of hardcoding the nix-
prefix:
{ config, inputs, pkgs, ... }:{ imports = [ inputs.deepseek-harness.nixosModules.default ];
programs.dsh = { enable = true; profiles.tui.bundles = [ pkgs.dsh.bundles.tui ]; defaultProfile = config.programs.dsh.profiles.tui.materializedName; };}Profile Modes
Section titled “Profile Modes”managed: everydshinvocation re-synchronizes the profile from the configuration, so local changes to managedpackage.jsonandcordis.patch.ymlare restored.mutable: Nix seeds the profile only when the directory does not exist; afterwards the user manages it withdsh pluginand Nix leaves it alone.
Neither mode takes over an existing unmarked directory with the same name.
Run the Web Service
Section titled “Run the Web Service”services.dsh runs the web profile as a NixOS systemd unit. It binds only to
loopback by default and does not open the firewall.
services.dsh = { enable = true; listenAddress = "127.0.0.1"; port = 3080; trustedHosts = [ "dsh.example.com" ];};By default the unit serves nix-web. If a custom programs.dsh.profiles.web
profile is declared, the service composes it from programs.dsh.package and
those profiles; otherwise it falls back to the web preset.
Key service options:
profile: materialized profile served by the unit, defaultnix-web;listenAddress,port, andtrustedHosts: binding and browser-trust settings;extraArguments: additional arguments appended to thedshcommand;userandgroup: fixed service account; leave both unset forDynamicUser;dataDir,homeDirectory, andworkspace: state and working paths;environment,environmentFile, andcredentials: runtime environment and secret sources;openFirewall: whether to openport, defaultfalse;autoStart: whether to start withmulti-user.target, defaulttrue.
Enable the optional systemd isolation profile when the service needs a stronger sandbox. It keeps networking available for the web server, while adding device, kernel, namespace, capability, and SUID/SGID restrictions. Additional writable paths and bind mounts must be declared explicitly:
services.dsh.isolation = { enable = true; rootDirectory = "/var/lib/dsh/root"; readWritePaths = [ "/var/lib/dsh/cache" ]; bindPaths = [ "/srv/dsh-assets:/opt/dsh/assets" ]; bindReadOnlyPaths = [ "/nix/store:/nix/store" "/etc/resolv.conf:/etc/resolv.conf" ];};rootDirectory is optional. When set, the directory is the service root and
the executable’s runtime must be made available below it. Use
bindReadOnlyPaths for the required Nix store paths, certificates, DNS files,
or other read-only inputs. Both bind options use systemd’s
source:destination syntax. The service’s homeDirectory and workspace
remain writable in isolation mode; readWritePaths adds more paths.
The unit is named dsh-web:
sudo systemctl start dsh-websudo systemctl restart dsh-websystemctl status dsh-webjournalctl -u dsh-web -fThe service defaults to systemd DynamicUser and stores state under
/var/lib/dsh. For reverse proxies, secret files, credentials, fixed
user/group mode, and the complete service option list, see
Advanced Usage.
Troubleshooting
Section titled “Troubleshooting”- If
attribute 'dsh' missingappears while evaluating a NixOS module, check thatnixosModules.defaultis imported and that no other module replacespkgswith a fixed package set that predates the dsh overlay. - If your flake passes
pkgsthroughspecialArgstonixosSystem, NixOS ignoresnixpkgs.overlays. Use a pkgs set that already includes the dsh overlay. When reusing an existing pkgs set, importinputs.nixpkgs.nixosModules.readOnlyPkgsand setnixpkgs.pkgsto that pkgs set. - If
dshstarts but does not use the expected profile, check thatprograms.dsh.defaultProfileuses the materialized name (nix-tui), not the raw profile key (tui).