Golden paths + safer workspace + mac app Nix mode
- Add golden paths doc and link from README\n- Default workspaceDir to stateDir/workspace and pin agents.defaults.workspace when unset\n- Fix macOS app defaults domain and add openclaw.nixMode toggle
This commit is contained in:
parent
90ff91f3be
commit
2cb22671dc
12
README.md
12
README.md
@ -16,6 +16,8 @@ If you’re **not listed as a maintainer** (see [AGENTS.md#maintainers](AGENTS.m
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Golden Paths](#golden-paths)
|
||||
|
||||
- [Contributions (read this first)](#contributions-read-this-first)
|
||||
- [What You Get](#what-you-get)
|
||||
- [Requirements](#requirements)
|
||||
@ -31,6 +33,16 @@ If you’re **not listed as a maintainer** (see [AGENTS.md#maintainers](AGENTS.m
|
||||
|
||||
---
|
||||
|
||||
## Golden Paths
|
||||
|
||||
**There should be one — and preferably only one — obvious way to deploy.**
|
||||
|
||||
Pick a Golden Path, then follow the docs:
|
||||
|
||||
- [docs/golden-paths.md](docs/golden-paths.md)
|
||||
|
||||
---
|
||||
|
||||
## What You Get
|
||||
|
||||
```
|
||||
|
||||
63
docs/golden-paths.md
Normal file
63
docs/golden-paths.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Golden Paths
|
||||
|
||||
nix-openclaw is opinionated: **there should be one obvious way to deploy**.
|
||||
|
||||
A **Golden Path** is a supported topology + defaults + docs that:
|
||||
|
||||
- is secure by default
|
||||
- is reproducible (pinned inputs)
|
||||
- avoids manual state drift
|
||||
- has a clear boundary between **Nix-managed config** and **runtime state**
|
||||
|
||||
If your setup doesn’t match a Golden Path, it may still work — but you’re on your own.
|
||||
|
||||
## GP1: Single Mac (laptop or Mac mini)
|
||||
|
||||
**Who it’s for:** simplest “it just works” install; macOS-only capabilities available locally.
|
||||
|
||||
- Gateway: macOS (launchd)
|
||||
- OpenClaw.app: same machine
|
||||
- Networking: localhost (default)
|
||||
|
||||
## GP2: VPS Gateway + Mac Node (recommended for reliability)
|
||||
|
||||
**Who it’s for:** always-on Gateway (Telegram/Discord/etc) with macOS-only capabilities bridged from your Mac.
|
||||
|
||||
- Gateway: Linux VPS (systemd user service)
|
||||
- Node: OpenClaw.app on macOS (connects over WebSocket)
|
||||
- Networking: **Tailscale tailnet** (private), no public exposure
|
||||
|
||||
Key idea: the Gateway routes tool calls to the node when `host=node` is selected.
|
||||
|
||||
### Why Tailscale?
|
||||
|
||||
- private-by-default connectivity
|
||||
- MagicDNS stable hostnames (no IP chasing)
|
||||
- easy to lock down with ACLs
|
||||
|
||||
### Nix mode on macOS app
|
||||
|
||||
OpenClaw.app supports **Nix mode** (`OPENCLAW_NIX_MODE=1` or `defaults write ai.openclaw.mac openclaw.nixMode -bool true`).
|
||||
|
||||
In Nix mode the app disables auto-mutation flows and treats config as read-only.
|
||||
If something is missing for a fully declarative deployment, it’s a bug — fix it upstream.
|
||||
|
||||
## GP3: Laptop-only dev
|
||||
|
||||
**Who it’s for:** local experimentation.
|
||||
|
||||
- Gateway: macOS/Linux laptop
|
||||
- Node: optional
|
||||
- Expect downtime / sleep / network changes
|
||||
|
||||
## Runtime state vs pinned config
|
||||
|
||||
Pinned / Nix-managed:
|
||||
- `openclaw.json` (gateway config)
|
||||
- documents (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, etc.)
|
||||
- workspace path selection
|
||||
|
||||
Runtime:
|
||||
- sessions, caches
|
||||
- pairing state (devices/nodes)
|
||||
- exec approvals
|
||||
@ -73,7 +73,13 @@ let
|
||||
inst.package;
|
||||
pluginPackages = plugins.pluginPackagesFor name;
|
||||
pluginEnvAll = plugins.pluginEnvAllFor name;
|
||||
mergedConfig = stripNulls (lib.recursiveUpdate (lib.recursiveUpdate baseConfig cfg.config) inst.config);
|
||||
mergedConfig0 = stripNulls (lib.recursiveUpdate (lib.recursiveUpdate baseConfig cfg.config) inst.config);
|
||||
existingWorkspace = (((mergedConfig0.agents or {}).defaults or {}).workspace or null);
|
||||
mergedConfig =
|
||||
if (cfg.workspace.pinAgentDefaults or true) && existingWorkspace == null then
|
||||
lib.recursiveUpdate mergedConfig0 { agents = { defaults = { workspace = inst.workspaceDir; }; }; }
|
||||
else
|
||||
mergedConfig0;
|
||||
configJson = builtins.toJSON mergedConfig;
|
||||
configFile = pkgs.writeText "openclaw-${name}.json" configJson;
|
||||
gatewayWrapper = pkgs.writeShellScriptBin "openclaw-gateway-${name}" ''
|
||||
@ -108,6 +114,7 @@ let
|
||||
appDefaults = lib.optionalAttrs (pkgs.stdenv.hostPlatform.isDarwin && inst.appDefaults.enable) {
|
||||
attachExistingOnly = inst.appDefaults.attachExistingOnly;
|
||||
gatewayPort = inst.gatewayPort;
|
||||
nixMode = inst.appDefaults.nixMode;
|
||||
};
|
||||
|
||||
appInstall = if !(pkgs.stdenv.hostPlatform.isDarwin && inst.app.install.enable && appPackage != null) then
|
||||
@ -250,8 +257,10 @@ in {
|
||||
|
||||
home.activation.openclawAppDefaults = lib.mkIf (pkgs.stdenv.hostPlatform.isDarwin && appDefaults != {}) (
|
||||
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
/usr/bin/defaults write com.steipete.Openclaw openclaw.gateway.attachExistingOnly -bool ${lib.boolToString (appDefaults.attachExistingOnly or true)}
|
||||
/usr/bin/defaults write com.steipete.Openclaw gatewayPort -int ${toString (appDefaults.gatewayPort or 18789)}
|
||||
# Nix mode + app defaults (OpenClaw.app)
|
||||
/usr/bin/defaults write ai.openclaw.mac openclaw.nixMode -bool ${lib.boolToString (appDefaults.nixMode or true)}
|
||||
/usr/bin/defaults write ai.openclaw.mac openclaw.gateway.attachExistingOnly -bool ${lib.boolToString (appDefaults.attachExistingOnly or true)}
|
||||
/usr/bin/defaults write ai.openclaw.mac gatewayPort -int ${toString (appDefaults.gatewayPort or 18789)}
|
||||
''
|
||||
);
|
||||
|
||||
|
||||
@ -137,6 +137,12 @@
|
||||
default = true;
|
||||
description = "Attach existing gateway only (macOS).";
|
||||
};
|
||||
|
||||
nixMode = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable OpenClaw Nix mode in the macOS app via defaults (openclaw.nixMode).";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -84,8 +84,16 @@ in {
|
||||
|
||||
workspaceDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${openclawLib.homeDir}/.openclaw/workspace";
|
||||
description = "Workspace directory for OpenClaw agent skills.";
|
||||
default = "${config.programs.openclaw.stateDir}/workspace";
|
||||
description = "Workspace directory for Openclaw agent skills (defaults to stateDir/workspace).";
|
||||
};
|
||||
|
||||
workspace = {
|
||||
pinAgentDefaults = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Pin agents.defaults.workspace to each instance workspaceDir when unset (prevents falling back to template ~/.openclaw/workspace).";
|
||||
};
|
||||
};
|
||||
|
||||
documents = lib.mkOption {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user