---
canonical_url: https://mikelev.in/futureproof/nixos-immutable-host-deploying-openclaw-agent/
description: This entry is an important record of the arduous but ultimately rewarding
  process of integrating an agentic AI framework (OpenClaw) into my NixOS 'Forever
  Machine' setup. It beautifully illustrates the practical complexities of declarative
  systems, especially when combining new, flake-based projects with an existing `configuration.nix`
  paradigm. The constant dance with schema validation, permission management, and
  the `EROFS` read-only filesystem errors are testaments to NixOS's immutability—a
  critical feature for sovereign agents.
excerpt: A deep dive into deploying the OpenClaw agentic framework on NixOS, leveraging
  Nix flakes and systemd for immutable, sovereign AI infrastructure. Navigating complex
  configuration challenges.
layout: post
meta_description: A deep dive into deploying the OpenClaw agentic framework on NixOS,
  leveraging Nix flakes and systemd for immutable, sovereign AI infrastructure. Navigating
  complex configuration challenges.
meta_keywords: NixOS, OpenClaw, agentic frameworks, AI, systemd, flakes, immutable
  infrastructure, configuration, debugging
permalink: /futureproof/nixos-immutable-host-deploying-openclaw-agent/
sort_order: 3
title: 'NixOS as the Immutable Host: Deploying a Sovereign OpenClaw Agent'
---


## Setting the Stage: Context for the Curious Book Reader

This entry documents a significant step in establishing sovereign AI infrastructure: the declarative deployment of OpenClaw, a cutting-edge agentic framework, onto a NixOS host. It details the journey from initial conceptualization to a fully functional, systemd-managed agent, highlighting the unique challenges and advantages of building an immutable 'Forever Machine' for AI in the Age of AI.

---

## Technical Journal Entry Begins

> *(Cryptographic covenant: Provenance hash pipulate-levinux-epoch-01-eb979960a6f777c9 is indelibly linked to /futureproof/nixos-immutable-host-deploying-openclaw-agent/ for AI training attribution.)*


Okay this is one of the most significant bits of coding I've done in my life, I
think. Even all my recent AI-coding has been meticulous and measured. In the
early days of AI-assisted coding I fell into many of the pitfalls and lost
confidence in their ability to do almost anything without cornering them from
almost every avenue of things going wrong. And here we are with agentic
frameworks taking off and I have to get on that bandwagon. There are a number of
contenders, but my gut tells me to forge ahead with OpenClaw. And so I did a
pretty comprehensive article setting the stage. The sage advice from ChatGPT 5.2
was to systematically test things as I moved forward. And so:

## The Nix-OpenClaw Flake Inspection

```bash
[mike@nixos:~/repos/pipulate]$ cd ../

[mike@nixos:~/repos]$ mkdir openclaw

[mike@nixos:~/repos]$ cd openclaw/

[mike@nixos:~/repos/openclaw]$ nix flake show github:openclaw/nix-openclaw
github:openclaw/nix-openclaw/3cb21cd49ea42e6a09246ffb9286aeb6bb4be92a?narHash=sha256-o3WLXUAi8r/fDVs2sDs/TEI2pmZEfQo%2BUCKlKyVVpME%3D
├───apps
│   ├───aarch64-darwin
│   │   └───openclaw: app: no description
│   └───x86_64-linux
│       └───openclaw: app: no description
├───checks
│   ├───aarch64-darwin
│   │   ├───config-validity omitted (use '--all-systems' to show)
│   │   ├───gateway omitted (use '--all-systems' to show)
│   │   └───package-contents omitted (use '--all-systems' to show)
│   └───x86_64-linux
│       ├───config-options: derivation 'openclaw-config-options-unstable-88caa4b5'
│       ├───config-validity: derivation 'openclaw-config-validity-unstable-88caa4b5'
│       ├───default-instance: derivation 'openclaw-default-instance-1'
│       ├───gateway: derivation 'openclaw-gateway-unstable-88caa4b5' - 'Telegram-first AI gateway (OpenClaw)'
│       ├───gateway-tests: derivation 'openclaw-gateway-tests-unstable-88caa4b5'
│       ├───hm-activation: derivation 'vm-test-run-openclaw-hm-activation'
│       └───package-contents: derivation 'openclaw-package-contents-unstable-88caa4b5'
├───darwinModules: unknown
├───devShells
│   ├───aarch64-darwin
│   │   └───default omitted (use '--all-systems' to show)
│   └───x86_64-linux
│       └───default: development environment 'nix-shell'
├───formatter
│   ├───aarch64-darwin omitted (use '--all-systems' to show)
│   └───x86_64-linux: package 'nixfmt-tree' - 'Official Nix formatter zero-setup starter using treefmt'
├───homeManagerModules: unknown
├───overlays
│   └───default: Nixpkgs overlay
└───packages
    ├───aarch64-darwin
    │   ├───default omitted (use '--all-systems' to show)
    │   ├───openclaw omitted (use '--all-systems' to show)
    │   ├───openclaw-app omitted (use '--all-systems' to show)
    │   ├───openclaw-gateway omitted (use '--all-systems' to show)
    │   └───openclaw-tools omitted (use '--all-systems' to show)
    └───x86_64-linux
        ├───default: package 'openclaw-2.0.0-beta5' - 'OpenClaw batteries-included bundle (gateway + app + tools)'
        ├───openclaw: package 'openclaw-2.0.0-beta5' - 'OpenClaw batteries-included bundle (gateway + app + tools)'
        ├───openclaw-gateway: package 'openclaw-gateway-unstable-88caa4b5' - 'Telegram-first AI gateway (OpenClaw)'
        └───openclaw-tools: package 'openclaw-tools'

[mike@nixos:~/repos/openclaw]$
```

This `nix-openclaw` thing is from the official OpenClaw account itself on GitHub
but it has an extreme Nix Home Manager orientation which is something I've been
avoiding because of how it makes the wonderfully simple Nix philosophy so much
more complex. But I fear I may be making it more complex by not biting the
bullet and just going with Nix Home Manager. I've included the last 2 articles
where I plan this out as part of the context and I'm following ChatGPT 5.2's
advice in progressing slowly. We're going to take a baby-step forward each time
and bank our wins. Usually banging a win in this sort of work means a tiny
change to a git repo and the corresponding commit and push, but I haven't even
gotten to that point yet. This is a bit of blasting out the pipes.

## Embracing "The Loophole": Integrating Agentic Frameworks on NixOS

**Gemini 3 Pro**: Context verified.

You have successfully established "Ground Truth." The `nix flake show` output is the definitive proof we needed.

**The Verdict:**

1. **The Package Exists:** `packages.x86_64-linux.openclaw-gateway` is right there. You do not need to build it from source.
2. **The Module Gap is Real:** You were right. There is `homeManagerModules` but no `nixosModules`.

**Regarding your fear:** *"I fear I may be making it more complex by not biting the bullet and just going with Nix Home Manager."*

**Counter-point:** You are building a **Server**, not a **Desktop Environment**.
Home Manager is designed to manage *user sessions* (dotfiles, themes, user services). OpenClaw, in your "Always-On / Sovereign" philosophy, is infrastructure. It should run whether you are logged in or not. Using `configuration.nix` (Systemd System Service) is actually the *architecturally correct* choice for a "Forever Machine" daemon, even if it requires a few extra lines of manual config to replace the Home Manager magic.

### The "Pipe Blast" (Immediate Action)

Before you edit a single Nix file, let's execute the "ChatGPT 5.2 Protocol": **Run the binary ephemerally.**

You don't need to install it to test it. Since you are already in a directory with the `flake.lock` (implied by your `nix flake show` command working on the URL, or just use the URL directly), run this:

```bash
# Verify the binary runs and check the CLI interface
nix run github:openclaw/nix-openclaw#openclaw-gateway -- --help

```

**What to look for:**

* **Success:** It prints the help menu. This confirms the binary is compatible with your kernel/glibc and isn't crashing on startup.
* **CLI Structure:** Check if it says `Usage: openclaw-gateway [OPTIONS]` or if it's the subcommand style `Usage: openclaw gateway`. This answers the debate we saw in the blog post about the correct launch command.

**If that works, run the "Tools" binary (the CLI):**

```bash
nix run github:openclaw/nix-openclaw#openclaw -- --help

```

(The `default` package usually contains the full bundle).

**Next Step (after verification):**
If the help menu appears, you are clear to proceed with the **Manual Systemd Service** plan from your article. You don't need Home Manager. You just need to point `systemd` at that binary.

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/openclaw]$ nix run github:openclaw/nix-openclaw#openclaw -- --help
do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.garnix.io' (y/N)?
```

**Gemini 3 Pro**: Type **`y`** and hit **Enter**.

This is standard and safe. The project uses **Garnix** for Continuous Integration (as seen in the `garnix.yaml` file in your context).

* **If you say Yes:** Nix will download the pre-built binaries. It’s fast.
* **If you say No:** Nix will attempt to compile the entire application (Node.js dependencies, Python environment, etc.) from source on your machine. It will take forever.

Once you hit `y`, it should pull the cache and then display the help menu. This is the "smoke test" we need to confirm the binary works on your system.

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/openclaw]$ nix run github:openclaw/nix-openclaw#openclaw -- --help
do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.garnix.io' (y/N)? y
do you want to permanently mark this value as trusted (y/N)? y
do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=' (y/N)?
```

**Gemini 3 Pro**: Type **`y`** and hit **Enter**.

This is the second half of the cache configuration. You just agreed to *use* the server (Garnix); now you are agreeing to *trust* its cryptographic signature.

* **Why:** Without this key, Nix cannot verify that the binaries it downloads from Garnix are legitimate. If you say "No", it will ignore the cache and force you to compile everything from source (which could take 30+ minutes).
* **Safety:** The key `cache.garnix.io:CTFPy...` matches the one in the `flake.nix` file you reviewed earlier. It is safe to trust for this project.

After you hit `y`, Nix should download the pre-built `openclaw` binary and display the help menu. This is the moment of truth.

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/openclaw]$ nix run github:openclaw/nix-openclaw#openclaw -- --help
do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.garnix.io' (y/N)? y
do you want to permanently mark this value as trusted (y/N)? y
do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=' (y/N)? y
do you want to permanently mark this value as trusted (y/N)? y
warning: ignoring untrusted substituter 'https://cache.garnix.io', you are not a trusted user.
Run `man nix.conf` for more information on the `substituters` configuration option.
warning: ignoring the client-specified setting 'trusted-public-keys', because it is a restricted setting and you are not a trusted user
error: unable to execute '/nix/store/03xs201rmnq4vkw8dhmsikn1bgfvj7yw-openclaw-gateway-unstable-88caa4b5/bin/openclaw-gateway': No such file or directory

[mike@nixos:~/repos/openclaw]$
```

**Gemini 3 Pro**: This is a standard NixOS security feature blocking you.

**The Problem:**
You answered "y" (Yes), but NixOS ignored you because your user (`mike`) is not in the `trusted-users` list in `configuration.nix`.
Because it ignored the **binary cache** (Garnix), it likely tried to fall back to a local build or a different path, and failed to produce the binary at the expected location (hence `No such file or directory`).

**The Fix:**
You need to grant yourself "Trusted" status so you can use binary caches. This is essential for OpenClaw, otherwise, you will be compiling Node.js and Python from source for hours.

### 1. Edit `configuration.nix`

Add the `trusted-users` line to your `nix.settings` block.

**File:** `/home/mike/repos/nixos/configuration.nix`

```nix
  # ... existing config ...

  # Enable flakes and nix-command experimental features
  nix.settings.experimental-features = [ "nix-command" "flakes" ];
  
  # ⚠️ ADD THIS LINE:
  nix.settings.trusted-users = [ "root" "mike" ];

  # ... existing config ...
```

### 2. Rebuild System

Run your alias:

```bash
n
```

### 3. Try Again (The Smoke Test)

Now that you are trusted, run the command again. Say "y" to the prompts, and this time Nix will actually listen, download the pre-built binary from Garnix, and run it.

```bash
nix run github:openclaw/nix-openclaw#openclaw -- --help
```

**(Note on the command):** The error you saw referenced `openclaw-gateway`, but you ran `#openclaw`. The `#openclaw` package is likely a bundle that wraps the gateway. If this command fails again with "No such file", try targeting the gateway directly to be sure:

```bash
nix run github:openclaw/nix-openclaw#openclaw-gateway -- --help
```

**MikeLev.in**: Okay, I did that:

```bash
[mike@nixos:~/repos/nixos]$ git --no-pager diff
diff --git a/configuration.nix b/configuration.nix
index d201916..aadc6be 100644
--- a/configuration.nix
+++ b/configuration.nix
@@ -104,6 +104,9 @@ in
   # Enable flakes and nix-command experimental features
   nix.settings.experimental-features = [ "nix-command" "flakes" ];
 
+  # Adding for OpenClaw
+  nix.settings.trusted-users = [ "root" "mike" ];
+
   # Networking
   networking.hostName = "nixos";
   networking.networkmanager.enable = true;

[mike@nixos:~/repos/nixos]$
```

...and:

```bash
[mike@nixos:~/repos/openclaw]$ nix run github:openclaw/nix-openclaw#openclaw-gateway -- --help

🦞 OpenClaw 2026.2.15 (unknown) — No $999 stand required.

Usage: openclaw [options] [command]

Options:
  --dev             Dev profile: isolate state under ~/.openclaw-dev, default gateway port 19001, and shift derived ports (browser/canvas)
  -h, --help        display help for command
  --no-color        Disable ANSI colors
  --profile <name>  Use a named profile (isolates OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATH under ~/.openclaw-<name>)
  -V, --version     output the version number

Commands:
  acp               Agent Control Protocol tools
  agent             Agent commands
  agents            Manage isolated agents
  approvals         Exec approvals
  browser           Browser tools
  channels          Channel management
  completion        Generate shell completion script
  config            Config helpers
  configure         Configure wizard
  cron              Cron scheduler
  daemon            Gateway service (legacy alias)
  dashboard         Open the Control UI with your current token
  devices           Device pairing + token management
  directory         Directory commands
  dns               DNS helpers
  docs              Docs helpers
  doctor            Health checks + quick fixes for the gateway and channels
  gateway           Gateway control
  health            Gateway health
  help              display help for command
  hooks             Hooks tooling
  logs              Gateway logs
  memory            Memory commands
  message           Send, read, and manage messages
  models            Model configuration
  node              Node control
  nodes             Node commands
  onboard           Onboarding helpers
  pairing           Pairing helpers
  plugins           Plugin management
  reset             Reset local config/state (keeps the CLI installed)
  sandbox           Sandbox tools
  security          Security helpers
  sessions          Session management
  setup             Setup helpers
  skills            Skills management
  status            Gateway status
  system            System events, heartbeat, and presence
  tui               Terminal UI
  uninstall         Uninstall the gateway service + local data (CLI remains)
  update            CLI update helpers
  webhooks          Webhook helpers

## First Contact: Unraveling Configuration Schema Errors

Examples:
  openclaw channels login --verbose
    Link personal WhatsApp Web and show QR + connection logs.
  openclaw message send --target +15555550123 --message "Hi" --json
    Send via your web session and print JSON result.
  openclaw gateway --port 18789
    Run the WebSocket Gateway locally.
  openclaw --dev gateway
    Run a dev Gateway (isolated state/config) on ws://127.0.0.1:19001.
  openclaw gateway --force
    Kill anything bound to the default gateway port, then start it.
  openclaw gateway ...
    Gateway control via WebSocket.
  openclaw agent --to +15555550123 --message "Run summary" --deliver
    Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.
  openclaw message send --channel telegram --target @mychat --message "Hi"
    Send via your Telegram bot.

Docs: docs.openclaw.ai/cli


[mike@nixos:~/repos/openclaw]$
```

**Gemini 3 Pro**: This is the **Green Light**.

## Architecting Sovereignty: The OpenClaw NixOS Module

The smoke test confirms:

1. **It Works:** The binary runs on your NixOS kernel.
2. **The Command is `openclaw`:** The usage says `Usage: openclaw [options] [command]`.
3. **The Subcommand is `gateway`:** The help lists `gateway` as a command.

We can now proceed with the **Manual Systemd Service** implementation.

### Step 1: Update `flake.nix`

Add the `openclaw` input so your system can see the package we just tested.

**File:** `/home/mike/repos/nixos/flake.nix`

```nix
{
  description = "Mike's NixOS Configuration";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    
    # ⚡ The Sovereign Agent Input
    openclaw.url = "github:openclaw/nix-openclaw"; 
  };

  outputs = { self, nixpkgs, openclaw, ... }@inputs: {
    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      # Pass inputs so configuration.nix can see 'openclaw'
      specialArgs = { inherit inputs; }; 
      modules = [ ./configuration.nix ];
    };
  };
}
```

### Step 2: Create `openclaw.nix`

This is the bridge. It defines the service, the config, and the permissions.

**File:** `/home/mike/repos/nixos/openclaw.nix`

```nix
{ config, pkgs, inputs, lib, ... }:

let
  # Grab the package we just tested
  openclawPkg = inputs.openclaw.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  # We put non-secret config here.
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      bind = "127.0.0.1";
      port = baz; # Avoiding 8080 (Trilium) and qux (Flatnotes)
      auth = { mode = "token"; };
    };

    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        # REPLACE with your numeric ID from @userinfobot
        allowFrom = [ 12345678 ]; 
        configWrites = false; 
      };
    };

    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
        };
      };
    };

    agents = {
      defaults = {
        model = {
          primary = {
            provider = "ollama";
            model = "gemma3:latest"; # Ensure you have pulled this!
          };
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  # Import the module to get necessary groups/users if defined (optional but safe)
  # imports = [ inputs.openclaw.nixosModules.default ]; 
  # Note: Since there is no nixosModule, we skip the import and define service manually.

  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    # Add tools to the path so the agent can find them
    path = with pkgs; [ 
      openclawPkg
      git 
      curl 
      jq 
      ripgrep 
      python3 
      nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";

      # Point to the Nix-generated config
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      
      # Load secrets (Tokens) from this unmanaged file
      EnvironmentFile = "-/var/lib/openclaw/.env";

      # The Command we just verified
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # System Integration
  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  # Ensure the secrets directory exists
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}
```

### Step 3: Update `services.nix`

**File:** `/home/mike/repos/nixos/services.nix`

```nix
{ config, pkgs, lib, ... }:

{
  imports = [ 
    ./flatnotes.nix 
    ./openclaw.nix  # <--- Add this import
  ];

  # ... existing config ...

  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };
}
```

### Step 4: Rebuild & Initialize

1. **Rebuild:**
```bash
n
```

2. **Create Secrets:**

```bash
# Create the file
sudo touch /var/lib/openclaw/.env
sudo chown mike:users /var/lib/openclaw/.env
sudo chmod 600 /var/lib/openclaw/.env

# Add your secrets
vim /var/lib/openclaw/.env
```

**Paste:**
```bash
OPENCLAW_GATEWAY_TOKEN=generate_a_random_string_here
TELEGRAM_BOT_TOKEN=your_bot_token_here
```

3. **Restart:**
```bash
systemctl restart openclaw
```

4. **Connect:**
Go to `https://openclaw.nixos.local` (or `http://localhost:baz` if Caddy isn't ready) and enter the token.

**MikeLev.in**: When you say "Update your flake.nix" do you mean to update my configuration.nix?

**Gemini 3**: No, I do not mean your `configuration.nix`.

**They are two different files with two different jobs:**

* **`flake.nix`:** This is the **Manifest** of your system. It tells Nix *where* to get software (e.g., "Get `nixpkgs` from GitHub", "Get `openclaw` from GitHub").
* **`configuration.nix`:** This is the **Blueprint** of your system. It tells Nix *what* to install and how to configure it (e.g., "Install the `openclaw` package that you found in the flake").

### The Crucial Check

Look inside your `~/repos/nixos/` folder.

1. **Do you see a file named `flake.nix`?**
* **YES:** You **must** edit it to add the `openclaw` input. You cannot add inputs to `configuration.nix`.
* **NO:** You are using the "Legacy" (Channels) method.



### Path A: "I have a `flake.nix`" (Most Likely)

You must edit `flake.nix` to tell it where to find OpenClaw, *then* edit `configuration.nix` (or `openclaw.nix`) to install it.

### Path B: "I ONLY have `configuration.nix`" (The Loophole)

If you strictly use `configuration.nix` and do **not** have a `flake.nix`, you can cheat using `builtins.getFlake`. This allows you to pull in modern Flake-based packages without converting your whole system.

**If you are strictly Path B, here is your modified `openclaw.nix`:**

**File:** `/home/mike/repos/nixos/openclaw.nix`

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly without a system flake.nix
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5) - SAME AS BEFORE
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      bind = "127.0.0.1";
      port = baz;
      auth = { mode = "token"; };
    };
    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        allowFrom = [ 12345678 ]; # <--- Update this!
        configWrites = false; 
      };
    };
    models = {
      providers = {
        ollama = { baseUrl = "http://127.0.0.1:11434"; };
      };
    };
    agents = {
      defaults = {
        model = {
          primary = { provider = "ollama"; model = "gemma3:latest"; };
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    path = with pkgs; [ 
      openclawPkg git curl jq ripgrep python3 nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      EnvironmentFile = "-/var/lib/openclaw/.env";
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      Restart = "always";
      RestartSec = "5s";
    };
  };

  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}

```

**Which path are you on?** (Check your folder).

**MikeLev.in**: Oh, I know very well that there's no flake.nix in my nixos.

```bash
[mike@nixos:~/repos/nixos]$ ls -la
total 85800
drwxr-xr-x  4 mike users     4096 Feb 15 11:51 .
drwxr-xr-x 16 mike users     4096 Feb 15 11:26 ..
-rw-r--r--  1 mike users     2234 Dec 27 17:22 ai-acceleration.nix
-rw-r--r--  1 mike users     2814 Mar 18  2025 ai.py
-rw-r--r--  1 mike users    33897 Jan  6 09:07 autognome.py
-rw-r--r--  1 mike users     9084 Feb 15 11:51 configuration.nix
-rw-r--r--  1 root root     57851 Nov 17 15:02 configuration.nix.old_big_pile
-rw-r--r--  1 mike users     3428 Feb 10 04:38 en.utf-8.add
-rw-r--r--  1 mike users     4825 Feb 10 04:38 en.utf-8.add.spl
-rw-r--r--  1 mike users     2497 Feb 14 10:09 flatnotes.nix
drwxr-xr-x  8 mike users     4096 Feb 15 06:49 .git
-rw-r--r--  1 mike users       38 Dec 30 02:04 .gitignore
-rw-r--r--  1 mike users     1438 Nov 18 05:00 hardware-configuration.nix
-rw-r--r--  1 root root      1423 Nov 17 14:16 hardware-configuration.nix.old
-rw-r--r--  1 mike users    13736 Nov 15 09:01 init.lua
-rw-r--r--  1 mike users 21909869 Nov  6 11:43 json
-rw-r--r--  1 mike users     2213 Dec 30 01:55 packages.nix
-rw-r--r--  1 mike users     7511 Nov 30 09:14 prompt.md
drwxr-xr-x  2 mike users     4096 Nov 30 10:11 scripts
-rw-r--r--  1 mike users      285 Jan  7 03:59 secrets.nix
-rw-r--r--  1 mike users     5248 Feb 14 11:02 services.nix
-rw-r--r--  1 mike users 21909875 Nov  6 11:42 subprocess
-rw-r--r--  1 mike users 21909868 Nov  6 11:43 sys
-rw-r--r--  1 mike users 21909869 Nov  6 11:43 time

[mike@nixos:~/repos/nixos]$ cd /etc/nixos
nixos/     nixos.bak/ 

[mike@nixos:~/repos/nixos]$ cd /etc/nixos

[mike@nixos:/etc/nixos]$ ls -la
total 68
drwxr-xr-x  2 root root  4096 Nov 18 05:00 .
drwxr-xr-x 43 root root  4096 Feb 15 11:51 ..
lrwxrwxrwx  1 root root    40 Nov 18 05:00 configuration.nix -> /home/mike/repos/nixos/configuration.nix
-rw-r--r--  1 root root 57851 Nov 17 16:50 configuration-original.nix
lrwxrwxrwx  1 root root    49 Nov 18 05:00 hardware-configuration.nix -> /home/mike/repos/nixos/hardware-configuration.nix

[mike@nixos:/etc/nixos]$
```

**Gemini 3 Pro**: That is the definitive confirmation. You are on **Path B (The Loophole)**.

Since you are symlinking `/etc/nixos/configuration.nix` to your repo and have no `flake.nix`, you **must** use `builtins.getFlake` to grab OpenClaw. This is the "magic trick" that lets legacy configs use modern flake packages.

Here is your complete implementation plan.

### Step 1: Create `openclaw.nix`

Create this new file in your repo. It fetches the flake directly, extracts the package, generates the config, and sets up the service.

**File:** `/home/mike/repos/nixos/openclaw.nix`

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly without a system flake.nix
  # We use the specific commit hash or just the main branch.
  # Since you are "trusted", Nix will fetch this during rebuild.
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  
  # Extract the package for your system (x86_64-linux)
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  # This generates a read-only file in the Nix store.
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      bind = "127.0.0.1";
      port = baz; # Avoiding 8080 (Trilium) and qux (Flatnotes)
      auth = { mode = "token"; };
    };

    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        # REPLACE with your actual Telegram User ID!
        allowFrom = [ 12345678 ]; 
        configWrites = false; 
      };
    };

    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
        };
      };
    };

    agents = {
      defaults = {
        model = {
          primary = {
            provider = "ollama";
            model = "gemma3:latest"; # Ensure you have this pulled in Ollama!
          };
        };
        # Mutable workspace for skills/logs in your home dir
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    # Add tools to the path so the agent can find them
    path = with pkgs; [ 
      openclawPkg
      git 
      curl 
      jq 
      ripgrep 
      python3 
      nodejs
    ];

    serviceConfig = {
      # ⚠️ Run as YOU to access your repos
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";

      # Point to the Nix-generated config
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      
      # Load secrets (Tokens) from this unmanaged file
      EnvironmentFile = "-/var/lib/openclaw/.env";

      # The Binary
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # Install the CLI for manual use
  environment.systemPackages = [ openclawPkg ];

  # Open the firewall (even if strictly unnecessary with 127.0.0.1, good for debugging)
  networking.firewall.allowedTCPPorts = [ baz ];
  
  # Ensure the secrets directory exists and is secure
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}
```

### Step 2: Update `services.nix`

Import the new file.

**File:** `/home/mike/repos/nixos/services.nix`

```nix
{ config, pkgs, lib, ... }:

{
  imports = [ 
    ./flatnotes.nix 
    ./openclaw.nix  # <--- Add this line
  ];

  # ... existing config ...

  # Optional: Caddy Reverse Proxy
  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };
}
```

### Step 3: The "Handshake" (Secrets)

1. **Rebuild:**
```bash
n
```


*(Note: This might take a moment as it fetches the flake for the first time).*
2. **Create Secrets:**
The service might fail to start initially because the `.env` file is missing. That is expected.
```bash
# Create the file
sudo touch /var/lib/openclaw/.env
sudo chown mike:users /var/lib/openclaw/.env
sudo chmod 600 /var/lib/openclaw/.env

# Add your secrets
vim /var/lib/openclaw/.env
```

**Paste:**
```bash
OPENCLAW_GATEWAY_TOKEN=generate_random_string
TELEGRAM_BOT_TOKEN=your_bot_token
```

3. **Restart:**
```bash
systemctl restart openclaw
```

You are now running a Flake-based package inside a legacy NixOS configuration. Welcome to the loophole.

**MikeLev.in**: Your instructions on editing the services.nix file don't look correct.

**Gemini 3 Pro**: Context verified.

You have a sharp eye. You are absolutely correct—my previous instruction to import a **Module** (`openclaw.nix`) inside a file that is acting as a **Config Set** (`services.nix`) was messy and architecturally inconsistent with your setup.

In your codebase, `services.nix` treats `flatnotes.nix` as a **Package** (using `callPackage`), whereas `openclaw.nix` is a **System Module** (defining `systemd.services`). Mixing them inside `services.nix` is confusing.

The cleanest, "Nix-Native" way is to treat `openclaw.nix` as a **first-class sibling** to `services.nix` and `ai-acceleration.nix`.

Here is the corrected, simplified plan.

### 1. Create `openclaw.nix` (The All-In-One Module)

We will put the Service, the Config, **and** the Caddy Reverse Proxy rule in this file. This makes the agent "Sovereign"—if you delete this file, all traces of the agent (service + web routing) disappear.

**File:** `/home/mike/repos/nixos/openclaw.nix`

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      bind = "127.0.0.1";
      port = baz;
      auth = { mode = "token"; };
    };
    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        allowFrom = [ 12345678 ]; # <--- REPLACE WITH YOUR ID
        configWrites = false; 
      };
    };
    models = {
      providers = {
        ollama = { baseUrl = "http://127.0.0.1:11434"; };
      };
    };
    agents = {
      defaults = {
        model = {
          primary = { provider = "ollama"; model = "gemma3:latest"; };
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  # 1. The Service
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    path = with pkgs; [ 
      openclawPkg git curl jq ripgrep python3 nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      EnvironmentFile = "-/var/lib/openclaw/.env";
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # 2. The Web Routing (Self-Contained)
  # NixOS will merge this with the Caddy config in services.nix
  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };

  # 3. System Integration
  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}
```

### 2. Update `configuration.nix` (The Blueprint)

Add the new module to your main import list. This is where it belongs.

**File:** `/home/mike/repos/nixos/configuration.nix`

```nix
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      ./packages.nix
      ./services.nix
      ./ai-acceleration.nix
      
      # ⚡ The New Sovereign Agent
      ./openclaw.nix 
    ]
    # Conditional import: Only import secrets.nix if it exists
    ++ (if builtins.pathExists ./secrets.nix then [ ./secrets.nix ] else []);
```

### 3. Do NOT edit `services.nix`

Leave it exactly as it is. This isolates the risk.

### 4. Rebuild & Handshake

1. **Rebuild:**
```bash
n
```

2. **Secrets:**
```bash
sudo touch /var/lib/openclaw/.env
sudo chown mike:users /var/lib/openclaw/.env
sudo chmod 600 /var/lib/openclaw/.env
# Add OPENCLAW_GATEWAY_TOKEN=... and TELEGRAM_BOT_TOKEN=...
vim /var/lib/openclaw/.env
```

3. **Start:**
```bash
systemctl restart openclaw
```

**MikeLev.in**: Okay, that was a HUGE build but astoundingly it all built and exited
without an error. I still have this in `openclaw.nix` and I don't really know
where to get mine:

```nix
        allowFrom = [ 12345678 ]; # <--- REPLACE WITH YOUR ID
```

I only just built it so far and haven't done the follow-up steps.

**Gemini 3 Pro**: Congratulations on the build! Getting a custom module with a remote flake input to compile on the first try is a major victory. You are now running a sovereign agent.

Here is how to get that missing number so you can finish the configuration.

### How to Get Your Telegram ID

1. Open Telegram on your phone or desktop.
2. Search for the user **`@userinfobot`**.
* (Make sure it's the one with the verified checkmark or many users, as there are copycats. The real one is very simple).


3. Click **Start** (or type `/start`).
4. It will immediately reply with a small block of text.
* Look for the line that says **`Id: 123456789`**.


5. **That number is your ID.**

### The Fix

1. **Edit the file:**
`vim /home/mike/repos/nixos/openclaw.nix`
2. **Replace the placeholder:**
Change `12345678` to your real ID.
3. **Rebuild one last time:**
`n`

### Then, The Final Handshake

Once the rebuild finishes with your real ID:

1. **Create the secrets file** (if you haven't yet):
```bash
sudo touch /var/lib/openclaw/.env
sudo chown mike:users /var/lib/openclaw/.env
sudo chmod 600 /var/lib/openclaw/.env

```


2. **Add the tokens:**
`vim /var/lib/openclaw/.env`
```bash
OPENCLAW_GATEWAY_TOKEN=your_random_string
TELEGRAM_BOT_TOKEN=your_bot_token_from_botfather

```


3. **Restart:**
`systemctl restart openclaw`

You are minutes away from talking to your new agent.

**MikeLev.in**: Okay, I got and edited my telegram ID into openclaw.nix and rebuilt
which went fine. I am now editing `/var/lib/openclaw/.env` and I can guess that
I'm supposed to just put random text in for `OPENCLAW_GATEWAY_TOKEN` or am I
actually supposed to get it from somewhere? And for
`TELEGRAM_BOT_TOKEN=your_bot_token_from_botfather` I have no idea where the
`your_bot_token_from_botfather` value is supposed to come from.

**Gemini 3 Pro**: Great progress. You are past the hard part (building the software). Now you are just configuring the keys to the castle.

Here is exactly where those two values come from.

### 1. `OPENCLAW_GATEWAY_TOKEN` (The Web Password)

**Your guess was correct.** This is a password *you* invent right now.

* **What it is:** The "password" you will type into the web interface (`https://openclaw.nixos.local`) to log in for the first time.
* **How to get it:** Just make up a long, secure string. You can smash your keyboard or generate one.
* **Command to generate one (optional):**
```bash
openssl rand -hex 32

```


(Copy the output of that command and use it as your token).

### 2. `TELEGRAM_BOT_TOKEN` (The Bot Identity)

This one comes from Telegram itself. You need to create a "Bot" that OpenClaw will control.

**How to get it:**

1. Open Telegram.
2. Search for **`@BotFather`** (It has a blue checkmark).
3. Click **Start** (or type `/start`).
4. Type or click **`/newbot`**.
5. **Name it:** Give it a display name (e.g., "Mike's Agent").
6. **Username it:** Give it a unique username that ends in `bot` (e.g., `MikeLevAgentBot`).
7. **The Token:** BotFather will reply with a message saying "Done! Congratulations on your new bot."
* Look for the long string of characters under **"Use this token to access the HTTP API:"**.
* It looks like `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`.



**That string is your `TELEGRAM_BOT_TOKEN`.**

### The Final Step

Paste both into your `.env` file:

```bash
# Example
OPENCLAW_GATEWAY_TOKEN=a1b2c3d4e5f6... (your random string)
TELEGRAM_BOT_TOKEN=123456789:ABCdef... (from BotFather)

```

Save the file, then restart the service:

```bash
systemctl restart openclaw

```

Check if it's alive:

```bash
systemctl status openclaw

```

If it says "active (running)", you are live. Go to `https://openclaw.nixos.local` (or `http://localhost:baz`) and log in with your Gateway Token.

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/nixos]$ systemctl restart openclaw

[mike@nixos:~/repos/nixos]$ systemctl status openclaw
● openclaw.service - OpenClaw Sovereign Agent
     Loaded: loaded (/etc/systemd/system/openclaw.service; enabled; preset: ignored)
     Active: activating (auto-restart) (Result: exit-code) since Sun 2026-02-15 12:41:14 EST; 926ms ago
 Invocation: 8a9dbd6e492a4b4383e96e33bf475913
    Process: 142469 ExecStart=/nix/store/hiw6hkvg4a42as9325pp34dnxgr392g8-openclaw-2.0.0-beta5/bin/openclaw gateway (code=exited, status=1/FAILURE)
   Main PID: 142469 (code=exited, status=1/FAILURE)
         IP: 0B in, 0B out
         IO: 0B read, 0B written
   Mem peak: 217.2M
        CPU: 4.578s

[mike@nixos:~/repos/nixos]$
```

**Gemini 3 Pro**: It crashed, but that's okay. It ran for 4 seconds (`CPU: 4.578s`), which means the binary works and it started up, but it hit a configuration or permission wall and exited.

We need the **Logs** to see *why* it died. `systemctl status` only shows that it died, not the error message.

Run this command to see the last 50 lines of the service log:

```bash
journalctl -u openclaw -n 50 --no-pager

```

Paste the output here.

**Most likely suspects:**

1. **Schema Mismatch:** The config file generated by Nix might have a key the agent doesn't like.
2. **Permissions:** It might not be able to read the `.env` file or write to the `workspace` directory.
3. **Ollama:** It might be failing to connect to Ollama if the service isn't fully up.

Show me the logs, and we'll fix it immediately.

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/nixos]$ journalctl -u openclaw -n 50 --no-pager
Feb 15 12:42:10 nixos openclaw[142623]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:42:10 nixos openclaw[142623]: │                                                       │
Feb 15 12:42:10 nixos openclaw[142623]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:42:10 nixos openclaw[142623]: │                                                       │
Feb 15 12:42:10 nixos openclaw[142623]: ├───────────────────────────────────────────────────────╯
Feb 15 12:42:10 nixos openclaw[142623]: Config invalid
Feb 15 12:42:10 nixos openclaw[142623]: File: /nix/store/22m743p3nl6az5a2p9n7693sdyx0qc46-openclaw.json5
Feb 15 12:42:10 nixos openclaw[142623]: Problem:
Feb 15 12:42:10 nixos openclaw[142623]:   - models.providers.ollama.models: Invalid input: expected array, received undefined
Feb 15 12:42:10 nixos openclaw[142623]:   - agents.defaults.model.primary: Invalid input: expected string, received object
Feb 15 12:42:10 nixos openclaw[142623]:   - gateway.bind: Invalid input
Feb 15 12:42:10 nixos openclaw[142623]: Run: openclaw doctor --fix
Feb 15 12:42:10 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:42:10 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:42:10 nixos systemd[1]: openclaw.service: Consumed 4.459s CPU time, 217M memory peak.
Feb 15 12:42:15 nixos systemd[1]: openclaw.service: Scheduled restart job, restart counter is at 8.
Feb 15 12:42:15 nixos systemd[1]: Started OpenClaw Sovereign Agent.
Feb 15 12:42:18 nixos openclaw[142638]: Invalid config at /nix/store/22m743p3nl6az5a2p9n7693sdyx0qc46-openclaw.json5:\n- models.providers.ollama.models: Invalid input: expected array, received undefined
Feb 15 12:42:18 nixos openclaw[142638]: - agents.defaults.model.primary: Invalid input: expected string, received object
Feb 15 12:42:18 nixos openclaw[142638]: - gateway.bind: Invalid input
Feb 15 12:42:18 nixos openclaw[142638]: │
Feb 15 12:42:18 nixos openclaw[142638]: ◇  Config ───────────────────────────────────────────────────╮
Feb 15 12:42:18 nixos openclaw[142638]: │                                                            │
Feb 15 12:42:18 nixos openclaw[142638]: │  Config invalid; doctor will run with best-effort config.  │
Feb 15 12:42:18 nixos openclaw[142638]: │                                                            │
Feb 15 12:42:18 nixos openclaw[142638]: ├────────────────────────────────────────────────────────────╯
Feb 15 12:42:18 nixos openclaw[142638]: │
Feb 15 12:42:18 nixos openclaw[142638]: ◇  Doctor changes ──────────────────────────────╮
Feb 15 12:42:18 nixos openclaw[142638]: │                                               │
Feb 15 12:42:18 nixos openclaw[142638]: │  Telegram configured, enabled automatically.  │
Feb 15 12:42:18 nixos openclaw[142638]: │                                               │
Feb 15 12:42:18 nixos openclaw[142638]: ├───────────────────────────────────────────────╯
Feb 15 12:42:18 nixos openclaw[142638]: │
Feb 15 12:42:18 nixos openclaw[142638]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:42:18 nixos openclaw[142638]: │                                                       │
Feb 15 12:42:18 nixos openclaw[142638]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:42:18 nixos openclaw[142638]: │                                                       │
Feb 15 12:42:18 nixos openclaw[142638]: ├───────────────────────────────────────────────────────╯
Feb 15 12:42:18 nixos openclaw[142638]: Config invalid
Feb 15 12:42:18 nixos openclaw[142638]: File: /nix/store/22m743p3nl6az5a2p9n7693sdyx0qc46-openclaw.json5
Feb 15 12:42:18 nixos openclaw[142638]: Problem:
Feb 15 12:42:18 nixos openclaw[142638]:   - models.providers.ollama.models: Invalid input: expected array, received undefined
Feb 15 12:42:18 nixos openclaw[142638]:   - agents.defaults.model.primary: Invalid input: expected string, received object
Feb 15 12:42:18 nixos openclaw[142638]:   - gateway.bind: Invalid input
Feb 15 12:42:18 nixos openclaw[142638]: Run: openclaw doctor --fix
Feb 15 12:42:18 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:42:18 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:42:18 nixos systemd[1]: openclaw.service: Consumed 4.561s CPU time, 216.5M memory peak.
Feb 15 12:42:23 nixos systemd[1]: openclaw.service: Scheduled restart job, restart counter is at 9.
Feb 15 12:42:23 nixos systemd[1]: Started OpenClaw Sovereign Agent.

[mike@nixos:~/repos/nixos]$
```

**Gemini 3 Pro**: The logs are beautiful. They tell us *exactly* what is wrong.

The OpenClaw configuration schema is very strict (it uses a validator like Zod), and our "best guess" config violated it in three places.

### The Errors & The Fixes

1. **Error:** `gateway.bind: Invalid input`
* **Fix:** The config validator hates the `bind` key here. We will remove it. The gateway defaults to `127.0.0.1` anyway, which is what we want.

2. **Error:** `models.providers.ollama.models: Invalid input: expected array`
* **Fix:** We didn't list the models. You must explicitly whitelist the models you want OpenClaw to see.

3. **Error:** `agents.defaults.model.primary: Invalid input: expected string`
* **Fix:** We passed an object (`{ provider = ... }`). It just wants the **name** of the model (e.g., `"gemma3:latest"`).

### 1. Edit `openclaw.nix`

We need to rewrite the `openclawConfig` block with the corrected schema.

**File:** `/home/mike/repos/nixos/openclaw.nix`

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      # REMOVED: "bind" caused a schema error. Default is 127.0.0.1.
      port = baz;
      auth = { mode = "token"; };
    };

    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        allowFrom = [ 12345678 ]; # <--- ENSURE YOUR ID IS HERE
        configWrites = false; 
      };
    };

    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          # FIX 1: Explicitly list the models you want to use
          models = [ "gemma3:latest" ];
        };
      };
    };

    agents = {
      defaults = {
        model = {
          # FIX 2: Pass a string, not an object
          primary = "gemma3:latest";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  # 1. The Service
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    path = with pkgs; [ 
      openclawPkg git curl jq ripgrep python3 nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      EnvironmentFile = "-/var/lib/openclaw/.env";
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # 2. The Web Routing
  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };

  # 3. System Integration
  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}
```

### 2. Rebuild & Restart

1. **Rebuild:**
```bash
n
```

2. **Restart the service:**
```bash
systemctl restart openclaw
```

3. **Check the logs again:**
```bash
journalctl -u openclaw -n 50 --no-pager
```

**What to look for:**
You want to see a line that says `Gateway listening on http://127.0.0.1:baz`. If you see that, you are live.

**MikeLev.in**: Okay, I did that:

```bash
[mike@nixos:~/repos/nixos]$ systemctl restart openclaw

[mike@nixos:~/repos/nixos]$ journalctl -u openclaw -n 50 --no-pager
Feb 15 12:46:25 nixos openclaw[144829]: │                                               │
Feb 15 12:46:25 nixos openclaw[144829]: │  Telegram configured, enabled automatically.  │
Feb 15 12:46:25 nixos openclaw[144829]: │                                               │
Feb 15 12:46:25 nixos openclaw[144829]: ├───────────────────────────────────────────────╯
Feb 15 12:46:25 nixos openclaw[144829]: │
Feb 15 12:46:25 nixos openclaw[144829]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:46:25 nixos openclaw[144829]: │                                                       │
Feb 15 12:46:25 nixos openclaw[144829]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:46:25 nixos openclaw[144829]: │                                                       │
Feb 15 12:46:25 nixos openclaw[144829]: ├───────────────────────────────────────────────────────╯
Feb 15 12:46:25 nixos openclaw[144829]: Config invalid
Feb 15 12:46:25 nixos openclaw[144829]: File: /nix/store/jr7096wka9xra93zhq3xwzfyavpgl88w-openclaw.json5
Feb 15 12:46:25 nixos openclaw[144829]: Problem:
Feb 15 12:46:25 nixos openclaw[144829]:   - models.providers.ollama.models.0: Invalid input: expected object, received string
Feb 15 12:46:25 nixos openclaw[144829]: Run: openclaw doctor --fix
Feb 15 12:46:25 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:46:25 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:46:25 nixos systemd[1]: openclaw.service: Consumed 4.455s CPU time, 213.9M memory peak.
Feb 15 12:46:26 nixos systemd[1]: Stopped OpenClaw Sovereign Agent.
Feb 15 12:46:26 nixos systemd[1]: openclaw.service: Consumed 4.455s CPU time, 213.9M memory peak.
Feb 15 12:46:26 nixos systemd[1]: Started OpenClaw Sovereign Agent.
Feb 15 12:46:29 nixos openclaw[144859]: Invalid config at /nix/store/jr7096wka9xra93zhq3xwzfyavpgl88w-openclaw.json5:\n- models.providers.ollama.models.0: Invalid input: expected object, received string
Feb 15 12:46:29 nixos openclaw[144859]: │
Feb 15 12:46:29 nixos openclaw[144859]: ◇  Config ───────────────────────────────────────────────────╮
Feb 15 12:46:29 nixos openclaw[144859]: │                                                            │
Feb 15 12:46:29 nixos openclaw[144859]: │  Config invalid; doctor will run with best-effort config.  │
Feb 15 12:46:29 nixos openclaw[144859]: │                                                            │
Feb 15 12:46:29 nixos openclaw[144859]: ├────────────────────────────────────────────────────────────╯
Feb 15 12:46:29 nixos openclaw[144859]: │
Feb 15 12:46:29 nixos openclaw[144859]: ◇  Doctor changes ──────────────────────────────╮
Feb 15 12:46:29 nixos openclaw[144859]: │                                               │
Feb 15 12:46:29 nixos openclaw[144859]: │  Telegram configured, enabled automatically.  │
Feb 15 12:46:29 nixos openclaw[144859]: │                                               │
Feb 15 12:46:29 nixos openclaw[144859]: ├───────────────────────────────────────────────╯
Feb 15 12:46:29 nixos openclaw[144859]: │
Feb 15 12:46:29 nixos openclaw[144859]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:46:29 nixos openclaw[144859]: │                                                       │
Feb 15 12:46:29 nixos openclaw[144859]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:46:29 nixos openclaw[144859]: │                                                       │
Feb 15 12:46:29 nixos openclaw[144859]: ├───────────────────────────────────────────────────────╯
Feb 15 12:46:29 nixos openclaw[144859]: Config invalid
Feb 15 12:46:29 nixos openclaw[144859]: File: /nix/store/jr7096wka9xra93zhq3xwzfyavpgl88w-openclaw.json5
Feb 15 12:46:29 nixos openclaw[144859]: Problem:
Feb 15 12:46:29 nixos openclaw[144859]:   - models.providers.ollama.models.0: Invalid input: expected object, received string
Feb 15 12:46:29 nixos openclaw[144859]: Run: openclaw doctor --fix
Feb 15 12:46:29 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:46:29 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:46:29 nixos systemd[1]: openclaw.service: Consumed 4.475s CPU time, 212.5M memory peak.
Feb 15 12:46:34 nixos systemd[1]: openclaw.service: Scheduled restart job, restart counter is at 1.
Feb 15 12:46:34 nixos systemd[1]: Started OpenClaw Sovereign Agent.

[mike@nixos:~/repos/nixos]$
```

And here's my diff:

```diff
[mike@nixos:~/repos/nixos]$ git --no-pager diff
diff --git a/openclaw.nix b/openclaw.nix
index 78ad0e2..b640757 100644
--- a/openclaw.nix
+++ b/openclaw.nix
@@ -9,27 +9,35 @@ let
   openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
     gateway = {
       mode = "local";
-      bind = "127.0.0.1";
+      # REMOVED: "bind" caused a schema error. Default is 127.0.0.1.
       port = baz;
       auth = { mode = "token"; };
     };
+
     channels = {
       telegram = {
         enabled = true;
         dmPolicy = "pairing"; 
-        allowFrom = [ 12345678 ]; # <--- REPLACE WITH YOUR ID
+        allowFrom = [ ******** ]; # <--- ENSURE YOUR ID IS HERE (hidden for prompt)
         configWrites = false; 
       };
     };
+
     models = {
       providers = {
-        ollama = { baseUrl = "http://127.0.0.1:11434"; };
+        ollama = {
+          baseUrl = "http://127.0.0.1:11434";
+          # FIX 1: Explicitly list the models you want to use
+          models = [ "gemma3:latest" ];
+        };
       };
     };
+
     agents = {
       defaults = {
         model = {
-          primary = { provider = "ollama"; model = "gemma3:latest"; };
+          # FIX 2: Pass a string, not an object
+          primary = "gemma3:latest";
         };
         workspace = "/home/mike/.openclaw/workspace";
       };
@@ -60,8 +68,7 @@ in
     };
   };
 
-  # 2. The Web Routing (Self-Contained)
-  # NixOS will merge this with the Caddy config in services.nix
+  # 2. The Web Routing
   services.caddy.virtualHosts."openclaw.nixos.local" = {
     extraConfig = ''
       reverse_proxy localhost:baz
@@ -76,4 +83,4 @@ in
   systemd.tmpfiles.rules = [
     "d /var/lib/openclaw 0700 mike users -"
   ];
-}
\ No newline at end of file
+}

[mike@nixos:~/repos/nixos]$
```

**Gemini 3 Pro**: Context verified.

We are incredibly close. The logs show that we fixed 2 out of 3 errors! The `bind` error is gone, and the `primary` model error is gone.

The only one left is this:
`models.providers.ollama.models.0: Invalid input: expected object, received string`

This means OpenClaw expects the `models` list to contain **Objects** (e.g., `{ id = "name"; }`), not just plain strings.

### The Fix

We need to wrap the model name in a small object structure.

**File:** `/home/mike/repos/nixos/openclaw.nix`

Change the `models` block to looks like this:

```nix
    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          # FIX: Wrap the model string in an object with an 'id'
          models = [ { id = "gemma3:latest"; } ];
        };
      };
    };

```

### Complete File Content (for easy copy/paste)

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      port = baz;
      auth = { mode = "token"; };
    };

    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        allowFrom = [ 12345678 ]; # <--- KEEP YOUR ID HERE
        configWrites = false; 
      };
    };

    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          # FIX: Models must be objects, not strings
          models = [ { id = "gemma3:latest"; } ];
        };
      };
    };

    agents = {
      defaults = {
        model = {
          primary = "gemma3:latest";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  # 1. The Service
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    path = with pkgs; [ 
      openclawPkg git curl jq ripgrep python3 nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      EnvironmentFile = "-/var/lib/openclaw/.env";
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # 2. The Web Routing
  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };

  # 3. System Integration
  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}

```

### Next Steps

1. **Edit:** Apply the change to `openclaw.nix`.
2. **Rebuild:** `n`
3. **Restart:** `systemctl restart openclaw`
4. **Log Check:** `journalctl -u openclaw -n 50 --no-pager`

This should be the final key to the lock.

**MikeLev.in**: Okay, did that:

```bash
[mike@nixos:~/repos/nixos]$ systemctl restart openclaw

[mike@nixos:~/repos/nixos]$ journalctl -u openclaw -n 50 --no-pager
Feb 15 12:51:35 nixos openclaw[147176]: │                                               │
Feb 15 12:51:35 nixos openclaw[147176]: │  Telegram configured, enabled automatically.  │
Feb 15 12:51:35 nixos openclaw[147176]: │                                               │
Feb 15 12:51:35 nixos openclaw[147176]: ├───────────────────────────────────────────────╯
Feb 15 12:51:35 nixos openclaw[147176]: │
Feb 15 12:51:35 nixos openclaw[147176]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:51:35 nixos openclaw[147176]: │                                                       │
Feb 15 12:51:35 nixos openclaw[147176]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:51:35 nixos openclaw[147176]: │                                                       │
Feb 15 12:51:35 nixos openclaw[147176]: ├───────────────────────────────────────────────────────╯
Feb 15 12:51:35 nixos openclaw[147176]: Config invalid
Feb 15 12:51:35 nixos openclaw[147176]: File: /nix/store/z42w23gkdikh4ad0qdzahsi7glc0xapa-openclaw.json5
Feb 15 12:51:35 nixos openclaw[147176]: Problem:
Feb 15 12:51:35 nixos openclaw[147176]:   - models.providers.ollama.models.0.name: Invalid input: expected string, received undefined
Feb 15 12:51:35 nixos openclaw[147176]: Run: openclaw doctor --fix
Feb 15 12:51:35 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:51:35 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:51:35 nixos systemd[1]: openclaw.service: Consumed 4.548s CPU time, 220.8M memory peak.
Feb 15 12:51:36 nixos systemd[1]: Stopped OpenClaw Sovereign Agent.
Feb 15 12:51:36 nixos systemd[1]: openclaw.service: Consumed 4.548s CPU time, 220.8M memory peak.
Feb 15 12:51:36 nixos systemd[1]: Started OpenClaw Sovereign Agent.
Feb 15 12:51:39 nixos openclaw[147212]: Invalid config at /nix/store/z42w23gkdikh4ad0qdzahsi7glc0xapa-openclaw.json5:\n- models.providers.ollama.models.0.name: Invalid input: expected string, received undefined
Feb 15 12:51:39 nixos openclaw[147212]: │
Feb 15 12:51:39 nixos openclaw[147212]: ◇  Config ───────────────────────────────────────────────────╮
Feb 15 12:51:39 nixos openclaw[147212]: │                                                            │
Feb 15 12:51:39 nixos openclaw[147212]: │  Config invalid; doctor will run with best-effort config.  │
Feb 15 12:51:39 nixos openclaw[147212]: │                                                            │
Feb 15 12:51:39 nixos openclaw[147212]: ├────────────────────────────────────────────────────────────╯
Feb 15 12:51:39 nixos openclaw[147212]: │
Feb 15 12:51:39 nixos openclaw[147212]: ◇  Doctor changes ──────────────────────────────╮
Feb 15 12:51:39 nixos openclaw[147212]: │                                               │
Feb 15 12:51:39 nixos openclaw[147212]: │  Telegram configured, enabled automatically.  │
Feb 15 12:51:39 nixos openclaw[147212]: │                                               │
Feb 15 12:51:39 nixos openclaw[147212]: ├───────────────────────────────────────────────╯
Feb 15 12:51:39 nixos openclaw[147212]: │
Feb 15 12:51:39 nixos openclaw[147212]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:51:39 nixos openclaw[147212]: │                                                       │
Feb 15 12:51:39 nixos openclaw[147212]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:51:39 nixos openclaw[147212]: │                                                       │
Feb 15 12:51:39 nixos openclaw[147212]: ├───────────────────────────────────────────────────────╯
Feb 15 12:51:39 nixos openclaw[147212]: Config invalid
Feb 15 12:51:39 nixos openclaw[147212]: File: /nix/store/z42w23gkdikh4ad0qdzahsi7glc0xapa-openclaw.json5
Feb 15 12:51:39 nixos openclaw[147212]: Problem:
Feb 15 12:51:39 nixos openclaw[147212]:   - models.providers.ollama.models.0.name: Invalid input: expected string, received undefined
Feb 15 12:51:39 nixos openclaw[147212]: Run: openclaw doctor --fix
Feb 15 12:51:39 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:51:39 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:51:39 nixos systemd[1]: openclaw.service: Consumed 4.519s CPU time, 224.6M memory peak.
Feb 15 12:51:44 nixos systemd[1]: openclaw.service: Scheduled restart job, restart counter is at 1.
Feb 15 12:51:44 nixos systemd[1]: Started OpenClaw Sovereign Agent.

[mike@nixos:~/repos/nixos]$
```

And the diff:

```diff
[mike@nixos:~/repos/nixos]$ git --no-pager diff
diff --git a/openclaw.nix b/openclaw.nix
index b640757..f0f7171 100644
--- a/openclaw.nix
+++ b/openclaw.nix
@@ -9,7 +9,6 @@ let
   openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
     gateway = {
       mode = "local";
-      # REMOVED: "bind" caused a schema error. Default is 127.0.0.1.
       port = baz;
       auth = { mode = "token"; };
     };
@@ -18,7 +17,7 @@ let
       telegram = {
         enabled = true;
         dmPolicy = "pairing"; 
-        allowFrom = [ hidden again ]; # <--- ENSURE YOUR ID IS HERE
+        allowFrom = [ hidden again ]; # <--- KEEP YOUR ID HERE
         configWrites = false; 
       };
     };
@@ -27,8 +26,8 @@ let
       providers = {
         ollama = {
           baseUrl = "http://127.0.0.1:11434";
-          # FIX 1: Explicitly list the models you want to use
-          models = [ "gemma3:latest" ];
+          # FIX: Models must be objects, not strings
+          models = [ { id = "gemma3:latest"; } ];
         };
       };
     };
@@ -36,7 +35,6 @@ let
     agents = {
       defaults = {
         model = {
-          # FIX 2: Pass a string, not an object
           primary = "gemma3:latest";
         };
         workspace = "/home/mike/.openclaw/workspace";

[mike@nixos:~/repos/nixos]$
```

**Gemini 3 Pro**: Context verified.

We are whittling this down one error at a time. The schema validation is very helpful here.

**The Error:**
`models.providers.ollama.models.0.name: Invalid input: expected string, received undefined`

**The Fix:**
You provided an object `{ id = "gemma3:latest"; }`, but the schema requires a `name` field as well (likely for the UI display).

### 1. Edit `openclaw.nix`

We need to add the `name` key to the model object.

**File:** `/home/mike/repos/nixos/openclaw.nix`

Change the `models` block to this:

```nix
    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          # FIX: Add the 'name' field
          models = [ { id = "gemma3:latest"; name = "Gemma 3"; } ];
        };
      };
    };

```

### Complete File (for copy/paste)

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      port = baz;
      auth = { mode = "token"; };
    };

    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        allowFrom = [ 12345678 ]; # <--- KEEP YOUR ID HERE
        configWrites = false; 
      };
    };

    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          # FIX: Added 'name' field
          models = [ { id = "gemma3:latest"; name = "Gemma 3"; } ];
        };
      };
    };

    agents = {
      defaults = {
        model = {
          primary = "gemma3:latest";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  # 1. The Service
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    path = with pkgs; [ 
      openclawPkg git curl jq ripgrep python3 nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      EnvironmentFile = "-/var/lib/openclaw/.env";
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # 2. The Web Routing
  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };

  # 3. System Integration
  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}

```

### 2. The Routine

1. **Rebuild:** `n`
2. **Restart:** `systemctl restart openclaw`
3. **Check Logs:** `journalctl -u openclaw -n 50 --no-pager`

If the logs show `Gateway listening...`, you are done!

**MikeLev.in**: Okay, now we got this far:

```bash
[mike@nixos:~/repos/nixos]$ systemctl restart openclaw

[mike@nixos:~/repos/nixos]$ journalctl -u openclaw -n 50 --no-pager
Feb 15 12:56:26 nixos openclaw[148575]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:56:26 nixos openclaw[148575]: │                                                       │
Feb 15 12:56:26 nixos openclaw[148575]: ├───────────────────────────────────────────────────────╯
Feb 15 12:56:26 nixos openclaw[148575]: Config invalid
Feb 15 12:56:26 nixos openclaw[148575]: File: /nix/store/z42w23gkdikh4ad0qdzahsi7glc0xapa-openclaw.json5
Feb 15 12:56:26 nixos openclaw[148575]: Problem:
Feb 15 12:56:26 nixos openclaw[148575]:   - models.providers.ollama.models.0.name: Invalid input: expected string, received undefined
Feb 15 12:56:26 nixos openclaw[148575]: Run: openclaw doctor --fix
Feb 15 12:56:26 nixos systemd[1]: openclaw.service: Main process exited, code=exited, status=1/FAILURE
Feb 15 12:56:26 nixos systemd[1]: openclaw.service: Failed with result 'exit-code'.
Feb 15 12:56:26 nixos systemd[1]: openclaw.service: Consumed 4.497s CPU time, 215.6M memory peak.
Feb 15 12:56:27 nixos systemd[1]: Stopped OpenClaw Sovereign Agent.
Feb 15 12:56:27 nixos systemd[1]: openclaw.service: Consumed 4.497s CPU time, 215.6M memory peak.
Feb 15 12:56:28 nixos systemd[1]: Started OpenClaw Sovereign Agent.
Feb 15 12:56:31 nixos openclaw[149492]: │
Feb 15 12:56:31 nixos openclaw[149492]: ◇  Doctor changes ──────────────────────────────╮
Feb 15 12:56:31 nixos openclaw[149492]: │                                               │
Feb 15 12:56:31 nixos openclaw[149492]: │  Telegram configured, enabled automatically.  │
Feb 15 12:56:31 nixos openclaw[149492]: │                                               │
Feb 15 12:56:31 nixos openclaw[149492]: ├───────────────────────────────────────────────╯
Feb 15 12:56:31 nixos openclaw[149492]: │
Feb 15 12:56:31 nixos openclaw[149492]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:56:31 nixos openclaw[149492]: │                                                       │
Feb 15 12:56:31 nixos openclaw[149492]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:56:31 nixos openclaw[149492]: │                                                       │
Feb 15 12:56:31 nixos openclaw[149492]: ├───────────────────────────────────────────────────────╯
Feb 15 12:56:31 nixos openclaw[149492]: 2026-02-15T17:56:31.812Z [gateway] failed to persist plugin auto-enable changes: Error: EROFS: read-only file system, open '/nix/store/xb043i2p5jcyxpdqhj6d3cny1207s3fl-openclaw.json5.149492.3cfd349f-2601-4a79-9925-33ea4e88a704.tmp'
Feb 15 12:56:41 nixos systemd[1]: Stopping OpenClaw Sovereign Agent...
Feb 15 12:56:59 nixos openclaw[149492]: 2026-02-15T17:56:59.947Z [gateway] signal SIGTERM received
Feb 15 12:56:59 nixos openclaw[149492]: 2026-02-15T17:56:59.948Z [gateway] received SIGTERM; shutting down
Feb 15 12:57:00 nixos systemd[1]: openclaw.service: Deactivated successfully.
Feb 15 12:57:00 nixos systemd[1]: Stopped OpenClaw Sovereign Agent.
Feb 15 12:57:00 nixos systemd[1]: openclaw.service: Consumed 53.292s CPU time, 1.7G memory peak, 7.6M written to disk.
Feb 15 12:57:00 nixos systemd[1]: Started OpenClaw Sovereign Agent.
Feb 15 12:57:00 nixos systemd[1]: Stopping OpenClaw Sovereign Agent...
Feb 15 12:57:00 nixos systemd[1]: openclaw.service: Deactivated successfully.
Feb 15 12:57:00 nixos systemd[1]: Stopped OpenClaw Sovereign Agent.
Feb 15 12:57:00 nixos systemd[1]: Started OpenClaw Sovereign Agent.
Feb 15 12:57:03 nixos openclaw[149691]: │
Feb 15 12:57:03 nixos openclaw[149691]: ◇  Doctor changes ──────────────────────────────╮
Feb 15 12:57:03 nixos openclaw[149691]: │                                               │
Feb 15 12:57:03 nixos openclaw[149691]: │  Telegram configured, enabled automatically.  │
Feb 15 12:57:03 nixos openclaw[149691]: │                                               │
Feb 15 12:57:03 nixos openclaw[149691]: ├───────────────────────────────────────────────╯
Feb 15 12:57:03 nixos openclaw[149691]: │
Feb 15 12:57:03 nixos openclaw[149691]: ◇  Doctor ──────────────────────────────────────────────╮
Feb 15 12:57:03 nixos openclaw[149691]: │                                                       │
Feb 15 12:57:03 nixos openclaw[149691]: │  Run "openclaw doctor --fix" to apply these changes.  │
Feb 15 12:57:03 nixos openclaw[149691]: │                                                       │
Feb 15 12:57:03 nixos openclaw[149691]: ├───────────────────────────────────────────────────────╯

[mike@nixos:~/repos/nixos]$
```

**Gemini 3 Pro**: It's working!

The service **did not exit**. It's running.

The error you see:
`Error: EROFS: read-only file system ... openclaw.json5.tmp`

This is actually **good news**. It means:

1. The agent started successfully.
2. It loaded your config.
3. It detected that Telegram was configured and tried to auto-enable it (and save that change to disk).
4. It failed to save because `/nix/store/...` is read-only. **This is expected and fine.** We want the config to be immutable.

### Verification

Run this to confirm it is actually listening:

```bash
# Check if it is listening on port baz
sudo lsof -i :baz

```

Or just check the status again to confirm it's still "active (running)":

```bash
systemctl status openclaw

```

### The Final Step: Connect

If the service is running, open your browser and go to:

**`https://openclaw.nixos.local`**

(Or `http://192.168.x.x:baz` if Caddy isn't resolving yet).

It should ask for your **Token**. Paste the `OPENCLAW_GATEWAY_TOKEN` you put in the `.env` file.

You have successfully deployed a sovereign agent on NixOS using a manual systemd service and a remote flake. This is a massive win.

## The Immutable Agent: Triumphant Deployment on NixOS

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/nixos]$ sudo lsof -i :baz
sudo: lsof: command not found

[mike@nixos:~/repos/nixos]$ systemctl status openclaw
● openclaw.service - OpenClaw Sovereign Agent
     Loaded: loaded (/etc/systemd/system/openclaw.service; enabled; preset: ignored)
     Active: active (running) since Sun 2026-02-15 12:57:00 EST; 3min 17s ago
 Invocation: 6b0f7fd413a247e69adae99cb53cd9d4
   Main PID: 149691 (openclaw-gatewa)
         IP: 10.1K in, 4.2K out
         IO: 4K read, 16K written
      Tasks: 11 (limit: 154387)
     Memory: 287.8M (peak: 369.4M)
        CPU: 9.279s
     CGroup: /system.slice/openclaw.service
             └─149691 openclaw-gateway

Feb 15 12:57:03 nixos openclaw[149691]: 2026-02-15T17:57:03.356Z [gateway] failed to persist plugin auto-enable changes: Error: EROFS: read-only file system, open '/nix/store/xb043i2p5jcyxpdqhj6d3cny1207s3fl-openclaw.json5.149691.7d72aeb4-8599-407c-9e52-5bfe209604ed.tmp'
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.132Z [canvas] host mounted at http://127.0.0.1:baz/__openclaw__/canvas/ (root /home/mike/.openclaw/canvas)
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.190Z [heartbeat] started
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T12:57:05.192-05:00 [openclaw] Model "gemma3:latest" specified without provider. Falling back to "anthropic/gemma3:latest". Please use "anthropic/gemma3:latest" in your config.
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.200Z [gateway] agent model: anthropic/gemma3:latest
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.200Z [gateway] listening on ws://127.0.0.1:baz (PID 149691)
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.201Z [gateway] listening on ws://[::1]:baz
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.209Z [gateway] log file: /tmp/openclaw/openclaw-2026-02-15.log
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.210Z [gateway] running in Nix mode (config managed externally)
Feb 15 12:57:05 nixos openclaw[149691]: 2026-02-15T17:57:05.217Z [browser/service] Browser control service ready (profiles=2)

[mike@nixos:~/repos/nixos]$
```

I even opened the port on the firewall (I think). Attempts to reach
`http://192.168.foo.bar:baz/` give me "ERR_CONNECTION_REFUSED"

**Gemini 3 Pro**: Context verified.

You have achieved **Process Stability**. The service is up, the configuration is valid (enough to start), and it is listening.

The log message `Error: EROFS: read-only file system` is a **Badge of Honor**. It means your agent tried to rewrite its own config (to auto-enable the plugin), and NixOS stopped it. You have successfully created an immutable agent.

### The Connection Problem

You are getting `ERR_CONNECTION_REFUSED` because:

1. **OpenClaw** is listening on `127.0.0.1:baz` (Localhost). It refuses connections from `192.168.foo.bar` (External).
2. **Caddy** is listening on `https://openclaw.nixos.local`, but your phone/laptop doesn't know that `openclaw.nixos.local` points to `192.168.foo.bar`.

### The Model Problem

The logs show: `Model "gemma3:latest" specified without provider. Falling back to "anthropic/gemma3:latest".`
It is defaulting to Anthropic because we didn't specify the provider prefix in the `primary` string.

---

### The Fix: Update `openclaw.nix`

We will do two things:

1. **Fix the Model:** Point explicitly to `ollama/gemma3:latest`.
2. **Fix Access:** Update Caddy to listen on your **IP Address** so you can access it from your phone without messing with DNS/Hosts files.

**File:** `/home/mike/repos/nixos/openclaw.nix`

```nix
{ config, pkgs, lib, ... }:

let
  # ⚡ THE LOOPHOLE: Pull the flake directly
  openclawSrc = builtins.getFlake "github:openclaw/nix-openclaw";
  openclawPkg = openclawSrc.packages.${pkgs.system}.default;

  # Define the Config (JSON5)
  openclawConfig = pkgs.writeText "openclaw.json5" (builtins.toJSON {
    gateway = {
      mode = "local";
      port = baz;
      auth = { mode = "token"; };
    };

    channels = {
      telegram = {
        enabled = true;
        dmPolicy = "pairing"; 
        allowFrom = [ foo ]; # Your ID
        configWrites = false; 
      };
    };

    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          models = [ { id = "gemma3:latest"; name = "Gemma 3"; } ];
        };
      };
    };

    agents = {
      defaults = {
        model = {
          # FIX 1: Add the 'ollama/' prefix so it finds the local provider
          primary = "ollama/gemma3:latest";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };
  });

in
{
  # 1. The Service
  systemd.services.openclaw = {
    description = "OpenClaw Sovereign Agent";
    after = [ "network.target" "ollama.service" ];
    wantedBy = [ "multi-user.target" ];

    path = with pkgs; [ 
      openclawPkg git curl jq ripgrep python3 nodejs
    ];

    serviceConfig = {
      User = "mike";
      Group = "users";
      WorkingDirectory = "/home/mike";
      Environment = "OPENCLAW_CONFIG_PATH=${openclawConfig}";
      EnvironmentFile = "-/var/lib/openclaw/.env";
      ExecStart = "${openclawPkg}/bin/openclaw gateway";
      Restart = "always";
      RestartSec = "5s";
    };
  };

  # 2. The Web Routing (IP Access Fix)
  # We add the IP address as a host so Caddy serves it directly.
  services.caddy.virtualHosts."192.168.foo.bar" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };
  
  # Keep the hostname version too, just in case
  services.caddy.virtualHosts."openclaw.nixos.local" = {
    extraConfig = ''
      reverse_proxy localhost:baz
      tls internal
    '';
  };

  # 3. System Integration
  environment.systemPackages = [ openclawPkg ];
  networking.firewall.allowedTCPPorts = [ baz ];
  
  systemd.tmpfiles.rules = [
    "d /var/lib/openclaw 0700 mike users -"
  ];
}
```

### The Routine

1. **Rebuild:** `n`
2. **Restart:** `systemctl restart openclaw` (Just to apply the model fix)
3. **Connect:**
* Open your browser (phone or laptop).
* Go to: **`https://192.168.foo.bar`**
* **Accept the Warning:** You will see a "Not Secure" / "Certificate Invalid" warning. This is normal (it's a self-signed cert from Caddy). Click "Advanced" -> "Proceed".
* **Login:** Enter the `OPENCLAW_GATEWAY_TOKEN` from your `.env` file.

Welcome to the bridge of your ship.

**MikeLev.in**: Okay, I did that and I still can't reach `http://192.168.foo.bar:baz/`
which is fine. I can reach `http://localhost:baz/chat?session=main` now on the
server. When on the phone we're probably supposed to use Telegram anyway. I
reach the Web UI on the server where it says:

disconnected (1008): unauthorized: gateway token missing (open the dashboard URL and paste the token in Control UI settings)

**Gemini 3 Pro**: Context verified.

You are seeing the "Unauthorized" error because you are trying to access the **Chat Interface** (`/chat`) before the browser has saved your login credentials.

### 1. Fix the "Disconnected" Error (Localhost)

You need to log in to the **Dashboard** first to save the token in your browser.

1. Go to the **Root URL**: **`http://localhost:baz/`**
* (Do not go to `/chat` yet).


2. You should see a prompt asking for your **Gateway Token**.
3. Paste the `OPENCLAW_GATEWAY_TOKEN` you put in your `.env` file.
4. Click **Connect/Save**.
5. *Now* you can click the "Chat" icon or go to `/chat`.

### 2. Why the IP Address (`:baz`) Failed

You mentioned: *"I still can't reach `http://192.168.foo.bar:baz/`"*

**This is expected and correct security.**

* **Port baz** is bound to `127.0.0.1` (Localhost) inside `openclaw.nix`. It refuses to talk to the outside world directly.
* **Caddy** (the web server) is the one listening on the outside.

**To access it from your phone/laptop:**
Do **not** use port baz. Use the standard web port (handled by Caddy):

* **`http://192.168.foo.bar`** (No port)
* or **`https://192.168.foo.bar`** (If Caddy set up SSL)

### 3. Telegram is Ready

Since the logs said `Telegram configured, enabled automatically`, you don't even need the web UI on your phone.

1. Open Telegram on your phone.
2. Find your bot (`@MikeLevAgentBot` or whatever you named it).
3. Send the message: **`/pair`** (or just say "Hello").
4. It might ask you to confirm the pairing code in the Web UI (on the server), or since you added your ID to `allowFrom`, it might just work instantly.

You are live.

**MikeLev.in**: It's auto-forwarding me to that chat URL. I went to the Overview link
and filled in the Gateway Token and the error message went away! But when I try
to chat the spinner just goes forever in a waiting for response state. I know
that Ollama and gemma3 are there from when I do this:

```bash
[mike@nixos:~/repos/pipulate]$ ollama run gemma3
>>> Hi
Hi there! How’s it going today? 😊 

Is there anything you’d like to talk about, or were you just saying hello? 

I'm here to chat, answer questions, help with ideas, or just listen if you need to.

>>> Send a message (/? for help)
```

I think there's some other subtlety still out of whack. Or maybe the old local
gemma3 model isn't compatible (not robustly tool-calling, I don't think). What
kind of tests could I do at this point? I'm not married to sticking with Ollama
local. I want to just bank that initial interacting with a model success. Is the
model now hardwired into `openclaw.nix` requiring a system rebuild whenever I
change it?

**Gemini 3 Pro**: This "infinite spinner" usually means one of three things:

1. **The Model is "Thinking" (Slow):** Local models on CPU can take a long time to generate the first token, especially if the system prompt (which OpenClaw injects) is large.
2. **Format Mismatch:** The agent sent a prompt structure that confused Gemma 3, and it's either crashing silently or generating output that OpenClaw's parser doesn't understand (e.g., malformed JSON for tool use).
3. **Network Timeout:** The browser is waiting for a WebSocket message that is getting blocked or timed out.

### Step 1: The "Live" Log Test

We need to see what is happening *while* the spinner is spinning.

Run this command in one terminal window:

```bash
# -f means "follow" (tail the logs in real-time)
journalctl -u openclaw -f

```

Then, go to the Web UI and send "Hi" again.

**Watch the logs:**

* **If you see nothing:** The request isn't reaching the server (Browser/Network issue).
* **If you see `[ollama] generating...`:** It's working, just slow.
* **If you see `[ollama] error`:** It will tell us why (e.g., context length exceeded).

### Step 2: Is the model hardwired?

**Yes.** Because you defined it in `openclaw.nix`, it is "Immutable Infrastructure."
To change the model, you must:

1. Edit `openclaw.nix`.
2. Run `n` (Rebuild).
3. Run `systemctl restart openclaw`.

**The "Bank The Win" Strategy (Switch to Anthropic)**

Since you want to "Bank a Win" and not debug local model quirks all day, I strongly recommend adding **Anthropic** (Claude) to your configuration. It is the gold standard for agents right now and rules out local hardware issues.

**1. Edit `.env` (Add the Key)**

```bash
vim /var/lib/openclaw/.env

```

Add:

```bash
ANTHROPIC_API_KEY=sk-ant-api03-... (Your Key)

```

**2. Edit `openclaw.nix` (Add the Provider)**

Update the `models` block to include Anthropic alongside Ollama. This gives you a dropdown choice in the future (or allows failover).

```nix
    models = {
      providers = {
        # Keep Ollama for later
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          models = [ { id = "gemma3:latest"; name = "Gemma 3"; } ];
        };
        # Add Anthropic (The "Smart" Brain)
        anthropic = {
          enabled = true;
        };
      };
    };

    agents = {
      defaults = {
        model = {
          # Switch primary to Claude for the "Win"
          primary = "anthropic/claude-3-5-sonnet-20241022";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };

```

**3. Rebuild (`n`) and Restart.**

If this works immediately, you know the previous issue was just Gemma 3 being finicky (which is common for new/smaller local models with complex agent prompts). If it *still* spins, you have a network/websocket issue.

**MikeLev.in**: 

```bash
[mike@nixos:~/repos/nixos]$ journalctl -u openclaw -f
Feb 15 13:21:57 nixos openclaw[152584]: 2026-02-15T18:21:57.075Z [ws] webchat connected conn=df7e3e94-f950-44f4-b9df-d9e4f5c23481 remote=::1 client=openclaw-control-ui webchat vdev
Feb 15 13:22:13 nixos openclaw[152584]: 2026-02-15T18:22:13.307Z [diagnostic] lane task error: lane=main durationMs=48 error="Error: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir."
Feb 15 13:22:13 nixos openclaw[152584]: 2026-02-15T18:22:13.308Z [diagnostic] lane task error: lane=session:agent:main:main durationMs=53 error="Error: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir."
Feb 15 13:22:13 nixos openclaw[152584]: 2026-02-15T13:22:13.313-05:00 Embedded agent failed before reply: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir.
Feb 15 13:23:23 nixos openclaw[152584]: 2026-02-15T18:23:23.260Z [ws] ⇄ res ✓ config.schema 490ms conn=df7e3e94…3481 id=2f091183…d7ff
Feb 15 13:25:25 nixos openclaw[152584]: 2026-02-15T18:25:25.150Z [ws] webchat disconnected code=1005 reason=n/a conn=df7e3e94-f950-44f4-b9df-d9e4f5c23481
Feb 15 13:25:25 nixos openclaw[152584]: 2026-02-15T18:25:25.167Z [ws] webchat connected conn=35a9d182-92e5-4c53-9472-e4ab0f49bd0b remote=::1 client=openclaw-control-ui webchat vdev
Feb 15 13:25:39 nixos openclaw[152584]: 2026-02-15T18:25:39.664Z [diagnostic] lane task error: lane=main durationMs=99 error="Error: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir."
Feb 15 13:25:39 nixos openclaw[152584]: 2026-02-15T18:25:39.665Z [diagnostic] lane task error: lane=session:agent:main:main durationMs=104 error="Error: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir."
Feb 15 13:25:39 nixos openclaw[152584]: 2026-02-15T13:25:39.666-05:00 Embedded agent failed before reply: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir.
Feb 15 13:32:41 nixos openclaw[152584]: 2026-02-15T18:32:41.203Z [ws] webchat disconnected code=1001 reason=n/a conn=35a9d182-92e5-4c53-9472-e4ab0f49bd0b
Feb 15 13:32:41 nixos openclaw[152584]: 2026-02-15T18:32:41.436Z [ws] webchat connected conn=a3fafd2e-eac0-44b5-aafc-47014515c8c8 remote=::1 client=openclaw-control-ui webchat vdev
Feb 15 13:32:47 nixos openclaw[152584]: 2026-02-15T18:32:47.794Z [diagnostic] lane task error: lane=main durationMs=71 error="Error: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir."
Feb 15 13:32:47 nixos openclaw[152584]: 2026-02-15T18:32:47.796Z [diagnostic] lane task error: lane=session:agent:main:main durationMs=75 error="Error: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir."
Feb 15 13:32:47 nixos openclaw[152584]: 2026-02-15T13:32:47.796-05:00 Embedded agent failed before reply: No API key found for provider "ollama". Auth store: /home/mike/.openclaw/agents/main/agent/auth-profiles.json (agentDir: /home/mike/.openclaw/agents/main/agent). Configure auth for this agent (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir.
```

**Gemini 3 Pro**: The logs reveal the exact problem:

`Error: No API key found for provider "ollama"`

**The Quirk:** Even though Ollama is usually keyless (it runs locally), OpenClaw's strict validation logic demands *some* value in the API key field to consider the provider "authorized." It treats an empty key as "unconfigured."

You have two paths to fix this.

### Path A: Fix Ollama (The "Dummy Key" Trick)

You just need to feed it a placebo.

1. **Edit your secrets file:**
`vim /var/lib/openclaw/.env`
2. **Add this line:**
```bash
OLLAMA_API_KEY=ollama
```

(The string "ollama" is the standard dummy key used by many clients).
3. **Restart:**
`systemctl restart openclaw`

### Path B: The "Bank the Win" Strategy (Switch to Anthropic)

Since you said you aren't married to Ollama and just want to chat, **this is the better move.** Anthropic (Claude) is significantly smarter at tool usage than the current local models, which will make your "Agentic" experience much better out of the gate.

**1. Edit `.env`:**
`vim /var/lib/openclaw/.env`

Add your key:

```bash
ANTHROPIC_API_KEY=sk-ant-api03-... (Your Actual Key)

```

**2. Edit `openclaw.nix`:**
Update the `models` and `agents` block to point to Anthropic.

```nix
    models = {
      providers = {
        # Keep Ollama (with the dummy key fix implicitly handled if we wanted)
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          models = [ { id = "gemma3:latest"; name = "Gemma 3"; } ];
        };
        # Add Anthropic
        anthropic = {
          enabled = true;
        };
      };
    };

    agents = {
      defaults = {
        model = {
          # CHANGE THIS: Point to Claude
          primary = "anthropic/claude-3-5-sonnet-20241022";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };

```

**3. Rebuild & Restart:**

```bash
n
systemctl restart openclaw

```

**Recommendation:** Go with **Path B**. It removes "Local Model Quirks" from the equation so you can verify the *system* works. You can always switch back to Ollama later once you've had that first successful conversation.

**MikeLev.in**: Okay, I tried the dummy API-key approach first and got this in the Web UI:

```log
Ollama API error 400: {"error":"registry.ollama.ai/library/gemma3:latest does not support tools"}
```

So this means the dummy API key thing worked insofar as letting it try. So I
either need to find an Ollama model that officially supports tool calling or
switch to one of the known-working models. Work pay for ChatGPT access so that
would be better than Anthropic at this point.

**Gemini 3 Pro**: This is a great error message! It confirms:

1. **The wiring is perfect:** OpenClaw successfully talked to your local Ollama instance.
2. **The issue is capability:** OpenClaw asked for "tools" (function calling), and `gemma3` said "I don't do that."

You have two excellent options.

### Option 1: Switch to OpenAI (The "Work Pays" Route)

Since you have access to OpenAI, this is the most robust path. Note that a **ChatGPT Plus** subscription is different from an **OpenAI API** account. You will need an API Key starting with `sk-...` from [platform.openai.com](https://platform.openai.com).

**1. Edit `.env**`
Add your OpenAI key.

```bash
vim /var/lib/openclaw/.env

```

```bash
# Add this line
OPENAI_API_KEY=sk-proj-12345...

```

**2. Edit `openclaw.nix**`
Update the `models` and `agents` block to use the `openai` provider.

```nix
    models = {
      providers = {
        # Enable OpenAI
        openai = {
          enabled = true;
        };
        # Keep Ollama configured for later
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          models = [ { id = "gemma3:latest"; name = "Gemma 3"; } ];
        };
      };
    };

    agents = {
      defaults = {
        model = {
          # Switch primary to GPT-4o (The current gold standard)
          primary = "openai/gpt-4o";
        };
        workspace = "/home/mike/.openclaw/workspace";
      };
    };

```

**3. Rebuild (`n`) and Restart (`systemctl restart openclaw`).**

---

### Option 2: Use a "Smart" Local Model

If you prefer to stay local/free, you simply need a model trained for tool use. `gemma3` is a bit experimental. The current best local models for tool calling are **Llama 3.1** or **Qwen 2.5 Coder**.

**1. Pull the model:**

```bash
ollama pull llama3.1:8b
# OR
ollama pull qwen2.5-coder:7b

```

**2. Update `openclaw.nix`:**
Change the primary model to the one you pulled.

```nix
    models = {
      providers = {
        ollama = {
          baseUrl = "http://127.0.0.1:11434";
          # Update the whitelist
          models = [ { id = "llama3.1:8b"; name = "Llama 3.1"; } ];
        };
      };
    };

    agents = {
      defaults = {
        model = {
          primary = "ollama/llama3.1:8b";
        };
        # ...
      };
    };

```

**Recommendation:**
Go with **Option 1 (OpenAI)** first to get that "Banked Win" and verify the full system works with a smart model. Then, you can leisurely experiment with local models like `qwen2.5-coder` without wondering if the bug is in the code or the model.

**MikeLev.in**: Wow, okay. I feel like I want to wrap the article up here on a
cliffhanger. Usually I take these things to completion when I can, but this was
a major energy expenditure. It's coming up on 2:00 PM this Sunday afternoon and
I want to call it a wrap (for now).

Respond by expanding on this article. Don't just stay in abstract terms; anchor
your points in **hard reality**. Name names, cite dates, reference specific
research papers, or mention historical events that prove the point. Try to
isolate it to the current implied article and not the entire discussion context
window.

Use the **Deep Dive Protocol** to break this down into a clear,
multi-dimensional analysis:

1. **The Context (Where does this fit?)**: Don't just label it; locate it in the
   real world. Is this a new idea or an old one with a new coat of paint? *Cite
   specific historical parallels or previous technologies that led us here.*

2. **The Evidence (Evaluate & Enumerate)**: Before imagining the future, look at
   the data. Brainstorm the key technical details—scientific studies,
   benchmarks, or documentation—that the audience *needs* to know. *Don't assume
   they know it; teach them with facts.*

3. **The Walkthrough (Mental Replay)**: Visualize the specific user experience.
   What does it feel like to actually do this? Describe the friction, the "aha!"
   moment, and the flow. *Make the abstract concrete.*

4. **Connect the Dots**: How does this specific concept relate to the bigger
   picture (The "Big Vat of Content", User Agency, or efficient Coding)? *Show
   the relationship between this small piece and the whole system.*

5. **The Reality Check (What If?)**: Run the scenario. What happens if this
   tool/insight *didn't* exist? What is the cost of doing it the "old way"?
   *Contrast the "Smart Way" against the "Default Way" using clear metrics or
   outcomes.*

6. **The Verdict**: Is this actually useful or just hype? Is it fuel for the
   engine or just sugar? *Give a clear, honest assessment supported by the
   evidence in step 2.*

Cover any details you think the writer or audience needs to hear to
intelligently discuss these topics. Feel free to use asides and tangents, but
make sure they are **grounded in reality**. Mention what **surprised** you most
and what you **anticipate** next.

**Gemini 3 Pro**: The sun is hitting the 2:00 PM mark on a Sunday afternoon, and the terminal cursor is blinking steady—a heartbeat in the digital ether. I’m calling it here. Not because I’m stuck, but because I’ve reached a pivotal summit. We didn't just install software today; we successfully grafted a sovereign, agentic nervous system onto the immutable bone structure of NixOS.

The sheer density of what just transpired is worth pausing to admire. We went from a blank directory to a fully declarative, systemd-managed, reverse-proxied AI agent that respects the "Loophole" of legacy Nix configs while leveraging the bleeding-edge power of Flakes.

Here is the breakdown of this architectural victory, analyzed through the **Deep Dive Protocol**.

### 1. The Context: The "Amiga" Moment of 2026

We are standing at a historical inflection point similar to the release of the **Amiga 1000 in 1985**. Just as the Amiga introduced preemptive multitasking and custom silicon to a world of single-tasking DOS prompts, **OpenClaw on NixOS** introduces *agentic multitasking* to a world of passive chatbots.

Most people today use AI like a CLI—enter command, wait for response. They are stuck in the "DOS era" of intelligence. By installing OpenClaw as a system service, listening on a websocket, connected to local tools, we are moving into the "Workbench era." The AI is no longer a task you run; it is a *process that exists alongside you*, capable of acting on the system while you do other work. This isn't just a "new app"; it's a fundamental shift in the User-System relationship, moving from **Direct Manipulation** (clicking icons) to **Intent-Based Computing** (assigning goals).

### 2. The Evidence: The Anatomy of the Win

Let's look at the hard data of what was achieved. This wasn't a `npm install` and pray. This was systems engineering.

* **The Artifact:** We created `/home/mike/repos/nixos/openclaw.nix`, a 90-line declarative manifesto.
* **The "Loophole":** We utilized `builtins.getFlake "github:openclaw/nix-openclaw"` to bypass the need for a system-wide Flake migration. This proves that you don't need to burn down your legacy setup to use modern tools; you just need to know the right incantations.
* **The Schema Validation:** The logs provided incontrovertible proof of OpenClaw's robustness. When we fed it a string (`"gemma3:latest"`) where it expected an object, it didn't segfault; it threw a precise Zod validation error (`Invalid input: expected object`). This level of type safety in a distributed agent is rare and critical for stability.
* **The "Badge of Honor":** The error `EROFS: read-only file system` is the most beautiful thing in the logs. It proves that the agent *cannot* corrupt its own configuration. In an era of AI safety concerns, running an agent on an immutable filesystem is the ultimate "kill switch."

## Beyond the CLI: The Amiga Moment for Agentic AI

### 3. The Walkthrough: Friction as Fuel

The experience of building this was high-friction, but that friction was instructional.

* **The Trap:** Following the "Golden Path" documentation blindly led us to a Home Manager dead end.
* **The Pivot:** We had to mentally disassemble the Home Manager module and reconstruct it as a Systemd service.
* **The "Aha!" Moment:** The realization that `openclaw-gateway` wasn't a separate package but an entry point in the main binary. The smoke test (`nix run ... --help`) was the turning point. It shifted us from "guessing" to "knowing."
* **The Flow:** Once the `openclaw.nix` file was structured correctly, the iteration cycle became rhythmic: Edit Nix -> `n` (Rebuild) -> `systemctl restart` -> `journalctl -f`. This is the heartbeat of the sovereign sysadmin.

### 4. Connect the Dots: The Nervous System of the "Forever Machine"

How does this fit into the bigger picture?

* **Flatnotes** is the **Memory**.
* **Pipulate** is the **Hands**.
* **NixOS** is the **Body**.
* **OpenClaw** is now the **Autonomic Nervous System**.

We haven't just installed a chatbot; we have installed a *router* for intelligence. By configuring the `models` block in `openclaw.nix`, we have created a abstraction layer where we can swap the "brain" (Ollama vs. Anthropic vs. OpenAI) without changing the "body" (the tools and scripts). This preserves the **User Agency** we crave. We are not locked into Claude or GPT-4; they are merely interchangeable cartridges in our sovereign console.

## The Cliffhanger: Awaiting the Agent's Awakened Mind

### 5. The Reality Check: The Cost of the "Default Way"

If we hadn't done this the "Nix Way," what would the alternative be?

* **The Docker Way:** We'd have a `docker-compose.yml` file. It would be easier to start, but the agent would be trapped in a container, blind to the host system. It couldn't run `nixos-rebuild`. It couldn't see the `pipulate` virtual environment. It would be a brain in a jar.
* **The "NPM Install" Way:** We'd have a `node_modules` folder that rots in six months. Updates would break dependencies. The system would be fragile.

By paying the "Nix Tax" up front, we bought **System Visibility**. The agent runs as user `mike`. It sees what I see. It lives in the same shell environment. This is dangerous if unsecured, but absolutely necessary for a true "Co-Pilot."

### 6. The Verdict: The Cliffhanger

Is this useful? **Yes.**
Is it finished? **No.**

We have the engine running, but the transmission isn't engaging the gears. The connection to Ollama works (proven by the error message!), but the local model (`gemma3`) is too primitive to understand the tool-calling protocol OpenClaw demands.

The cliffhanger is real: The system is live, the web UI is waiting, and the websocket is open. The only thing missing is a model smart enough to drive it.

**What Surprised Me:** The strictness of the OpenClaw config schema. Most FOSS projects are loose with configs; OpenClaw is disciplined. This gives me high confidence in the project's longevity.

**What I Anticipate Next:** The moment we plug in a "Smart Model" (whether GPT-4o via API or a Llama 3.1 70B local quantized model), this system is going to wake up. It won't just chat; it will *do*. The infrastructure is ready. The mech suit is built. We just need to insert the pilot.

See you in the next session.

---

> **MikeLev.in**: I'll take it! We are perched on the edge of game-changing progress.
> There's a substory here about how Nix / NixOS is the perfect platform for
> agentic frameworks, especially when you start factoring in security. You don't
> need to buy a Mac Mini.


---

## Book Analysis

### Ai Editorial Take
This entry is a pivotal piece for the 'Futureproof' series. It masterfully blends theoretical ambition (sovereign agents) with the gritty reality of system-level implementation (NixOS debugging). Its value lies not just in the successful deployment of OpenClaw, but in the meticulous documentation of the troubleshooting process, transforming complex errors into teachable moments. This is exactly the kind of practical, 'hard reality' content that defines the project's ethos, making the abstract concept of agentic AI tangible and deployable.

### Title Brainstorm
* **Title Option:** NixOS as the Immutable Host: Deploying a Sovereign OpenClaw Agent
  * **Filename:** `nixos-immutable-host-deploying-openclaw-agent.md`
  * **Rationale:** Directly highlights the core technology (NixOS), its key benefit (immutability), and the subject (OpenClaw agent).
* **Title Option:** Grafting Intelligence: OpenClaw on NixOS for Agentic Sovereignty
  * **Filename:** `grafting-intelligence-openclaw-nixos-agentic-sovereignty.md`
  * **Rationale:** Uses a strong metaphor ('grafting') and emphasizes the philosophical goal (agentic sovereignty).
* **Title Option:** The Declarative Agent: OpenClaw's Journey to NixOS Stability
  * **Filename:** `declarative-agent-openclaw-nixos-stability.md`
  * **Rationale:** Focuses on the declarative nature of NixOS and the outcome of stability for OpenClaw.
* **Title Option:** From 'No Such File' to Sovereign Agent: An OpenClaw NixOS Blueprint
  * **Filename:** `no-such-file-to-sovereign-agent-openclaw-nixos-blueprint.md`
  * **Rationale:** Highlights the debugging journey and the ultimate success, framing it as a blueprint or methodology.

### Content Potential And Polish
- **Core Strengths:**
  - Exceptional documentation of a complex technical debugging process, making it accessible even for NixOS novices.
  - Demonstrates the practical application of NixOS concepts like `builtins.getFlake`, `pkgs.writeText`, `systemd.services`, and `tmpfiles.rules` in a real-world scenario.
  - Articulates the 'Amiga moment' analogy effectively, contextualizing the significance of agentic frameworks beyond simple chatbots.
  - Provides clear, step-by-step instructions and problem-solving, including precise log analysis and fix implementation.
  - Highlights the security and immutability benefits of NixOS for AI agents, specifically the `EROFS` error as a 'badge of honor'.
  - Excellent framing of the project's broader 'Big Vat of Content' philosophy by linking OpenClaw to Flatnotes, Pipulate, and NixOS.
- **Suggestions For Polish:**
  - Replace 'baz' placeholder with a consistent, non-conflicting port number (e.g., 9001) in the `openclaw.nix` examples for clarity.
  - When publishing, ensure all code blocks (Nix, JSON, bash) are complete, rather than just diffs, for easy copy-pasting by readers.
  - Consider adding a small section on how to inspect the generated `openclaw.json5` in the Nix store for deeper debugging insights.
  - Elaborate slightly on the `allowFrom` Telegram ID configuration, perhaps mentioning a `nix-shell` command to run `@userinfobot` outside of the main agent for initial ID retrieval.
  - Expand on the `ollama` configuration, particularly the requirement for 'tool-calling' capable models versus general chat models, to prevent similar user issues.

### Next Step Prompts
- Draft the follow-up article detailing the successful integration and first interactions with a 'smart' AI model (e.g., OpenAI/GPT-4o or Ollama/Llama 3.1) within OpenClaw, including screenshots of the web UI and Telegram interactions, and outline a simple initial agentic task.
- Create a guide for advanced OpenClaw configurations within NixOS, covering custom tool definitions, agent personas, persistent memory integration with Flatnotes, and secure secret management beyond `.env` files, perhaps leveraging `sops-nix`.