@ory/openclaw 0.1.3 → 0.2.1
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 +23 -9
- package/dist/cli/main.js +17 -0
- package/dist/plugin.js +56 -30
- package/package.json +2 -2
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. **
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- `
|
|
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 permissions (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
|
-
|
|
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: {
|
|
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.
|
|
232
|
-
|
|
233
|
-
subjectId,
|
|
234
|
-
message: blockReason,
|
|
242
|
+
client.tracer.record("tool.invoke", "ok", {
|
|
243
|
+
attributes: { ...mcpAttrs, allowed: false, observed: true },
|
|
235
244
|
});
|
|
236
|
-
return
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
268
|
-
|
|
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
|
+
"version": "0.2.1",
|
|
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
|
|
68
|
+
"@ory/argus": "0.2.1"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=24"
|