高级用法
本文覆盖自定义 profile、覆盖方式和 Overlay。模块接入见 NixOS 集成 和 Home Manager 集成; 快速开始与常用命令见 README。
导入 nixosModules.default 并启用 programs.dsh:
{ imports = [ inputs.deepseek-harness.nixosModules.default ];
programs.dsh = { enable = true; profiles.tui.bundles = [ pkgs.dsh.bundles.tui ]; defaultProfile = "nix-tui"; };}名为 tui 的 profile 会生成到 $DSH_HOME/profiles/nix-tui
(默认 ~/.dsh/profiles/nix-tui)。Nix 只同步由它管理的 profile;已有但没有
Nix 标记的同名目录不会被接管或覆盖。
defaultProfile 使用生成后的名称(nix-tui)。不要直接填原始 profile key(tui)。
没有显式传入 --profile 时,dsh 使用这个名称;显式传入时仍可选择任何可用 profile。
每个声明的 profile 都暴露只读的 rawName(tui)和
materializedName(nix-tui)。在模块中,defaultProfile 可使用
config.programs.dsh.profiles.tui.materializedName,避免手写前缀。
每个 profile 支持 bundles 和一层 YAML patch;patch 会作为
cordis.patch.yml 在 bundle 层之后应用。
programs.dsh.patch 以结构化数据管理 Home 级
$DSH_HOME/cordis.patch.yml。该层在所选 profile 的 patch 之后应用,并由
所有 profile 共用。
NixOS Web 服务
Section titled “NixOS Web 服务”启用 services.dsh 可以把 web preset 作为 systemd 单元运行。默认只监听
loopback,并且不打开防火墙。
{ imports = [ inputs.deepseek-harness.nixosModules.default ];
services.dsh = { enable = true; profile = "nix-web"; listenAddress = "127.0.0.1"; port = 3080; trustedHosts = [ "dsh.example.com" ]; };}服务把 DSH_HOME 放在 /var/lib/dsh/home,把
/var/lib/dsh/workspace 作为工作目录。如果覆盖 dataDir、
homeDirectory 或 workspace,请确保服务用户可写这些路径。
除非同时设置 user 和 group,单元默认使用 systemd DynamicUser。
Dynamic 模式把状态放在 /var/lib/dsh;固定用户模式会为配置的
dataDir、homeDirectory 和 workspace 创建对应目录。
如果需要更严格的 systemd 沙箱,请显式启用隔离模式:
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" ];};隔离模式保留网络能力,并增加设备、内核、命名空间、能力以及 SUID/SGID
限制。homeDirectory 和 workspace 仍然可写;如需其他可写路径,请使用
readWritePaths。设置 rootDirectory 后,程序运行时依赖必须位于根目录中;
请使用 bindReadOnlyPaths 挂载只读运行时输入。两个 bind 选项都使用 systemd
的 source:destination 格式。
自定义 profile
Section titled “自定义 profile”services.dsh 会从 programs.dsh(或 services.dsh.profiles)声明的
profile 组合服务包,自定义 web profile 会直接用于服务:
{ programs.dsh.profiles.web.patch = '' # 针对 web profile 的 cordis patch 操作 ''; services.dsh.enable = true;}当 services.dsh.profile(默认 nix-web)出现在 programs.dsh.profiles
中时,单元会运行由 programs.dsh.package 与该组 profile 组合出的包;
否则回退到 web preset。services.dsh.profiles 支持与
programs.dsh.profiles 相同的 bundles、patch、mode 选项;显式赋值会
替换从 programs.dsh 继承的 profile。
不要把密钥写进 Nix 配置,使用运行时密钥来源。
environmentFile:
environmentFile 由 systemd 直接读取,与 LoadCredential 分开加载。
services.dsh.environmentFile = "/run/secrets/dsh.env";文件使用 systemd EnvironmentFile 语法:
DEEPSEEK_API_KEY=...DSH_PERMISSION_MODE=workspace-write使用 sops-nix 时,把 environmentFile 指向渲染后的 secret:
imports = [ inputs.sops-nix.nixosModules.sops inputs.deepseek-harness.nixosModules.default];
sops.secrets."dsh-env" = { };services.dsh.environmentFile = "/run/secrets/dsh-env";使用 systemd credentials 时,用 credentials.<name> 指定
LoadCredential 来源。如果 credential 本身是环境文件,命名为 env,再把它
作为 EnvironmentFile 加载:
services.dsh = { credentials.env = "/run/secrets/dsh.env"; environmentFile = "/run/credentials/dsh-web/env";};credential 会暴露在 /run/credentials/dsh-web/<name>。
保持 listenAddress 为 loopback,把公网入口交给反向代理。Nginx:
services.nginx = { enable = true; virtualHosts."dsh.example.com" = { forceSSL = true; enableACME = true; locations."/" = { proxyPass = "http://127.0.0.1:3080"; proxyWebsockets = true; extraConfig = '' proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; ''; }; };};
services.dsh.trustedHosts = [ "dsh.example.com" ];Caddy:
services.caddy = { enable = true; virtualHosts."dsh.example.com".extraConfig = '' reverse_proxy 127.0.0.1:3080 '';};
services.dsh.trustedHosts = [ "dsh.example.com" ];服务单元名为 dsh-web,默认随 multi-user.target 启动。设置
services.dsh.autoStart = false 后需要手动启动:
sudo systemctl start dsh-websudo systemctl stop dsh-websudo systemctl restart dsh-websystemctl status dsh-webjournalctl -u dsh-web -fHome Manager Web 服务
Section titled “Home Manager Web 服务”homeModules.default 还提供 services.dsh,为每个用户生成 systemd user
单元,让非 NixOS 用户获得等价的服务体验:
{ imports = [ inputs.deepseek-harness.homeModules.default ];
services.dsh = { enable = true; port = 3080; };}用户管理器中的单元名为 dsh-web.service,默认随 default.target 启动,
设置 services.dsh.autoStart = false 后需要手动启动。它默认使用 ~/.dsh
作为 DSH_HOME,与 CLI 共享已生成的 profile:
systemctl --user start dsh-websystemctl --user status dsh-webjournalctl --user -u dsh-web -f选项与 NixOS 服务一致,仅不含 user、group、openFirewall;
programs.dsh.profiles 的复用规则同样适用。
自定义 profile
Section titled “自定义 profile”在 NixOS 之外,用 withProfiles 从包中生成 profile:
pkgs.dsh.dsh.withProfiles { tui.bundles = [ pkgs.dsh.bundles.tui ];}每个 profile 的 bundles 既接受列表,也接受一个接收 bundle scope 的函数,
方便直接使用短名称:
pkgs.dsh.dsh.withProfiles { tui.bundles = b: with b; [ tui ];}这会生成 nix-tui profile,并清除默认 profile。如果需要让它成为默认,再对
结果执行 override:
(pkgs.dsh.dsh.withProfiles { tui.bundles = [ pkgs.dsh.bundles.tui ];}).override { defaultProfile = "nix-tui";}withAgentPresets 可以给包添加 preset 定义。多次调用会按 ID 合并;同一个
ID 以后一次定义为准。它可以放在 withProfiles 前面或后面:
( (pkgs.dsh.dsh.withAgentPresets { web-subagents = { source = "standard"; enableTools = [ "tool-subagent-codex" ]; }; }).withAgentPresets { code-subagents = { source = "ptc"; enableTools = [ "tool-subagent-claude-code" ]; }; }).withProfiles { web = { bundles = [ pkgs.dsh.bundles.subagent-codex ]; agentPreset = "web-subagents"; };}可选子代理提供程序
Section titled “可选子代理提供程序”Codex 和 Claude Code 以可选 Profile Bundle 提供。只需把允许该 profile 使用的 提供程序加入组合:
programs.dsh.profiles.web.bundles = with pkgs.dsh.bundles; [ web-app subagent-codex subagent-claude-code];安装提供程序 Bundle 只会把 Host provider 放进运行时。它不会启动 Codex 或
Claude Code,也不会自动开放对应工具。内置的 standard 和 ptc
Agent Preset 默认禁用 tool-subagent-codex 与
tool-subagent-claude-code。可以同时声明 preset 和 profile:
programs.dsh = { agentPresets.web-subagents = { source = "standard"; enableTools = [ "tool-subagent-codex" ]; };
profiles.web = { bundles = with pkgs.dsh.bundles; [ web-app subagent-codex ]; agentPreset = "web-subagents"; };};构建时会把指定的 preset 复制到
$DSH_HOME/.agent-presets/web-subagents,只从 enableTools 列出的行删除
disabled。managed profile 会在启动前同步副本;mutable profile 只生成一次。
安装 Bundle 不会开放工具;工具是否可用由 Agent Preset 单独决定。
这些 Bundle 使用 DeepSeek Harness 上游 workspace 固定的版本,不搜索
PATH。如需改用某个 Nixpkgs revision 或 overlay 单独打包的二进制,可以覆盖
Bundle input:
programs.dsh.profiles.web.bundles = with pkgs.dsh.bundles; [ (subagent-codex.override { codexPackage = pkgs.codex; }) (subagent-claude-code.override { claudeCodePackage = pkgs.claude-code; })];codexPackage 必须提供 bin/codex 和 bin/codex-code-mode-host;
claudeCodePackage 必须把 bin/claude 声明为 main program。属性名和可用性取决于
所选 Nixpkgs revision 或 overlay。覆盖后,可执行文件版本由覆盖包决定,不再跟随
上游 workspace lock。
Claude Agent SDK 使用 MIT 许可证,其内置 Claude Code payload 为非自由软件。
选择 subagent-claude-code 时需显式允许该包:
{ lib, ... }:{ nixpkgs.config.allowUnfreePredicate = package: lib.getName package == "dsh-subagent-claude-code";}自定义 Bundle 组合
Section titled “自定义 Bundle 组合”用 withBundles 追加 bundle。它既接受 bundle 包列表,也接受一个接收 bundle
scope 的函数,方便直接使用短名称:
pkgs.dsh.dsh.withBundles [ pkgs.dsh.bundles.tui pkgs.dsh.bundles.web-app]
pkgs.dsh.dsh.withBundles (b: with b; [ tui web-app])选中的 bundle 会追加到当前组合,也会追加到包里的每个 Nix 管理 profile, 因此生成的 profile 会随这次组合同步。所有 bundle 按列表顺序应用,后面的 bundle 会覆盖前面 bundle 的 Cordis 配置;基础层固定在最前面。
preset 通过 override 设置其他包输入。withAgentPresets 按 ID 合并 preset
定义,withBundles 用于添加 Bundle,withProfiles 会替换包里的 profiles,并清除
preset 的默认 profile。
Overlay
Section titled “Overlay”把 overlay 加入 Nixpkgs 后,使用 pkgs.dsh scope:
{ nixpkgs.overlays = [ inputs.deepseek-harness.overlays.default ]; environment.systemPackages = [ pkgs.dsh.dsh ];}该 scope 包含 pkgs.dsh.bundles.*、pkgs.dsh.presets.*、
pkgs.dsh.helpers.* 下的公开 helper,以及 pkgs.dsh.dsh-desktop 等包输出。
flake 的 packages 展开的是同一 scope,因此 nix build .#bundles.tui 和
nix run .#presets.web 仍然可用。
打包外部 Bundle
Section titled “打包外部 Bundle”对于本 flake 未收录的独立 bundle,使用 pkgs.dsh.helpers.buildBundle:
{ pkgs, ... }:let customBundle = pkgs.dsh.helpers.buildBundle (finalAttrs: { pname = "example-bundle"; version = "0.1.0";
src = pkgs.fetchFromGitHub { owner = "example"; repo = "example-bundle"; tag = "v${finalAttrs.version}"; hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; };
npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; npmBuildScript = "build"; linkKernelNodeModules = pkgs.dsh.dsh-kernel;
meta.description = "Example DSH bundle"; });in{ programs.dsh.profiles.tui.bundles = [ customBundle ];}该 helper 使用 buildNpmPackage 的标准 npmInstallHook:由
package.json.name 决定 package 目录,由 npm pack 文件清单决定运行时内容。
安装后的 manifest 必须声明 dsh.bundle.patch,对应文件也必须包含在 package
内容中。Builder 会校验结果,并生成组合阶段使用的 bundle manifest。只有非标准
package 布局才需要覆盖 installPhase。外部 pnpm monorepo 可使用
pkgs.dsh.helpers.buildBundle.fromPnpmWorkspace。