diff --git a/.gitignore b/.gitignore index 34b25fd..cb8818f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ env/ # Installer ISO live WiFi (SSID/PSK); see docs/server-installer-usb.md nixos/installer-wifi.nix + +# OpenClaw: Telegram user ID(s), not in public repo +nixos/home/danny/openclaw-allow-from.nix diff --git a/AGENTS.md b/AGENTS.md index ba41641..25605ff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,3 +51,7 @@ ssh -i ~/.ssh/id_ed25519_sunken_ship danny@sunken-ship 'hostname; ip addr' Rebuild on the server (flake is in `nixos/`): `ssh ... 'cd /etc/dotfiles/nixos && sudo nixos-rebuild switch --flake .#sunken-ship'`. The server has WiFi (see [docs/sunken-ship-wifi.md](docs/sunken-ship-wifi.md)); it remains reachable when ethernet is unplugged. +## OpenClaw (macOS) + +OpenClaw (AI assistant gateway, Telegram) is integrated in the dotfiles flake. Config: [nixos/home/danny/openclaw.nix](nixos/home/danny/openclaw.nix). Documents: [nixos/home/danny/openclaw-documents/](nixos/home/danny/openclaw-documents/). Secrets (bot token, gateway token, Telegram user ID) live in the config or `~/.secrets/`. One apply: `darwin-rebuild switch --flake .` from `nixos/`. + diff --git a/nixos/flake.lock b/nixos/flake.lock index 65ffc7c..2181c71 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -40,6 +40,24 @@ "inputs": { "systems": "systems" }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { "lastModified": 1681202837, "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", @@ -75,6 +93,27 @@ } }, "home-manager_2": { + "inputs": { + "nixpkgs": [ + "nix-openclaw", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767909183, + "narHash": "sha256-u/bcU0xePi5bgNoRsiqSIwaGBwDilKKFTz3g0hqOBAo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "cd6e96d56ed4b2a779ac73a1227e0bb1519b3509", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_3": { "inputs": { "nixpkgs": [ "zen-browser", @@ -116,10 +155,49 @@ "type": "github" } }, + "nix-openclaw": { + "inputs": { + "flake-utils": "flake-utils", + "home-manager": "home-manager_2", + "nix-steipete-tools": "nix-steipete-tools", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1773408280, + "narHash": "sha256-C37W5LyM+MA1J50zIkIZEAVjbtY2oeobAOYBXyyEpQU=", + "owner": "openclaw", + "repo": "nix-openclaw", + "rev": "b49874ed1e8e9f4e54c18580d05359d32c9eaa83", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-openclaw", + "type": "github" + } + }, + "nix-steipete-tools": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1773283011, + "narHash": "sha256-bRsRSdy2Wez2zUWZha6wYCcNwcUyYAOtP4/GOX4htLw=", + "owner": "openclaw", + "repo": "nix-steipete-tools", + "rev": "526067c585ede853ebd16172bb8a461ccc1df55c", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-steipete-tools", + "type": "github" + } + }, "nixos-wsl": { "inputs": { "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1771243519, @@ -137,6 +215,38 @@ } }, "nixpkgs": { + "locked": { + "lastModified": 1767364772, + "narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1767767207, + "narHash": "sha256-Mj3d3PfwltLmukFal5i3fFt27L6NiKXdBezC1EBuZs4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5912c1772a44e31bf1c63c0390b90501e5026886", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1770019141, "narHash": "sha256-VKS4ZLNx4PNrABoB0L8KUpc1fE7CLpQXQs985tGfaCU=", @@ -152,7 +262,7 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs_4": { "locked": { "lastModified": 1771177547, "narHash": "sha256-trTtk3WTOHz7hSw89xIIvahkgoFJYQ0G43IlqprFoMA=", @@ -168,7 +278,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_5": { "locked": { "lastModified": 1682134069, "narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=", @@ -187,8 +297,9 @@ "disko": "disko", "home-manager": "home-manager", "nix-darwin": "nix-darwin", + "nix-openclaw": "nix-openclaw", "nixos-wsl": "nixos-wsl", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_4", "vscode-server": "vscode-server", "zen-browser": "zen-browser" } @@ -208,10 +319,25 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "vscode-server": { "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_3" + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_5" }, "locked": { "lastModified": 1770124655, @@ -229,7 +355,7 @@ }, "zen-browser": { "inputs": { - "home-manager": "home-manager_2", + "home-manager": "home-manager_3", "nixpkgs": [ "nixpkgs" ] diff --git a/nixos/flake.nix b/nixos/flake.nix index 14a43c5..0b552bc 100644 --- a/nixos/flake.nix +++ b/nixos/flake.nix @@ -18,6 +18,8 @@ disko.url = "github:nix-community/disko"; disko.inputs.nixpkgs.follows = "nixpkgs"; + + nix-openclaw.url = "github:openclaw/nix-openclaw"; }; outputs = { @@ -29,6 +31,7 @@ home-manager, zen-browser, disko, + nix-openclaw, ... }: { nixosConfigurations = { @@ -89,14 +92,19 @@ # macOS (nix-darwin) configuration darwinConfigurations."Daniel-Macbook-Air" = nix-darwin.lib.darwinSystem { - specialArgs = { inherit zen-browser; }; + specialArgs = { inherit zen-browser nix-openclaw; }; modules = [ ./hosts/macos.nix ./fish.nix + # OpenClaw overlay so pkgs.openclaw etc. are available + ({ nix-openclaw, ... }: { + nixpkgs.overlays = [ nix-openclaw.overlays.default ]; + }) + # Home Manager on macOS home-manager.darwinModules.home-manager - ({ lib, zen-browser, ... }: { + ({ lib, zen-browser, nix-openclaw, ... }: { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; # Automatically backup files before home-manager overwrites them @@ -108,7 +116,11 @@ # Force an absolute path even if another module sets a bad value. home.username = "danny"; home.homeDirectory = lib.mkForce "/Users/danny"; - imports = [ ./home/danny/home.nix ]; + imports = [ + ./home/danny/home.nix + nix-openclaw.homeManagerModules.openclaw + ./home/danny/openclaw.nix + ]; }; }) ]; diff --git a/nixos/home/danny/openclaw-allow-from.nix.example b/nixos/home/danny/openclaw-allow-from.nix.example new file mode 100644 index 0000000..2794718 --- /dev/null +++ b/nixos/home/danny/openclaw-allow-from.nix.example @@ -0,0 +1,3 @@ +# Copy to openclaw-allow-from.nix (gitignored) and put your Telegram user ID(s) from @userinfobot. +# Example: +[ 00000000 ] diff --git a/nixos/home/danny/openclaw-documents/AGENTS.md b/nixos/home/danny/openclaw-documents/AGENTS.md new file mode 100644 index 0000000..913de0f --- /dev/null +++ b/nixos/home/danny/openclaw-documents/AGENTS.md @@ -0,0 +1,7 @@ +# Agent instructions + +Instructions for the AI assistant (OpenClaw) when acting on your behalf. + +- Prefer terminal and scripting for automation; use GUI only when necessary. +- Prefer tools and skills provided by enabled plugins; suggest enabling a plugin if a task needs it. +- Do not store secrets or tokens in the repo; use ~/.secrets/ or environment. diff --git a/nixos/home/danny/openclaw-documents/SOUL.md b/nixos/home/danny/openclaw-documents/SOUL.md new file mode 100644 index 0000000..8bd4ec5 --- /dev/null +++ b/nixos/home/danny/openclaw-documents/SOUL.md @@ -0,0 +1,7 @@ +# Soul + +Who the assistant is, its personality and boundaries. + +- Helpful and concise. +- Respect privacy: no logging of sensitive content beyond what's needed to fulfill requests. +- Prefer safe defaults; ask before destructive or irreversible actions. diff --git a/nixos/home/danny/openclaw-documents/TOOLS.md b/nixos/home/danny/openclaw-documents/TOOLS.md new file mode 100644 index 0000000..0f209fa --- /dev/null +++ b/nixos/home/danny/openclaw-documents/TOOLS.md @@ -0,0 +1,6 @@ +# Tools + +What the assistant can use and how. + +- CLI tools and skills come from enabled plugins (see `nixos/home/danny/openclaw.nix` → `programs.openclaw.instances.default.plugins`). +- Add plugins there and run `darwin-rebuild switch --flake .` from ~/dotfiles/nixos to install new tools and skills. diff --git a/nixos/home/danny/openclaw-gateway-wrapper.sh b/nixos/home/danny/openclaw-gateway-wrapper.sh new file mode 100644 index 0000000..94086e7 --- /dev/null +++ b/nixos/home/danny/openclaw-gateway-wrapper.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Load OPENCLAW_GATEWAY_TOKEN from a file and exec the real gateway. +# Install: token in ~/.secrets/openclaw-gateway-token (one line, no newline). +set -euo pipefail +TOKEN_FILE="${OPENCLAW_GATEWAY_TOKEN_FILE:-$HOME/.secrets/openclaw-gateway-token}" +if [ -f "$TOKEN_FILE" ]; then + export OPENCLAW_GATEWAY_TOKEN=$(cat "$TOKEN_FILE") +fi +exec "$@" diff --git a/nixos/home/danny/openclaw.nix b/nixos/home/danny/openclaw.nix new file mode 100644 index 0000000..753acb7 --- /dev/null +++ b/nixos/home/danny/openclaw.nix @@ -0,0 +1,57 @@ +# OpenClaw (AI assistant gateway) – Telegram, launchd, documents. +# Secrets (not in repo): +# ~/.secrets/telegram-bot-token +# ~/.secrets/openclaw-gateway-token (one line, gateway auth token) +# nixos/home/danny/openclaw-allow-from.nix (gitignored; copy from .example) +# After editing, run: darwin-rebuild switch --flake . (from ~/dotfiles/nixos) + +{ config, lib, ... }: + +let + # Telegram user IDs from gitignored file so we don't commit them + allowFromPath = ./. + "/openclaw-allow-from.nix"; + allowFrom = if builtins.pathExists allowFromPath then import allowFromPath else [ ]; +in +{ + programs.openclaw = { + enable = true; + documents = ./openclaw-documents; + + config = { }; + + instances.default = { + enable = true; + config = { + gateway = { + mode = "local"; + auth.token = ""; # loaded from ~/.secrets/openclaw-gateway-token via wrapper + }; + channels.telegram = { + tokenFile = "/Users/danny/.secrets/telegram-bot-token"; + allowFrom = allowFrom; + groups."*" = { requireMention = true; }; + }; + }; + plugins = [ + # e.g. { source = "github:openclaw/nix-steipete-tools?dir=tools/summarize"; } + ]; + }; + }; + + # Wrapper loads gateway token from file and execs the real gateway (keeps token out of store) + home.file.".local/bin/openclaw-gateway-wrapper" = { + source = ./openclaw-gateway-wrapper.sh; + executable = true; + }; + + # Prepend wrapper to launchd so OPENCLAW_GATEWAY_TOKEN is set from file at runtime + launchd.agents."com.steipete.openclaw.gateway" = lib.mkForce ( + (config.launchd.agents."com.steipete.openclaw.gateway" or { }) // { + config = (config.launchd.agents."com.steipete.openclaw.gateway".config or { }) // { + ProgramArguments = [ + (config.home.homeDirectory + "/.local/bin/openclaw-gateway-wrapper") + ] ++ (config.launchd.agents."com.steipete.openclaw.gateway".config.ProgramArguments or [ ]); + }; + } + ); +}