@darkelogix/openclaw-trusted-mode 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -12,6 +12,16 @@ Terminology and acronyms: [`GLOSSARY.md`](./GLOSSARY.md).
12
12
  ## Unreleased
13
13
  - No unreleased changes recorded.
14
14
 
15
+ ## v1.0.6
16
+ - Add a runtime fallback that reads `plugins.entries.openclaw-trusted-mode.config` from `OPENCLAW_CONFIG_PATH` or `~/.openclaw/openclaw.json` when OpenClaw does not populate `api.config` on the hook-only plugin path.
17
+ - Preserve governed-mode settings written by `openclaw-trusted-mode-configure` even on OpenClaw builds that load the plugin with incomplete in-memory config injection.
18
+ - Publish the runtime fallback module in the npm tarball and cover it with adapter tests.
19
+
20
+ ## v1.0.5
21
+ - Add `openclaw-trusted-mode-configure`, a dedicated guided config writer for governed OpenClaw hosts that updates `~/.openclaw/openclaw.json` with `plugins.allow`, tenant, gateway, environment, PDP URL, and fail-closed settings.
22
+ - Remove the remaining manual-JSON ambiguity from the package docs by documenting the new configure command for governed mode.
23
+ - Keep the package contract explicit by publishing the new configure CLI and its config-writer module in the npm tarball.
24
+
15
25
  ## v1.0.4
16
26
  - Make the public npm package install-safe for current OpenClaw builds by separating Trusted Mode Check environment reads from PDP network calls in the shipped CLI entrypoint.
17
27
  - Align `openclaw.plugin.json` with the published package version and keep the required helper CLI modules in the npm tarball.
package/README.md CHANGED
@@ -128,6 +128,19 @@ openclaw plugins info openclaw-trusted-mode
128
128
 
129
129
  For a standalone free-mode config, start from [`openclaw.user-config.entry.example.json`](./openclaw.user-config.entry.example.json).
130
130
 
131
+ For governed mode, install/register the plugin first, then write the OpenClaw host config with:
132
+
133
+ ```bash
134
+ openclaw-trusted-mode-configure \
135
+ --tenantId darkelogix \
136
+ --gatewayId gw-dev \
137
+ --environment dev \
138
+ --pdpUrl http://10.90.0.6:8001/v1/authorize \
139
+ --certificationStatus LOCKDOWN_ONLY
140
+ ```
141
+
142
+ This command updates `~/.openclaw/openclaw.json`, adds `openclaw-trusted-mode` to `plugins.allow`, and writes the governed plugin settings under `plugins.entries.openclaw-trusted-mode`.
143
+
131
144
  ## Plugin config
132
145
 
133
146
  See [`openclaw.plugin.json`](./openclaw.plugin.json) for config schema and defaults, including:
@@ -165,6 +178,8 @@ Recommended paid / PDP-backed baseline:
165
178
  "toolPolicyMode": "PDP",
166
179
  "pdpUrl": "http://localhost:8001/v1/authorize",
167
180
  "tenantId": "trial-tenant",
181
+ "gatewayId": "gw-smoke-1",
182
+ "environment": "prod",
168
183
  "failClosed": true,
169
184
  "certificationStatus": "LOCKDOWN_ONLY"
170
185
  }
@@ -198,4 +213,3 @@ npm run bundle-release-evidence
198
213
  npm run startup-health-check -- --skip-plugin-check
199
214
  ```
200
215
 
201
-
@@ -54,8 +54,8 @@ If not:
54
54
  Set-Location <openclaw-trusted-mode-path>
55
55
  npm install
56
56
  npm run build
57
- openclaw plugins install <openclaw-trusted-mode-path> --no-color
58
- openclaw plugins info openclaw-trusted-mode --no-color
57
+ openclaw plugins install <openclaw-trusted-mode-path>
58
+ openclaw plugins info openclaw-trusted-mode
59
59
  ```
60
60
 
61
61
  Expected: plugin status is `loaded`.
@@ -104,7 +104,24 @@ Check in order:
104
104
  3. If using WSL, test `host.docker.internal` endpoint.
105
105
  4. Keep `failClosed=true` in production.
106
106
 
107
- ## 6) License endpoint or support placeholders are unresolved
107
+ ## 6) `[Trusted Mode ERROR] Hardening config invalid`
108
+
109
+ Meaning: OpenClaw loaded the plugin, but `~/.openclaw/openclaw.json` still has an incomplete plugin config.
110
+
111
+ For governed mode, rewrite the host config with:
112
+
113
+ ```powershell
114
+ openclaw-trusted-mode-configure --tenantId <tenant-id> --gatewayId <gateway-id> --environment <environment> --pdpUrl http://<guard-pro-host>:8001/v1/authorize --certificationStatus LOCKDOWN_ONLY
115
+ ```
116
+
117
+ That command:
118
+
119
+ 1. Adds `openclaw-trusted-mode` to `plugins.allow`.
120
+ 2. Sets `toolPolicyMode=PDP`.
121
+ 3. Writes `tenantId`, `gatewayId`, `environment`, `pdpUrl`, `failClosed=true`, and `allowedTenantIds=[tenantId]`.
122
+ 4. Replaces stale standalone `ALLOWLIST_ONLY` defaults that can block governed startup.
123
+
124
+ ## 7) License endpoint or support placeholders are unresolved
108
125
 
109
126
  Define and verify the values from `START_HERE.md` section "Remaining org-specific values to fill once":
110
127
 
@@ -117,7 +134,7 @@ After setting values:
117
134
  1. Re-run licensing/activation check (`sde-cli license status`).
118
135
  2. Re-run pilot or production readiness checklist.
119
136
 
120
- ## 7) `ENTITLEMENT_DENIED` for expected active tenant
137
+ ## 8) `ENTITLEMENT_DENIED` for expected active tenant
121
138
 
122
139
  1. Inspect `ops/entitlements.json` for exact tenant key and SKU.
123
140
  2. Confirm request `tenant_id` matches exactly.
@@ -129,7 +146,7 @@ Set-Location <sde-enterprise-path>
129
146
  python ops\pdp_admin_cli.py authorize-test --tenant-id <tenant_id> --tool-name read_file --gateway-id gw-1 --environment prod
130
147
  ```
131
148
 
132
- ## 8) Policy pack signature verification errors
149
+ ## 9) Policy pack signature verification errors
133
150
 
134
151
  1. Confirm both files exist for active variant:
135
152
  - `<variant>.json`
package/START_HERE.md CHANGED
@@ -110,13 +110,13 @@ Copy-Item <sde-enterprise-path>\.env.example <sde-enterprise-path>\.env -Force
110
110
 
111
111
  Use this path if you want practical local hardening without SDE PDP.
112
112
 
113
- ```powershell
114
- Set-Location <openclaw-trusted-mode-path>
115
- npm install
116
- npm run build
117
- openclaw plugins install <openclaw-trusted-mode-path> --no-color
118
- openclaw plugins info openclaw-trusted-mode --no-color
119
- ```
113
+ ```powershell
114
+ Set-Location <openclaw-trusted-mode-path>
115
+ npm install
116
+ npm run build
117
+ openclaw plugins install <openclaw-trusted-mode-path>
118
+ openclaw plugins info openclaw-trusted-mode
119
+ ```
120
120
 
121
121
  Expected: plugin status is `loaded`.
122
122
 
@@ -152,15 +152,16 @@ Expected:
152
152
 
153
153
  ## 6) Build and install plugin for SDE-backed mode
154
154
 
155
- ```powershell
156
- Set-Location <openclaw-trusted-mode-path>
157
- npm install
158
- npm run build
159
- openclaw plugins install <openclaw-trusted-mode-path> --no-color
160
- openclaw plugins info openclaw-trusted-mode --no-color
161
- ```
162
-
163
- Expected: plugin status is `loaded`.
155
+ ```powershell
156
+ Set-Location <openclaw-trusted-mode-path>
157
+ npm install
158
+ npm run build
159
+ openclaw plugins install <openclaw-trusted-mode-path>
160
+ openclaw plugins info openclaw-trusted-mode
161
+ openclaw-trusted-mode-configure --tenantId <tenant-id> --gatewayId <gateway-id> --environment <environment> --pdpUrl http://<guard-pro-host>:8001/v1/authorize --certificationStatus LOCKDOWN_ONLY
162
+ ```
163
+
164
+ Expected: plugin status is `loaded`.
164
165
 
165
166
  ## 7) Run first-success smoke tests
166
167
 
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.parseConfigureCliArgs = parseConfigureCliArgs;
5
+ exports.runConfigureCli = runConfigureCli;
6
+ const node_process_1 = require("node:process");
7
+ const node_os_1 = require("node:os");
8
+ const node_path_1 = require("node:path");
9
+ const openclawConfig_1 = require("./openclawConfig");
10
+ const runtimeCertification_1 = require("./runtimeCertification");
11
+ function printHelp() {
12
+ console.log(`Configure ${openclawConfig_1.PLUGIN_ID} governed mode in OpenClaw.
13
+
14
+ Usage:
15
+ openclaw-trusted-mode-configure --tenantId <tenant> --gatewayId <gateway> --environment <environment> --pdpUrl <url> [options]
16
+
17
+ Options:
18
+ --policyVariant <variant> Policy variant to store in plugin config.
19
+ Default: guard-pro.v2026.02
20
+ --configPath <path> OpenClaw config path.
21
+ Default: ${openclawConfig_1.DEFAULT_OPENCLAW_CONFIG_PATH}
22
+ --certificationStatus <status> CERTIFIED_ENFORCED | LOCKDOWN_ONLY | UNSUPPORTED
23
+ Default: LOCKDOWN_ONLY
24
+ --pdpTimeoutMs <ms> PDP timeout to store in plugin config.
25
+ Default: 5000
26
+ --failOpen Write failClosed=false instead of true.
27
+ --json Print a JSON summary instead of prose.
28
+ --help Show this help text.
29
+ `);
30
+ }
31
+ function expandHomePath(value) {
32
+ if (value === "~")
33
+ return (0, node_os_1.homedir)();
34
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
35
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), value.slice(2));
36
+ }
37
+ return value;
38
+ }
39
+ function readFlagValue(argv, flag) {
40
+ const index = argv.indexOf(flag);
41
+ if (index === -1)
42
+ return undefined;
43
+ const value = argv[index + 1];
44
+ if (!value || value.startsWith("--")) {
45
+ throw new Error(`Missing value for ${flag}`);
46
+ }
47
+ return value;
48
+ }
49
+ function parseConfigureCliArgs(argv) {
50
+ if (argv.includes("--help")) {
51
+ printHelp();
52
+ (0, node_process_1.exit)(0);
53
+ }
54
+ const tenantId = readFlagValue(argv, "--tenantId");
55
+ const gatewayId = readFlagValue(argv, "--gatewayId");
56
+ const environment = readFlagValue(argv, "--environment");
57
+ const pdpUrl = readFlagValue(argv, "--pdpUrl");
58
+ const policyVariant = readFlagValue(argv, "--policyVariant") || "guard-pro.v2026.02";
59
+ const configPath = expandHomePath(readFlagValue(argv, "--configPath") || (0, node_path_1.join)((0, node_os_1.homedir)(), ".openclaw", "openclaw.json"));
60
+ const certificationStatus = (0, runtimeCertification_1.normalizeRuntimeCertificationStatus)(readFlagValue(argv, "--certificationStatus") || "LOCKDOWN_ONLY");
61
+ const timeoutRaw = readFlagValue(argv, "--pdpTimeoutMs");
62
+ const pdpTimeoutMs = timeoutRaw ? Number(timeoutRaw) : 5000;
63
+ const failClosed = !argv.includes("--failOpen");
64
+ const jsonMode = argv.includes("--json");
65
+ const missing = [
66
+ ["--tenantId", tenantId],
67
+ ["--gatewayId", gatewayId],
68
+ ["--environment", environment],
69
+ ["--pdpUrl", pdpUrl],
70
+ ].filter(([, value]) => !value);
71
+ if (missing.length > 0) {
72
+ throw new Error(`Missing required flags: ${missing.map(([flag]) => flag).join(", ")}`);
73
+ }
74
+ if (!Number.isFinite(pdpTimeoutMs) || pdpTimeoutMs <= 0) {
75
+ throw new Error("--pdpTimeoutMs must be a positive number");
76
+ }
77
+ return {
78
+ tenantId: tenantId,
79
+ gatewayId: gatewayId,
80
+ environment: environment,
81
+ pdpUrl: pdpUrl,
82
+ policyVariant,
83
+ configPath,
84
+ certificationStatus,
85
+ failClosed,
86
+ pdpTimeoutMs,
87
+ jsonMode,
88
+ };
89
+ }
90
+ function runConfigureCli(argv = process.argv.slice(2)) {
91
+ try {
92
+ const options = parseConfigureCliArgs(argv);
93
+ const result = (0, openclawConfig_1.writeGovernedConfig)(options);
94
+ const output = {
95
+ pluginId: openclawConfig_1.PLUGIN_ID,
96
+ configPath: result.configPath,
97
+ created: result.created,
98
+ pluginAllowAdded: result.pluginAllowAdded,
99
+ mode: "governed",
100
+ values: {
101
+ tenantId: options.tenantId,
102
+ gatewayId: options.gatewayId,
103
+ environment: options.environment,
104
+ pdpUrl: options.pdpUrl,
105
+ policyVariant: options.policyVariant,
106
+ certificationStatus: options.certificationStatus,
107
+ failClosed: options.failClosed,
108
+ pdpTimeoutMs: options.pdpTimeoutMs,
109
+ },
110
+ };
111
+ if (options.jsonMode) {
112
+ console.log(JSON.stringify(output, null, 2));
113
+ return;
114
+ }
115
+ console.log(`Configured ${openclawConfig_1.PLUGIN_ID} governed mode at ${result.configPath}`);
116
+ console.log(`- plugins.allow includes ${openclawConfig_1.PLUGIN_ID}`);
117
+ console.log(`- tenantId=${options.tenantId}`);
118
+ console.log(`- gatewayId=${options.gatewayId}`);
119
+ console.log(`- environment=${options.environment}`);
120
+ console.log(`- pdpUrl=${options.pdpUrl}`);
121
+ console.log(`- toolPolicyMode=PDP`);
122
+ console.log(`- failClosed=${options.failClosed}`);
123
+ console.log(`- certificationStatus=${options.certificationStatus}`);
124
+ }
125
+ catch (error) {
126
+ console.error(error.message);
127
+ console.error(`Run "openclaw-trusted-mode-configure --help" for usage.`);
128
+ (0, node_process_1.exit)(1);
129
+ }
130
+ }
131
+ if (require.main === module) {
132
+ runConfigureCli();
133
+ }
package/dist/index.js CHANGED
@@ -5,9 +5,10 @@ const constraints_1 = require("./constraints");
5
5
  const contextCurator_1 = require("./contextCurator");
6
6
  const runtimeCertification_1 = require("./runtimeCertification");
7
7
  const hardening_1 = require("./hardening");
8
+ const runtimePluginConfig_1 = require("./runtimePluginConfig");
8
9
  const sdeGuidance_1 = require("./sdeGuidance");
9
10
  function register(api) {
10
- const config = api.config;
11
+ const config = (0, runtimePluginConfig_1.mergeDefinedConfig)((0, runtimePluginConfig_1.readRuntimePluginConfig)(), (api.config || {}));
11
12
  const pdpUrl = config.pdpUrl || 'http://localhost:8001/v1/authorize';
12
13
  const policyVariant = config.policyVariant || 'guard-pro.v2026.02';
13
14
  const pdpTimeoutMs = typeof config.pdpTimeoutMs === 'number' ? config.pdpTimeoutMs : 5000;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_OPENCLAW_CONFIG_PATH = exports.PLUGIN_ID = void 0;
4
+ exports.normalizeConfigText = normalizeConfigText;
5
+ exports.parseOpenClawConfig = parseOpenClawConfig;
6
+ exports.configureGovernedPlugin = configureGovernedPlugin;
7
+ exports.writeGovernedConfig = writeGovernedConfig;
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = require("node:path");
10
+ exports.PLUGIN_ID = "openclaw-trusted-mode";
11
+ exports.DEFAULT_OPENCLAW_CONFIG_PATH = "~/.openclaw/openclaw.json";
12
+ function ensureObject(value) {
13
+ if (value && typeof value === "object" && !Array.isArray(value)) {
14
+ return value;
15
+ }
16
+ return {};
17
+ }
18
+ function normalizeConfigText(text) {
19
+ return `${text.trimEnd()}\n`;
20
+ }
21
+ function parseOpenClawConfig(raw) {
22
+ const parsed = JSON.parse(raw);
23
+ return ensureObject(parsed);
24
+ }
25
+ function configureGovernedPlugin(baseConfig, input) {
26
+ const nextConfig = ensureObject(baseConfig);
27
+ const plugins = ensureObject(nextConfig.plugins);
28
+ const existingAllow = Array.isArray(plugins.allow) ? [...plugins.allow] : [];
29
+ const allow = existingAllow.filter((item) => typeof item === "string");
30
+ const pluginAllowAdded = !allow.includes(exports.PLUGIN_ID);
31
+ if (pluginAllowAdded) {
32
+ allow.push(exports.PLUGIN_ID);
33
+ }
34
+ const entries = ensureObject(plugins.entries);
35
+ const existingEntry = ensureObject(entries[exports.PLUGIN_ID]);
36
+ const existingPluginConfig = ensureObject(existingEntry.config);
37
+ const nextPluginConfig = {
38
+ ...existingPluginConfig,
39
+ pdpUrl: input.pdpUrl,
40
+ policyVariant: input.policyVariant,
41
+ pdpTimeoutMs: input.pdpTimeoutMs,
42
+ failClosed: input.failClosed,
43
+ tenantId: input.tenantId,
44
+ gatewayId: input.gatewayId,
45
+ environment: input.environment,
46
+ toolPolicyMode: "PDP",
47
+ requireTenantId: true,
48
+ allowedTenantIds: [input.tenantId],
49
+ certificationStatus: input.certificationStatus,
50
+ };
51
+ delete nextPluginConfig.allowedTools;
52
+ entries[exports.PLUGIN_ID] = {
53
+ ...existingEntry,
54
+ enabled: true,
55
+ config: nextPluginConfig,
56
+ };
57
+ plugins.allow = allow;
58
+ plugins.entries = entries;
59
+ nextConfig.plugins = plugins;
60
+ return {
61
+ config: nextConfig,
62
+ pluginAllowAdded,
63
+ };
64
+ }
65
+ function writeGovernedConfig(input) {
66
+ let parsed = {};
67
+ let created = false;
68
+ try {
69
+ parsed = parseOpenClawConfig((0, node_fs_1.readFileSync)(input.configPath, "utf8"));
70
+ }
71
+ catch (error) {
72
+ const code = error?.code;
73
+ if (code === "ENOENT") {
74
+ created = true;
75
+ }
76
+ else {
77
+ throw error;
78
+ }
79
+ }
80
+ const { config, pluginAllowAdded } = configureGovernedPlugin(parsed, input);
81
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(input.configPath), { recursive: true });
82
+ (0, node_fs_1.writeFileSync)(input.configPath, normalizeConfigText(JSON.stringify(config, null, 2)), "utf8");
83
+ return {
84
+ configPath: input.configPath,
85
+ created,
86
+ pluginAllowAdded,
87
+ };
88
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveOpenClawConfigPath = resolveOpenClawConfigPath;
4
+ exports.readRuntimePluginConfig = readRuntimePluginConfig;
5
+ exports.mergeDefinedConfig = mergeDefinedConfig;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_os_1 = require("node:os");
8
+ const node_path_1 = require("node:path");
9
+ const PLUGIN_ID = "openclaw-trusted-mode";
10
+ function ensureObject(value) {
11
+ if (value && typeof value === "object" && !Array.isArray(value)) {
12
+ return value;
13
+ }
14
+ return {};
15
+ }
16
+ function resolveOpenClawConfigPath(env = process.env) {
17
+ if (env.OPENCLAW_CONFIG_PATH)
18
+ return env.OPENCLAW_CONFIG_PATH;
19
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), ".openclaw", "openclaw.json");
20
+ }
21
+ function readRuntimePluginConfig(env = process.env) {
22
+ const configPath = resolveOpenClawConfigPath(env);
23
+ if (!(0, node_fs_1.existsSync)(configPath)) {
24
+ return {};
25
+ }
26
+ try {
27
+ const root = ensureObject(JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf8")));
28
+ const plugins = ensureObject(root.plugins);
29
+ const entries = ensureObject(plugins.entries);
30
+ const entry = ensureObject(entries[PLUGIN_ID]);
31
+ return ensureObject(entry.config);
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ function mergeDefinedConfig(base, override) {
38
+ const merged = { ...base };
39
+ for (const [key, value] of Object.entries(override)) {
40
+ if (value !== undefined) {
41
+ merged[key] = value;
42
+ }
43
+ }
44
+ return merged;
45
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-trusted-mode",
3
3
  "name": "Trusted Mode Governance",
4
- "version": "1.0.4",
4
+ "version": "1.0.6",
5
5
  "description": "Provable enforcement using SDE-PDP + signed policy packs",
6
6
  "author": "Darkelogix",
7
7
  "hooks": ["before_tool_call"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darkelogix/openclaw-trusted-mode",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "MIT-licensed OpenClaw Trusted Mode plugin with standalone hardening and optional SDE-backed governance",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,8 @@
8
8
  ".": "./dist/index.js"
9
9
  },
10
10
  "bin": {
11
- "openclaw-trusted-mode-check": "./dist/cli.js"
11
+ "openclaw-trusted-mode-check": "./dist/cli.js",
12
+ "openclaw-trusted-mode-configure": "./dist/configureCli.js"
12
13
  },
13
14
  "homepage": "https://www.darkelogix.ai/openclaw/",
14
15
  "repository": {
@@ -39,11 +40,14 @@
39
40
  "dist/cli.js",
40
41
  "dist/cliConfig.js",
41
42
  "dist/cliPdpClient.js",
43
+ "dist/configureCli.js",
42
44
  "dist/constraints.js",
43
45
  "dist/contextCurator.js",
44
46
  "dist/hardening.js",
45
47
  "dist/index.js",
48
+ "dist/openclawConfig.js",
46
49
  "dist/packageVersion.js",
50
+ "dist/runtimePluginConfig.js",
47
51
  "dist/runtimeCertification.js",
48
52
  "dist/sdeGuidance.js",
49
53
  "openclaw.plugin.json",
@@ -78,6 +82,7 @@
78
82
  "verify-compatibility-matrix": "node scripts/update_compatibility_matrix.js --check",
79
83
  "enforce-cert-quota": "node scripts/enforce_cert_quota.js",
80
84
  "trusted-mode-check": "node dist/cli.js",
85
+ "trusted-mode-configure": "node dist/configureCli.js",
81
86
  "trusted-mode-check:mock": "node scripts/mock_pdp.js",
82
87
  "lint-docs-terminology": "python scripts/lint_docs_terminology.py"
83
88
  },