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

The SettingsManager persists all tray configuration to a single 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 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 loads settings.json during construction, normalizes missing or legacy values, and raises a Saved event after every successful write.

// 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.

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.

// 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 from one checkout never interferes with another.

Unit Test Validation in SettingsManagerIsolationTests.cs

The test suite in 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.

// 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

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. Every public auto-property on that record becomes a top-level property in 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.

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. Subscribers can hook this event to react to configuration changes in real time.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →