@ory/argus 1.0.0 → 1.1.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/assets/skills/ory-build-agent/SKILL.md +5 -4
- package/assets/skills/ory-temporal-worker/SKILL.md +9 -9
- package/assets/skills/permissions-onboarding/SKILL.md +66 -142
- package/dist/adapters.js +1 -1
- package/dist/bash-parser.js +13 -1
- package/dist/build-info.json +4 -4
- package/dist/cli.js +1 -1
- package/dist/contract-suite.js +5 -2
- package/dist/local/manager.js +1 -1
- package/dist/local/seed.d.ts +1 -1
- package/dist/local/seed.js +4 -62
- package/dist/opl.d.ts +11 -18
- package/dist/opl.js +21 -36
- package/dist/permissions-cli.d.ts +2 -2
- package/dist/permissions-cli.js +15 -32
- package/dist/permissions.d.ts +7 -10
- package/dist/permissions.js +16 -28
- package/dist/post-install.d.ts +1 -1
- package/dist/post-install.js +2 -2
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +78 -1
- package/dist/setup.js +1 -1
- package/dist/status-cli.d.ts +1 -1
- package/dist/status-cli.js +12 -12
- package/dist/status-data.d.ts +8 -5
- package/dist/status-data.js +12 -11
- package/package.json +1 -1
|
@@ -361,7 +361,8 @@ PKCE flow, permission tuples, and activity events are all visible:
|
|
|
361
361
|
`jq` — you
|
|
362
362
|
should see `user.auth` → `agent.auth` → `permission.check` →
|
|
363
363
|
`tool.complete` for every call.
|
|
364
|
-
5.
|
|
364
|
+
5. Native tools are allowed unless explicitly blocked. Promote to enforce once
|
|
365
|
+
the block policy is ready. The posture is a
|
|
365
366
|
permission on the Ory project, read on every session — an admin sets it
|
|
366
367
|
in the Ory Console (Agent Security); see the resolved value with
|
|
367
368
|
`... permissions` via any harness CLI (same shared config file).
|
|
@@ -376,9 +377,9 @@ For full env-var coverage (including the user/agent split,
|
|
|
376
377
|
- It does not generate the agent. The user owns the agent loop, tool
|
|
377
378
|
catalog, and deployment shape. This skill only drops `@ory/argus` into
|
|
378
379
|
whatever they already have.
|
|
379
|
-
- It does not
|
|
380
|
-
(Agent Security); `@ory/argus` only reads
|
|
381
|
-
seed
|
|
380
|
+
- It does not write permissions. Native-tool blocks and MCP grants are
|
|
381
|
+
provisioned in the Ory Console (Agent Security); `@ory/argus` only reads
|
|
382
|
+
them. The local seed writes no baseline native-tool grants.
|
|
382
383
|
- It does not adapt one of the existing harness plugins (`@ory/claude-code`,
|
|
383
384
|
`@ory/codex`, `@ory/gemini-cli`, `@ory/openclaw`, `@ory/opencode`). Those
|
|
384
385
|
are for users running those harnesses — not building a custom agent.
|
|
@@ -244,19 +244,19 @@ The Workflow itself produces no Ory activity — only its Activities do.
|
|
|
244
244
|
|
|
245
245
|
## Step 6 — Promotion from observe to enforce
|
|
246
246
|
|
|
247
|
-
The worker starts in `observe` mode by default:
|
|
248
|
-
recorded as a `permission.
|
|
249
|
-
the
|
|
247
|
+
The worker starts in `observe` mode by default: explicit blocks pass through but
|
|
248
|
+
each is recorded as a `permission.block_observed` activity event. Once the user
|
|
249
|
+
has confirmed the block set is what they expect, an admin promotes the posture to `enforce` in
|
|
250
250
|
the Ory Console (Agent Security). It is a permission on the project, read on
|
|
251
251
|
every session, so the worker picks it up on its next run with no redeploy.
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
253
|
+
Activities are allowed by default. Add `blockedSubjects` relations for Activity
|
|
254
|
+
names that must not run, then review them before promoting the posture. See
|
|
255
|
+
{{REF_PERMISSIONS_ONBOARDING}}.
|
|
256
256
|
|
|
257
|
-
To exercise the deny path locally,
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
To exercise the deny path locally, add a `blockedSubjects` relation for an
|
|
258
|
+
Activity name, kick the Workflow, and watch the explicit-block error in the
|
|
259
|
+
Temporal Web UI.
|
|
260
260
|
|
|
261
261
|
## Step 7 — Beyond the dev server
|
|
262
262
|
|
|
@@ -1,195 +1,119 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ory-permissions-onboarding
|
|
3
|
-
description: Onboard
|
|
3
|
+
description: Onboard native agent-tool authorization with default allow and explicit deny overrides. Covers connecting the plugin, reviewing allowed and blocked tools, writing blocks in the Ory Console, and promoting the project from observe to enforce. MCP and PermissionMode remain grant-based.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Onboard
|
|
6
|
+
# Onboard Ory Permissions for Agent Tool Calls
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
first-run failure mode of getting every tool call blocked because the
|
|
11
|
-
project has no grants yet.
|
|
8
|
+
Help the user connect Agent Security and roll out explicit native-tool blocks
|
|
9
|
+
without treating missing grants as denials.
|
|
12
10
|
|
|
13
|
-
**The plugin only reads permissions. It never writes them.**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Console** (Agent Security) by someone with access to the project. That
|
|
17
|
-
split is deliberate: it means installing and connecting a plugin needs
|
|
18
|
-
nothing but project connection details and a public OAuth2 client id, with no
|
|
19
|
-
workspace privilege and no project API key.
|
|
11
|
+
**The plugin only reads permissions. It never writes them.** The permission
|
|
12
|
+
model, native-tool blocks, MCP grants, and observe/enforce posture are managed
|
|
13
|
+
in the **Ory Console** (Agent Security).
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
Native policy is simple:
|
|
22
16
|
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
the MCP server work either way — they don't need a project at all.
|
|
29
|
-
- **`permissionMode`** — `observe` or `enforce`, **read from the Ory
|
|
30
|
-
project** on every session, and only meaningful once connected.
|
|
31
|
-
`observe` (the default) runs every check, records denials, and lets the
|
|
32
|
-
tool proceed. `enforce` blocks on deny. There is no local override — an
|
|
33
|
-
admin sets it in the Console.
|
|
17
|
+
- `AgentTool` and `ShellTool` have no `users` relation.
|
|
18
|
+
- Their `use` permit is `!blockedSubjects.includes(subject)`.
|
|
19
|
+
- No matching block means allowed; a matching block means denied.
|
|
20
|
+
- `observe` logs the block and proceeds; `enforce` vetoes the call.
|
|
21
|
+
- MCP permissions and `PermissionMode#enforcedSubjects` remain grant-based.
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
enforce in the Console.**
|
|
23
|
+
## 1. Connect Agent Security
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
A fresh install has the developer-experience half working and Agent
|
|
41
|
-
Security not connected. Copy the connection details shown in the Ory Console
|
|
42
|
-
under **Agent Security**:
|
|
25
|
+
Copy the connection details shown in the Ory Console under **Agent Security**:
|
|
43
26
|
|
|
44
27
|
```sh
|
|
45
28
|
{{NPX}} configure --project-url <URL> --agent-security-url <URL>
|
|
46
29
|
```
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
- **Agent Security URL** — the canonical broker origin (`https://agents.console.ory.com` in production).
|
|
50
|
-
- **OAuth2 client id** — the project's public login client, provisioned
|
|
51
|
-
in the Console. The per-session user login needs it to complete its
|
|
52
|
-
PKCE browser flow.
|
|
53
|
-
|
|
54
|
-
Then confirm what the plugin resolved:
|
|
31
|
+
Then inspect the resolved configuration:
|
|
55
32
|
|
|
56
33
|
```sh
|
|
57
34
|
{{NPX}} status
|
|
58
35
|
```
|
|
59
36
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- **Project URL**: a real Ory URL or your local dev gateway (not "NOT SET").
|
|
63
|
-
- **OAuth2 Client**: the project's public login client (not "NOT SET").
|
|
64
|
-
- **Agent Security**: connected. If it says "not connected", it names the
|
|
65
|
-
unresolved connection value.
|
|
66
|
-
|
|
67
|
-
If the user doesn't have those values, they need them from whoever
|
|
68
|
-
administers the project. Don't try to create the client from here — the
|
|
69
|
-
plugin has no privilege to, by design.
|
|
37
|
+
Confirm that Agent Security is connected and the public OAuth2 login client is
|
|
38
|
+
resolved. The plugin needs no workspace privilege or project API key.
|
|
70
39
|
|
|
71
|
-
##
|
|
40
|
+
## 2. Review Native Tool Access
|
|
72
41
|
|
|
73
42
|
```sh
|
|
74
43
|
{{NPX}} permissions
|
|
75
44
|
```
|
|
76
45
|
|
|
77
|
-
|
|
46
|
+
The report shows:
|
|
78
47
|
|
|
79
|
-
- The live
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
blocked / errored**, from a real check against the project right now.
|
|
84
|
-
- A one-line coverage summary for decomposed shell command words.
|
|
48
|
+
- The live permission mode and its source.
|
|
49
|
+
- The user subject being checked.
|
|
50
|
+
- Each built-in native tool as `allowed`, `blocked`, or `errored`.
|
|
51
|
+
- A compact allowed/blocked summary for decomposed shell command words.
|
|
85
52
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
be blocked under enforce.
|
|
53
|
+
A fresh project normally reports native tools as **allowed**. There is no grant
|
|
54
|
+
coverage step and the local seed writes no baseline `AgentTool` or `ShellTool`
|
|
55
|
+
grants.
|
|
90
56
|
|
|
91
|
-
|
|
92
|
-
**are** being blocked right now. The fix is in the Console (grant the
|
|
93
|
-
tools, or move the project back to observe) — not from the CLI.
|
|
57
|
+
## 3. Add Explicit Blocks in the Console
|
|
94
58
|
|
|
95
|
-
|
|
59
|
+
To block a native tool, write a `blockedSubjects` relation in the Ory Console:
|
|
96
60
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{{NPX}} watch # live event, decision, and error view
|
|
61
|
+
```text
|
|
62
|
+
namespace: AgentTool
|
|
63
|
+
object: <tool name> e.g. WebFetch
|
|
64
|
+
relation: blockedSubjects
|
|
65
|
+
subject: User:<id>
|
|
103
66
|
```
|
|
104
67
|
|
|
105
|
-
|
|
106
|
-
the watched log and stderr. Denials appear as `permission.observe_deny` activity events (and
|
|
107
|
-
`permission.block_observed` for an explicit block), with the tool name
|
|
108
|
-
and the subject. That list is exactly what has to be granted before
|
|
109
|
-
enforce is safe.
|
|
110
|
-
|
|
111
|
-
## Step 4: Grant the tools in the Ory Console
|
|
68
|
+
To block a command word reached through a shell tool:
|
|
112
69
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
object: <tool name> (e.g. Read, Bash, WebFetch)
|
|
119
|
-
relation: use
|
|
120
|
-
subject: User:<id> (a SubjectSet — namespace `User`, object the user id)
|
|
70
|
+
```text
|
|
71
|
+
namespace: ShellTool
|
|
72
|
+
object: <command word> e.g. curl
|
|
73
|
+
relation: blockedSubjects
|
|
74
|
+
subject: User:<id>
|
|
121
75
|
```
|
|
122
76
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Console's *Add relationship* dialog emits and exactly what the runtime
|
|
127
|
-
check uses. A bare subject id will not match.
|
|
128
|
-
- Granting a **group** and putting users in it scales better than
|
|
129
|
-
per-user grants; the check follows subject-set expansion.
|
|
130
|
-
- **Shell command words** are authorized separately in the `ShellTool`
|
|
131
|
-
namespace (`ShellTool:curl#use`, …) because a denied surface is
|
|
132
|
-
otherwise reachable through `bash -c "curl …"`.
|
|
133
|
-
- **MCP server tools** are discovered dynamically per session, so they
|
|
134
|
-
are not in the built-in catalog and need granting as they come into
|
|
135
|
-
scope.
|
|
136
|
-
|
|
137
|
-
Re-run `{{NPX}} permissions` afterwards to confirm coverage.
|
|
77
|
+
Blocks can also target the project, an agent credential, an agent session, a
|
|
78
|
+
sub-agent credential, or a sub-agent spawn. Group/SubjectSet expansion is
|
|
79
|
+
supported. A native deny is always an explicit block, never a missing grant.
|
|
138
80
|
|
|
139
|
-
|
|
81
|
+
MCP is intentionally different: continue granting MCP server/tool access with
|
|
82
|
+
its existing `users`/`use` and `invoke` relations.
|
|
140
83
|
|
|
141
|
-
|
|
142
|
-
the posture in the Console (Agent Security). The mode is a permission on
|
|
143
|
-
the project, so it can be set **project-wide** — covering principals
|
|
144
|
-
that don't exist yet — or **per principal** as an exception.
|
|
84
|
+
## 4. Observe Before Enforcing
|
|
145
85
|
|
|
146
|
-
|
|
147
|
-
reinstalling or reconfiguring anything. Confirm it landed:
|
|
86
|
+
While the project is in `observe`, run normal sessions and inspect:
|
|
148
87
|
|
|
149
88
|
```sh
|
|
150
89
|
{{NPX}} permissions
|
|
90
|
+
{{NPX}} watch
|
|
151
91
|
```
|
|
152
92
|
|
|
153
|
-
|
|
93
|
+
Matching blocks emit `permission.block_observed` but the tool still runs. Review
|
|
94
|
+
those events, then promote the project or selected principals to `enforce` in
|
|
95
|
+
the Ory Console. Every gate reads the project-controlled posture.
|
|
154
96
|
|
|
155
|
-
|
|
97
|
+
## 5. Verify Enforcement
|
|
156
98
|
|
|
157
|
-
|
|
158
|
-
- **Denied tools** are blocked with a clear "Ory: permission denied"
|
|
159
|
-
message naming the tool (or the shell command word).
|
|
160
|
-
- The activity log shows `permission.check` events per tool call and
|
|
161
|
-
`tool.block` events for denials.
|
|
99
|
+
Under `enforce`:
|
|
162
100
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
101
|
+
- Tools without a matching block run normally.
|
|
102
|
+
- Explicitly blocked tools produce a clear "explicitly blocked" message.
|
|
103
|
+
- A blocked `ShellTool` word blocks the outer shell command and names the word.
|
|
104
|
+
- Infrastructure failures retain the core fail-open behavior.
|
|
166
105
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Granting is additive. To express the opposite — a subject is **blocked**
|
|
170
|
-
from a tool, beating any grant, including one inherited through a group —
|
|
171
|
-
an admin writes a `blockedSubjects` relation in the Console. Ory evaluates it
|
|
172
|
-
natively (the `use` permit is "granted AND not blocked"), so it holds
|
|
173
|
-
no matter how the grant was reached.
|
|
174
|
-
|
|
175
|
-
A block behaves like any other deny: under **observe** it is recorded (a
|
|
176
|
-
`permission.block_observed` activity event) but the tool still runs; under
|
|
177
|
-
**enforce** it blocks. `permissions` marks blocked tools so they
|
|
178
|
-
are distinguishable from a plain missing grant at a glance.
|
|
106
|
+
If a tool is unexpectedly blocked, use `{{NPX}} permissions` to identify the
|
|
107
|
+
block and remove or narrow its `blockedSubjects` relation in the Console.
|
|
179
108
|
|
|
180
109
|
## Reference
|
|
181
110
|
|
|
182
111
|
| Command | Effect |
|
|
183
112
|
|---|---|
|
|
184
|
-
| `{{NPX}} configure --project-url <URL> --agent-security-url <URL>` | Connect
|
|
185
|
-
| `{{NPX}} configure --disconnect` |
|
|
186
|
-
| `{{NPX}} permissions` | Print
|
|
187
|
-
| `{{NPX}} status` |
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
and setting the observe/enforce posture.
|
|
192
|
-
|
|
193
|
-
For deeper background on the authentication side of the flow (which
|
|
194
|
-
identity is the subject, how the user gate resolves it), see
|
|
195
|
-
{{REF_AUTH_SETUP}} and {{REF_LOGIN_FLOW}}.
|
|
113
|
+
| `{{NPX}} configure --project-url <URL> --agent-security-url <URL>` | Connect Agent Security. |
|
|
114
|
+
| `{{NPX}} configure --disconnect` | Disconnect sign-in and permission checks. |
|
|
115
|
+
| `{{NPX}} permissions` | Print live mode plus native allowed/blocked state. Read-only. |
|
|
116
|
+
| `{{NPX}} status` | Print configuration, identities, native tool access, and recent activity. |
|
|
117
|
+
|
|
118
|
+
For authentication and subject-resolution details, see {{REF_AUTH_SETUP}} and
|
|
119
|
+
{{REF_LOGIN_FLOW}}.
|
package/dist/adapters.js
CHANGED
|
@@ -366,7 +366,7 @@ async function decideTool(client, opts) {
|
|
|
366
366
|
const label = (0, subject_js_1.subjectLabel)(subjectRef);
|
|
367
367
|
const canBlock = opts.canBlock ?? true;
|
|
368
368
|
const activityAttrs = gateActivityAttributes(opts);
|
|
369
|
-
const check = { namespace, object: opts.toolName, relation: "
|
|
369
|
+
const check = { namespace, object: opts.toolName, relation: "use", ...subjectRef };
|
|
370
370
|
if (!(0, config_js_1.isSecurityConnected)()) {
|
|
371
371
|
const outcome = {
|
|
372
372
|
kind: "not_connected",
|
package/dist/bash-parser.js
CHANGED
|
@@ -172,6 +172,16 @@ function resolveCommandName(nameNode) {
|
|
|
172
172
|
return basename(child.text);
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
+
function commandNameIsAmbiguous(nameNode, name) {
|
|
176
|
+
const raw = nameNode.text;
|
|
177
|
+
if (!/[\\'"$]/.test(raw))
|
|
178
|
+
return false;
|
|
179
|
+
if (!name)
|
|
180
|
+
return true;
|
|
181
|
+
if (raw === `"${name}"` || raw === `'${name}'`)
|
|
182
|
+
return false;
|
|
183
|
+
return !raw.endsWith(`/${name}"`) && !raw.endsWith(`/${name}'`);
|
|
184
|
+
}
|
|
175
185
|
function argNodes(cmd) {
|
|
176
186
|
return cmd.childrenForFieldName("argument").filter((n) => n !== null);
|
|
177
187
|
}
|
|
@@ -271,7 +281,7 @@ function walk(node, pipeline, parser, state, depth, treeDepth) {
|
|
|
271
281
|
}
|
|
272
282
|
const pid = pipeline >= 0 ? pipeline : state.pipelineCounter++;
|
|
273
283
|
const name = resolveCommandName(nameNode);
|
|
274
|
-
if (name === null)
|
|
284
|
+
if (name === null || commandNameIsAmbiguous(nameNode, name))
|
|
275
285
|
state.tooComplex = true;
|
|
276
286
|
const args = argNodes(node);
|
|
277
287
|
state.commands.push({
|
|
@@ -361,6 +371,8 @@ async function extractShellCommands(command) {
|
|
|
361
371
|
if (!tree)
|
|
362
372
|
return { commands: [], tooComplex: true };
|
|
363
373
|
const state = { commands: [], tooComplex: false, pipelineCounter: 0 };
|
|
374
|
+
if (command.includes("\\\n"))
|
|
375
|
+
state.tooComplex = true;
|
|
364
376
|
if (tree.rootNode.hasError)
|
|
365
377
|
state.tooComplex = true;
|
|
366
378
|
// The walk must never throw out of this function — an uncaught error would
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "ory-agent-plugins",
|
|
3
|
-
"commit": "
|
|
4
|
-
"commitShort": "
|
|
3
|
+
"commit": "fc87fd73772d79007d1072c605ede76de42792ce",
|
|
4
|
+
"commitShort": "fc87fd7",
|
|
5
5
|
"branch": "main",
|
|
6
|
-
"commitDate": "2026-08-
|
|
6
|
+
"commitDate": "2026-08-30T17:10:01-07:00",
|
|
7
7
|
"dirty": false,
|
|
8
|
-
"builtAt": "2026-08-
|
|
8
|
+
"builtAt": "2026-08-31T00:13:34.583Z"
|
|
9
9
|
}
|
package/dist/cli.js
CHANGED
|
@@ -125,7 +125,7 @@ function runConfigureCommand(binName, args) {
|
|
|
125
125
|
console.log(`User subject ns: ${resolved.userSubjectNamespace ?? "User (default)"} [${resolved.userSubjectNamespaceSource}]`);
|
|
126
126
|
console.log("");
|
|
127
127
|
if (resolved.security.connected) {
|
|
128
|
-
console.log("Sign-in and permission checks are running.
|
|
128
|
+
console.log("Sign-in and permission checks are running. Native-tool blocks, MCP grants,");
|
|
129
129
|
console.log("and the observe/enforce posture are managed in the Ory Console (Agent Security).");
|
|
130
130
|
console.log("");
|
|
131
131
|
console.log("To disconnect (skills, commands, the local stack, and activity logging keep working):");
|
package/dist/contract-suite.js
CHANGED
|
@@ -200,9 +200,12 @@ function runHarnessContractSuite(adapter) {
|
|
|
200
200
|
else {
|
|
201
201
|
(0, vitest_1.expect)(blockEvents).toHaveLength(0);
|
|
202
202
|
}
|
|
203
|
-
//
|
|
203
|
+
// Native tools are default-allow, so a denied use check is an
|
|
204
|
+
// explicit block and receives the block-specific observe event.
|
|
204
205
|
const observeEvents = (0, testing_js_1.getActivityEvents)(client, "permission.observe_deny");
|
|
205
|
-
(0,
|
|
206
|
+
const blockObservedEvents = (0, testing_js_1.getActivityEvents)(client, "permission.block_observed");
|
|
207
|
+
(0, vitest_1.expect)(observeEvents).toHaveLength(0);
|
|
208
|
+
(0, vitest_1.expect)(blockObservedEvents).toHaveLength(check === "denied" && mode === "observe" ? 1 : 0);
|
|
206
209
|
});
|
|
207
210
|
(0, vitest_1.it)("records tool.invoke when an interactive tool proceeds", async () => {
|
|
208
211
|
process.env.ORY_INTERACTIVE_TOOLS = adapter.tool;
|
package/dist/local/manager.js
CHANGED
|
@@ -849,7 +849,7 @@ function printSeedResult(result) {
|
|
|
849
849
|
console.log(` User password: ${result.user.password}`);
|
|
850
850
|
console.log(` User OAuth2: ${result.user.client.clientId} (PKCE)`);
|
|
851
851
|
console.log("");
|
|
852
|
-
console.log(`
|
|
852
|
+
console.log(` Native tool grants: none (default allow; explicit blocks only)`);
|
|
853
853
|
console.log("");
|
|
854
854
|
console.log("To use with any Ory agent plugin, set these environment variables:");
|
|
855
855
|
console.log("");
|
package/dist/local/seed.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface SeedResult {
|
|
|
53
53
|
/** OAuth2 authorization_code+PKCE client for interactive logins. */
|
|
54
54
|
client: SeededOAuth2Client;
|
|
55
55
|
};
|
|
56
|
-
/**
|
|
56
|
+
/** Native baseline permission tuples written against the user's subject (always zero). */
|
|
57
57
|
permissions: {
|
|
58
58
|
namespace: string;
|
|
59
59
|
subject: string;
|
package/dist/local/seed.js
CHANGED
|
@@ -27,14 +27,11 @@ exports.USER_SUBJECT_NAMESPACE = exports.USER_CLIENT_ID = void 0;
|
|
|
27
27
|
exports.seedLocalEnvironment = seedLocalEnvironment;
|
|
28
28
|
const auth_js_1 = require("../auth.js");
|
|
29
29
|
const branding_js_1 = require("../branding.js");
|
|
30
|
-
const tool_catalog_js_1 = require("../tool-catalog.js");
|
|
31
30
|
const subject_js_1 = require("../subject.js");
|
|
32
|
-
const opl_js_1 = require("../opl.js");
|
|
33
31
|
const configs_js_1 = require("./configs.js");
|
|
34
32
|
const ports_js_1 = require("./ports.js");
|
|
35
33
|
const health_js_1 = require("./health.js");
|
|
36
34
|
const KRATOS_ADMIN = (0, ports_js_1.hostUrl)(configs_js_1.LOCAL_STACK_HOST, configs_js_1.KRATOS_ADMIN_PORT);
|
|
37
|
-
const KETO_WRITE = (0, ports_js_1.hostUrl)(configs_js_1.LOCAL_STACK_HOST, configs_js_1.KETO_WRITE_PORT);
|
|
38
35
|
const HYDRA_ADMIN = (0, ports_js_1.hostUrl)(configs_js_1.LOCAL_STACK_HOST, configs_js_1.HYDRA_ADMIN_PORT);
|
|
39
36
|
const USER_EMAIL = "user@ory-local.dev";
|
|
40
37
|
const USER_PASSWORD = "ory-user-local-dev-password!";
|
|
@@ -49,11 +46,6 @@ const USER_PASSWORD = "ory-user-local-dev-password!";
|
|
|
49
46
|
* dragging the value out of the `local up` banner.
|
|
50
47
|
*/
|
|
51
48
|
exports.USER_CLIENT_ID = branding_js_1.USER_LOGIN_CLIENT_ID;
|
|
52
|
-
/**
|
|
53
|
-
* Tools the explicit local-stack command seeds tuples for. Sourced from the
|
|
54
|
-
* shared catalog so one seed covers every harness's built-in tool set.
|
|
55
|
-
*/
|
|
56
|
-
const COMMON_TOOLS = tool_catalog_js_1.ALL_TOOLS;
|
|
57
49
|
async function jsonFetch(url, opts = {}) {
|
|
58
50
|
const res = await fetch(url, {
|
|
59
51
|
...opts,
|
|
@@ -94,44 +86,6 @@ async function seedIdentity(email, password, name) {
|
|
|
94
86
|
* install config all address the user under the same namespace.
|
|
95
87
|
*/
|
|
96
88
|
exports.USER_SUBJECT_NAMESPACE = subject_js_1.DEFAULT_USER_SUBJECT_NAMESPACE;
|
|
97
|
-
/**
|
|
98
|
-
* Write permission tuples granting the user identity access to all
|
|
99
|
-
* common tools. The subject is written as a SubjectSet
|
|
100
|
-
* `User:<userId>` (no relation) so the Console UI's *Add relationship*
|
|
101
|
-
* dialog — which only emits SubjectSet subjects — can recreate the same
|
|
102
|
-
* tuple by hand during the runbook walkthrough.
|
|
103
|
-
*/
|
|
104
|
-
async function seedPermissions(userId, namespace, objects = COMMON_TOOLS) {
|
|
105
|
-
let created = 0;
|
|
106
|
-
for (const object of objects) {
|
|
107
|
-
try {
|
|
108
|
-
await jsonFetch(`${KETO_WRITE}/admin/relation-tuples`, {
|
|
109
|
-
method: "PUT",
|
|
110
|
-
body: JSON.stringify({
|
|
111
|
-
namespace,
|
|
112
|
-
object,
|
|
113
|
-
relation: opl_js_1.RELATION_USERS,
|
|
114
|
-
subject_set: {
|
|
115
|
-
namespace: exports.USER_SUBJECT_NAMESPACE,
|
|
116
|
-
object: userId,
|
|
117
|
-
relation: "",
|
|
118
|
-
},
|
|
119
|
-
}),
|
|
120
|
-
});
|
|
121
|
-
created++;
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
125
|
-
if (!msg.includes("409")) {
|
|
126
|
-
process.stderr.write(` Warning: failed to create permission for ${object}: ${msg}\n`);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
created++;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return created;
|
|
134
|
-
}
|
|
135
89
|
/**
|
|
136
90
|
* Delete a Hydra OAuth2 client if it exists, then create it at a known
|
|
137
91
|
* client_id (with a known secret, for a confidential client). We always
|
|
@@ -209,22 +163,10 @@ async function seedLocalEnvironment(namespace = "AgentTool") {
|
|
|
209
163
|
process.stderr.write(" Creating user identity...\n");
|
|
210
164
|
const userIdentity = await seedIdentity(USER_EMAIL, USER_PASSWORD, "Local User");
|
|
211
165
|
process.stderr.write(` User: ${userIdentity.id} (${userIdentity.email})\n`);
|
|
212
|
-
// 2.
|
|
213
|
-
//
|
|
214
|
-
// Subject is a SubjectSet `User:<id>` so the Console UI can rewrite
|
|
215
|
-
// the same tuple shape by hand during the runbook demo.
|
|
166
|
+
// 2. Native tools are allowed by default. Seed no AgentTool or ShellTool
|
|
167
|
+
// baseline grants; local behavior must match hosted default-allow policy.
|
|
216
168
|
const subjectLabel = `${exports.USER_SUBJECT_NAMESPACE}:${userIdentity.id}`;
|
|
217
|
-
process.stderr.write(
|
|
218
|
-
const tupleCount = await seedPermissions(userIdentity.id, namespace);
|
|
219
|
-
process.stderr.write(` Permissions: ${tupleCount} entries in '${namespace}' for ${subjectLabel}\n`);
|
|
220
|
-
// Shell-command sub-tools (issue #76): grant every catalog word in the
|
|
221
|
-
// ShellTool namespace so a launched harness can run `bash -c "curl …"`
|
|
222
|
-
// out of the box, and the enforce demo (revoke `ShellTool:curl#users`,
|
|
223
|
-
// watch it block) works without hand-writing tuples first.
|
|
224
|
-
const shellWords = (0, tool_catalog_js_1.getShellCommandCatalog)();
|
|
225
|
-
const shellNamespace = (0, tool_catalog_js_1.resolveShellCommandNamespace)();
|
|
226
|
-
const shellCount = await seedPermissions(userIdentity.id, shellNamespace, shellWords);
|
|
227
|
-
process.stderr.write(` Shell commands: ${shellCount} entries in '${shellNamespace}' for ${subjectLabel}\n`);
|
|
169
|
+
process.stderr.write(" Native tools: allowed unless explicitly blocked (no baseline grants)\n");
|
|
228
170
|
// 3. User OAuth2 client (PKCE) — pre-registered so the user gate's
|
|
229
171
|
// PKCE flow has somewhere to authenticate against. The agent's
|
|
230
172
|
// OAuth2 client is intentionally NOT pre-registered; the harness
|
|
@@ -253,6 +195,6 @@ async function seedLocalEnvironment(namespace = "AgentTool") {
|
|
|
253
195
|
}
|
|
254
196
|
return {
|
|
255
197
|
user: { identity: userIdentity, password: USER_PASSWORD, client: userClient },
|
|
256
|
-
permissions: { namespace, subject: subjectLabel, tuples:
|
|
198
|
+
permissions: { namespace, subject: subjectLabel, tuples: 0 },
|
|
257
199
|
};
|
|
258
200
|
}
|
package/dist/opl.d.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ory Permission Language (OPL) schema for the Agent Security permission model.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* permission is otherwise purely existence-based (no path ⇒ no access, any path
|
|
7
|
-
* ⇒ access), so the deny-override is modeled natively in OPL as a permit:
|
|
4
|
+
* Native agent and shell tools are allowed by default and may be explicitly
|
|
5
|
+
* blocked. The deny is modeled natively in OPL as a permit:
|
|
8
6
|
*
|
|
9
|
-
* use =
|
|
7
|
+
* use = !blockedSubjects.includes(subject)
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `resolveCheckRelation`), so this model must be applied to the project for
|
|
15
|
-
* checks to resolve; with nothing blocked, `use` resolves identically to a bare
|
|
16
|
-
* `users` existence check, so grants alone allow.
|
|
9
|
+
* Argus always checks the **`use` permit**, so a missing block allows and a
|
|
10
|
+
* matching `blockedSubjects` relation denies. This model must be applied to the
|
|
11
|
+
* project for checks to resolve.
|
|
17
12
|
*
|
|
18
13
|
* ## Naming conventions
|
|
19
14
|
*
|
|
@@ -194,8 +189,7 @@ export declare const PERMISSION_MODE_OBJECT = "mode";
|
|
|
194
189
|
*/
|
|
195
190
|
export declare const PROJECT_POSTURE_OBJECT = "project";
|
|
196
191
|
/**
|
|
197
|
-
* The permit Argus checks:
|
|
198
|
-
* Keto. A verb, as Keto's own `view` is — it names the question the gate asks.
|
|
192
|
+
* The native-tool permit Argus checks: `!blockedSubjects`, evaluated by Keto.
|
|
199
193
|
*/
|
|
200
194
|
export declare const PERMIT_USE = "use";
|
|
201
195
|
/**
|
|
@@ -236,9 +230,9 @@ export declare function renderOplImport(): string;
|
|
|
236
230
|
/** Render a principal-namespace class (an empty `implements Namespace`). */
|
|
237
231
|
export declare function renderPrincipalClass(name: string): string;
|
|
238
232
|
/**
|
|
239
|
-
* Render a tool-namespace class body: the `
|
|
240
|
-
*
|
|
241
|
-
*
|
|
233
|
+
* Render a native tool-namespace class body: the `blockedSubjects` relation and
|
|
234
|
+
* the default-allow `use` permit Argus checks. The single definition is used for
|
|
235
|
+
* both AgentTool and ShellTool; MCP namespaces remain grant-based elsewhere.
|
|
242
236
|
*/
|
|
243
237
|
export declare function renderToolNamespaceClass(namespace?: string): string;
|
|
244
238
|
/**
|
|
@@ -248,8 +242,7 @@ export declare function renderToolNamespaceClass(namespace?: string): string;
|
|
|
248
242
|
*/
|
|
249
243
|
export declare function renderDelegationNamespaceClass(namespace?: string): string;
|
|
250
244
|
/**
|
|
251
|
-
* Render the role class:
|
|
252
|
-
* One class, one relation — a role is a group of subjects and nothing else.
|
|
245
|
+
* Render the role class: a reusable group whose members can be blocked together.
|
|
253
246
|
*/
|
|
254
247
|
export declare function renderAgentRoleNamespaceClass(namespace?: string): string;
|
|
255
248
|
/** Render a harness/sub-agent type group whose members inherit posture. */
|