# How openclaw-windows-node Uses SettingsManager JSON Schema and Worktree Isolation for Per-Checkout Configuration

> Discover how openclaw-windows-node uses SettingsManager JSON schema and worktree isolation with OPENCLAW_TRAY_DATA_DIR for per-checkout configuration. Learn more!

- Repository: [openclaw/openclaw-windows-node](https://github.com/openclaw/openclaw-windows-node)
- Tags: deep-dive
- Published: 2026-06-05

---

**The `SettingsManager` persists all tray configuration to a single [`settings.json`](https://github.com/openclaw/openclaw-windows-node/blob/main/settings.json) file shaped by the `SettingsData` record, and it isolates each Git worktree by overriding the storage directory via the `OPENCLAW_TRAY_DATA_DIR` environment variable.**

The `openclaw-windows-node` repository manages Windows tray configuration through a centralized **`SettingsManager`** that reads and writes a strongly typed JSON file. Understanding the exact JSON schema and the worktree isolation mechanism is essential for developers running multiple checkouts or CI pipelines side by side.

## SettingsManager JSON Schema Defined by `SettingsData`

The canonical schema lives in [`src/OpenClaw.Shared/SettingsData.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Shared/SettingsData.cs) as the **`SettingsData`** record. Every public auto-property maps directly to a JSON property, and the **`System.Text.Json`** serializer writes the file with indented formatting while omitting `null` values.

The key properties and their defaults are:

- **`GatewayUrl`** — WebSocket gateway URL, defaults to `"ws://localhost:18789"`.
- **`UseSshTunnel`** — Boolean flag defaulting to `false`; when enabled, the related tunnel properties configure the connection:
  - `SshTunnelUser` and `SshTunnelHost` default to `""`.
  - `SshTunnelRemotePort` and `SshTunnelLocalPort` both default to `18789`.
- **`AutoStart`** — `true`, controls whether the tray launches on user login.
- **`GlobalHotkeyEnabled`** — `true`, toggles global hot-key shortcuts.
- **`ShowNotifications`** — `true`, with `NotificationSound` defaulting to `"Default"`.
- **Notification filters** — `NotifyHealth`, `NotifyUrgent`, and `NotifyInfo` each default to `true` for fine-grained toast control.
- **`EnableNodeMode`** — `false`, runs the gateway-client node locally when set to `true`.
- **`EnableMcpServer`** — `false`, runs the local MCP HTTP server when enabled.
- **`TtsProvider`** — Defaults to `"Piper"`; the `TtsElevenLabsApiKey` property stores a **DPAPI-protected** string prefixed with `dpapi:`.
- **`A2UIImageHosts`** — Optional list of allowed HTTPS hosts for the A2UI image renderer.
- **Sandbox settings** — `SandboxClipboard`, `SandboxDocumentsAccess`, and `SandboxTimeoutMs` (`30000`) configure the MXC sandbox.
- **`PreferredGatewayId`** — Optional preferred gateway identifier, defaults to `null`.
- **`McpOnlyMode`** — Used only for migrating older settings; it is **not persisted** to disk.

## How `SettingsManager` Persists and Reads Settings

The `SettingsManager` class in [`src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs) loads [`settings.json`](https://github.com/openclaw/openclaw-windows-node/blob/main/settings.json) during construction, normalizes missing or legacy values, and raises a **`Saved`** event after every successful write.

```csharp
// src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs
var settings = new SettingsManager();
settings.AutoStart = false;
settings.Save(); // Persists to disk and fires the Saved event

```

Sensitive values are protected before serialization. Calling **`SettingsManager.ProtectSettingSecret`** encrypts strings with Windows DPAPI and prefixes them with `dpapi:` so they can be stored safely inside the JSON file.

```csharp
string apiKey = "my-elevenlabs-key";
string protectedValue = SettingsManager.ProtectSettingSecret(apiKey);
// Result: a dpapi:-prefixed string written to settings.json

```

## Worktree Isolation via `OPENCLAW_TRAY_DATA_DIR`

By default, the manager resolves the settings directory to `%APPDATA%\OpenClawTray`. To prevent separate Git worktrees or CI jobs from overwriting the same file, the **`GetDefaultSettingsDirectory`** method checks for the `OPENCLAW_TRAY_DATA_DIR` environment variable and uses that path when present.

```csharp
// src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs
private static string GetDefaultSettingsDirectory()
{
    return Environment.GetEnvironmentVariable("OPENCLAW_TRAY_DATA_DIR") is { Length: > 0 } overrideDir
        ? overrideDir
        : Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "OpenClawTray");
}

```

Each worktree can export its own directory before launching the tray, guaranteeing that [`settings.json`](https://github.com/openclaw/openclaw-windows-node/blob/main/settings.json) from one checkout never interferes with another.

## Unit Test Validation in [`SettingsManagerIsolationTests.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/SettingsManagerIsolationTests.cs)

The test suite in [`tests/OpenClaw.Tray.Tests/SettingsManagerIsolationTests.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/tests/OpenClaw.Tray.Tests/SettingsManagerIsolationTests.cs) programmatically proves this isolation. It creates a temporary folder, sets `OPENCLAW_TRAY_DATA_DIR` to that folder, instantiates a `SettingsManager`, and verifies that all reads and writes occur inside the overridden path rather than the global `%APPDATA%` location.

```csharp
// tests/OpenClaw.Tray.Tests/SettingsManagerIsolationTests.cs
var customDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(customDir);

Environment.SetEnvironmentVariable("OPENCLAW_TRAY_DATA_DIR", customDir);

var isolated = new SettingsManager();
isolated.EnableNodeMode = true;
isolated.Save();

Assert.True(File.Exists(Path.Combine(customDir, "settings.json")));

```

## Summary

- The **JSON schema** is defined entirely by the `SettingsData` record in [`src/OpenClaw.Shared/SettingsData.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Shared/SettingsData.cs), serialized with `System.Text.Json`.
- **Default values** cover gateway URLs, SSH tunneling, notifications, TTS providers, and sandbox configuration.
- **Secrets** such as `TtsElevenLabsApiKey` are DPAPI-protected via `SettingsManager.ProtectSettingSecret`.
- `SettingsManager` lives in [`src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs) and fires a `Saved` event after each write.
- **Worktree isolation** relies on the `OPENCLAW_TRAY_DATA_DIR` environment variable, which overrides `%APPDATA%\OpenClawTray`.
- The behavior is enforced by unit tests in [`tests/OpenClaw.Tray.Tests/SettingsManagerIsolationTests.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/tests/OpenClaw.Tray.Tests/SettingsManagerIsolationTests.cs).

## Frequently Asked Questions

### What file defines the SettingsManager JSON schema in openclaw-windows-node?

The schema is defined by the `SettingsData` record in [`src/OpenClaw.Shared/SettingsData.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Shared/SettingsData.cs). Every public auto-property on that record becomes a top-level property in [`settings.json`](https://github.com/openclaw/openclaw-windows-node/blob/main/settings.json).

### How does openclaw-windows-node keep settings isolated between Git worktrees?

Isolation is achieved through the `OPENCLAW_TRAY_DATA_DIR` environment variable. When set, `SettingsManager.GetDefaultSettingsDirectory()` routes storage to that custom path instead of `%APPDATA%\OpenClawTray`, so each worktree maintains its own [`settings.json`](https://github.com/openclaw/openclaw-windows-node/blob/main/settings.json).

### Are secrets like API keys stored securely in settings.json?

Yes. The `SettingsManager.ProtectSettingSecret` method encrypts sensitive strings with Windows DPAPI and writes them with a `dpapi:` prefix. Only the same user profile can decrypt the value later.

### What event signals that settings have been successfully saved?

`SettingsManager` raises the `Saved` event immediately after a successful write to [`settings.json`](https://github.com/openclaw/openclaw-windows-node/blob/main/settings.json). Subscribers can hook this event to react to configuration changes in real time.