@ory/openclaw 0.1.3 → 0.2.0

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/README.md CHANGED
@@ -98,6 +98,8 @@ The plugin is **fail-open** on its own infrastructure failures (network errors,
98
98
 
99
99
  ### Enable enforcement
100
100
 
101
+ After install the plugin runs in **observe mode**: every tool call is checked against Ory Permissions, but a deny is recorded as a `permission.observe_deny` audit span and the tool runs anyway. This lets you see what *would* be blocked before turning on hard blocking.
102
+
101
103
  1. **Turn on the user gate.** In your shell:
102
104
 
103
105
  ```bash
@@ -106,19 +108,29 @@ The plugin is **fail-open** on its own infrastructure failures (network errors,
106
108
 
107
109
  The next OpenClaw session refreshes or prompts for PKCE login. Subsequent sessions reuse the persisted token until it expires.
108
110
 
109
- 2. **Grant yourself permission to use a tool.** Use the Ory MCP server from inside OpenClaw (*"grant me invoke on the Bash tool"*) or the CLI directly:
111
+ 2. **Bootstrap tuples for the built-in tools.** One idempotent command grants the current user `use` on every tool OpenClaw ships with (execute_command, read_file, write_file, …):
112
+
113
+ ```bash
114
+ npx -y -p @ory/openclaw ory-openclaw permissions bootstrap
115
+ ```
116
+
117
+ If a user identity is already cached at install time, the installer runs this for you automatically — re-run after adding tools, switching subjects, or changing the namespace.
118
+
119
+ 3. **Check coverage.** `permissions status` probes every tool in the harness's catalog and prints allowed / denied per tool:
110
120
 
111
121
  ```bash
112
- ory create relationship \
113
- --namespace AgentTools \
114
- --object Bash \
115
- --relation invoke \
116
- --subject-id <your-user-subject-id>
122
+ npx -y -p @ory/openclaw ory-openclaw permissions status
117
123
  ```
118
124
 
119
- Your subject id is printed at the start of every OpenClaw session when `ORY_AGENT_DEBUG=true`.
125
+ Add tuples for any MCP server tools or custom commands by hand, or via the Ory MCP server from inside OpenClaw (*"grant me use on the execute_command tool"*).
126
+
127
+ 4. **Promote to enforce.** Once the observe-mode logs look right, switch over:
128
+
129
+ ```bash
130
+ npx -y -p @ory/openclaw ory-openclaw permissions enforce
131
+ ```
120
132
 
121
- 3. **See a denial.** Pick a tool you didn't grant (or remove the tuple) and ask OpenClaw to use it. The hook blocks the call and OpenClaw shows the denial reason. The decision is recorded as a `tool.block` trace span.
133
+ Denies now block the tool call; OpenClaw shows the denial reason and the decision is recorded as a `tool.block` trace span with `blocked: true`. Switch back any time with `permissions observe`.
122
134
 
123
135
  ## CLI reference
124
136
 
@@ -126,6 +138,7 @@ The plugin is **fail-open** on its own infrastructure failures (network errors,
126
138
  npx -y -p @ory/openclaw ory-openclaw install | uninstall [--project-dir <path>]
127
139
  npx -y -p @ory/openclaw ory-openclaw configure [--project-url <url>] [--api-key <key>] [--audit-only]
128
140
  npx -y -p @ory/openclaw ory-openclaw agent <status|unregister> Manage the agent's OAuth2 identity
141
+ npx -y -p @ory/openclaw ory-openclaw permissions <status|bootstrap|observe|enforce>
129
142
  npx -y -p @ory/openclaw ory-openclaw local <up|down|status|seed|logs|env|configure|reset>
130
143
  npx -y -p @ory/openclaw ory-openclaw watch [<trace-file>]
131
144
  npx -y -p @ory/openclaw ory-openclaw status [--project-dir <path>]
@@ -134,7 +147,8 @@ npx -y -p @ory/openclaw ory-openclaw status [--projec
134
147
  Highlights:
135
148
 
136
149
  - `agent status` — show the current persisted DCR identity for the agent.
137
- - `configure --audit-only` — record decisions without blocking; useful for a phased rollout.
150
+ - `permissions observe` / `permissions enforce` switch between "log denies, allow through" (the install default) and "block denies." `permissions bootstrap` writes `use` tuples for the harness's built-in tools so the promotion path doesn't require hand-writing relationships.
151
+ - `configure --audit-only` — kill switch that disables Ory entirely (no auth, no permission checks; only audit logging of tool invocations). For phased rollouts, prefer `permissions observe` over `--audit-only`.
138
152
  - `local seed` / `local env` — reseed the test user, or print env vars for pointing other tools at the local stack.
139
153
 
140
154
  ## Troubleshooting
package/dist/cli/main.js CHANGED
@@ -55,6 +55,10 @@ function main() {
55
55
  switch (command) {
56
56
  case "install":
57
57
  install(args);
58
+ postInstallPermissions("ory-openclaw", "openclaw").then(() => process.exit(0), (err) => {
59
+ console.error(err.message ?? err);
60
+ process.exit(1);
61
+ });
58
62
  break;
59
63
  case "uninstall":
60
64
  uninstall(args);
@@ -68,6 +72,12 @@ function main() {
68
72
  process.exit(1);
69
73
  });
70
74
  break;
75
+ case "permissions":
76
+ (0, argus_1.runPermissionsCommand)("ory-openclaw", "openclaw", args).then((code) => process.exit(code), (err) => {
77
+ console.error(err.message ?? err);
78
+ process.exit(1);
79
+ });
80
+ break;
71
81
  case "status":
72
82
  status(args);
73
83
  break;
@@ -92,6 +102,12 @@ function main() {
92
102
  process.exit(1);
93
103
  }
94
104
  }
105
+ async function postInstallPermissions(binName, harness) {
106
+ const bootstrapped = await (0, argus_1.maybeAutoBootstrap)(binName, harness);
107
+ (0, argus_1.printPermissionsOnboardingHelp)(binName, harness, {
108
+ bootstrappedAutomatically: bootstrapped,
109
+ });
110
+ }
95
111
  function parseProjectDir(args) {
96
112
  const idx = args.indexOf("--project-dir");
97
113
  if (idx !== -1 && args[idx + 1])
@@ -183,6 +199,7 @@ Commands:
183
199
  install [--project-dir <path>] Register the Ory plugin in OpenClaw config
184
200
  uninstall [--project-dir <path>] Remove the Ory plugin from OpenClaw config
185
201
  configure Set or view Ory project URL and API key
202
+ permissions <cmd> Manage permission mode and tool tuples (status, bootstrap, observe, enforce)
186
203
  status [--project-dir <path>] Show plugin configuration and status
187
204
  watch [trace-file] Watch live trace output
188
205
  local <cmd> Manage local Ory dev environment (up, down, status, seed, ...)
package/dist/plugin.js CHANGED
@@ -219,54 +219,80 @@ function createBeforeToolCallHandler(client) {
219
219
  subject,
220
220
  spanAttributes: { toolName: ctx.toolId },
221
221
  });
222
- if (!mcpResult.allowed) {
222
+ const mcpAttrs = {
223
+ toolName: ctx.toolId,
224
+ mcpServer: mcpTool.serverName,
225
+ mcpTool: mcpTool.toolName,
226
+ ...inputSummary,
227
+ };
228
+ const decision = (0, argus_1.applyPermissionMode)(client, mcpResult.allowed, {
229
+ object: mcpTool.serverName,
230
+ relation: "use",
231
+ subjectId,
232
+ spanAttributes: mcpAttrs,
233
+ });
234
+ if (decision.kind === "allow") {
235
+ client.tracer.record("tool.invoke", "ok", { attributes: mcpAttrs });
236
+ return;
237
+ }
238
+ if (decision.kind === "observe") {
223
239
  client.tracer.record("tool.block", "denied", {
224
- attributes: { toolName: ctx.toolId, mcpServer: mcpTool.serverName, mcpTool: mcpTool.toolName, ...inputSummary, ...(0, argus_1.alertAttributes)(true) },
225
- });
226
- const blockReason = (0, argus_1.formatDenialMessage)({
227
- tool: ctx.toolId,
228
- subjectId,
229
- mcp: mcpTool,
240
+ attributes: { ...mcpAttrs, allowed: false, ...(0, argus_1.alertAttributes)(false) },
230
241
  });
231
- client.logger.warn("tool.denied", {
232
- toolId: ctx.toolId,
233
- subjectId,
234
- message: blockReason,
242
+ client.tracer.record("tool.invoke", "ok", {
243
+ attributes: { ...mcpAttrs, allowed: false, observed: true },
235
244
  });
236
- return { block: true, blockReason };
245
+ return;
237
246
  }
238
- client.tracer.record("tool.invoke", "ok", {
239
- attributes: { toolName: ctx.toolId, mcpServer: mcpTool.serverName, mcpTool: mcpTool.toolName, ...inputSummary },
240
- });
241
- return;
242
- }
243
- const namespace = resolveNamespace();
244
- const result = await client.checkPermission({
245
- namespace,
246
- object: ctx.toolId,
247
- relation: "use",
248
- ...subject,
249
- }, { spanAttributes: { toolName: ctx.toolId } });
250
- if (!result.allowed) {
251
247
  client.tracer.record("tool.block", "denied", {
252
- attributes: { toolName: ctx.toolId, ...inputSummary, allowed: result.allowed, ...(0, argus_1.alertAttributes)(true) },
248
+ attributes: { ...mcpAttrs, allowed: false, ...(0, argus_1.alertAttributes)(true) },
253
249
  });
254
250
  const blockReason = (0, argus_1.formatDenialMessage)({
255
251
  tool: ctx.toolId,
256
252
  subjectId,
257
- namespace,
253
+ mcp: mcpTool,
258
254
  });
259
255
  client.logger.warn("tool.denied", {
260
256
  toolId: ctx.toolId,
261
257
  subjectId,
262
258
  message: blockReason,
263
259
  });
264
- // OpenClaw supports blocking — return { block: true, blockReason }
265
260
  return { block: true, blockReason };
266
261
  }
267
- client.tracer.record("tool.invoke", "ok", {
268
- attributes: { toolName: ctx.toolId, ...inputSummary, allowed: result.allowed },
262
+ const namespace = resolveNamespace();
263
+ const decision = await (0, argus_1.checkAndDecide)(client, { namespace, object: ctx.toolId, relation: "use", ...subject }, { spanAttributes: { toolName: ctx.toolId } });
264
+ if (decision.kind === "fail_open") {
265
+ handlePermissionError(decision.error, ctx.toolId, client);
266
+ return;
267
+ }
268
+ const attrs = { toolName: ctx.toolId, ...inputSummary };
269
+ if (decision.kind === "allow") {
270
+ client.tracer.record("tool.invoke", "ok", { attributes: { ...attrs, allowed: true } });
271
+ return;
272
+ }
273
+ if (decision.kind === "observe") {
274
+ client.tracer.record("tool.block", "denied", {
275
+ attributes: { ...attrs, allowed: false, ...(0, argus_1.alertAttributes)(false) },
276
+ });
277
+ client.tracer.record("tool.invoke", "ok", {
278
+ attributes: { ...attrs, allowed: false, observed: true },
279
+ });
280
+ return;
281
+ }
282
+ client.tracer.record("tool.block", "denied", {
283
+ attributes: { ...attrs, allowed: false, ...(0, argus_1.alertAttributes)(true) },
284
+ });
285
+ const blockReason = (0, argus_1.formatDenialMessage)({
286
+ tool: ctx.toolId,
287
+ subjectId,
288
+ namespace,
289
+ });
290
+ client.logger.warn("tool.denied", {
291
+ toolId: ctx.toolId,
292
+ subjectId,
293
+ message: blockReason,
269
294
  });
295
+ return { block: true, blockReason };
270
296
  }
271
297
  catch (err) {
272
298
  handlePermissionError(err, ctx.toolId, client);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ory/openclaw",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Ory plugin for OpenClaw: scaffolding skills, a local Ory instance, and authentication, authorization, and audit for every tool call",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ory.com",
@@ -65,7 +65,7 @@
65
65
  "!dist/**/*.tsbuildinfo"
66
66
  ],
67
67
  "dependencies": {
68
- "@ory/argus": "0.1.3"
68
+ "@ory/argus": "0.2.0"
69
69
  },
70
70
  "engines": {
71
71
  "node": ">=24"