@ory/argus 0.14.0 → 1.0.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.
Files changed (150) hide show
  1. package/README.md +31 -46
  2. package/assets/commands/temporal-up.md +1 -1
  3. package/assets/skills/auth-setup/SKILL.md +1 -1
  4. package/assets/skills/local-dev/SKILL.md +17 -7
  5. package/assets/skills/ory-build-agent/SKILL.md +39 -91
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -17
  7. package/assets/skills/ory-temporal-worker/SKILL.md +31 -33
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +88 -32
  10. package/dist/adapters.js +443 -164
  11. package/dist/agent-auth.d.ts +226 -67
  12. package/dist/agent-auth.js +951 -205
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +33 -4
  16. package/dist/auth.js +163 -20
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +77 -16
  20. package/dist/branding.js +93 -23
  21. package/dist/build-info.json +4 -4
  22. package/dist/cli-invocation.d.ts +1 -1
  23. package/dist/cli-invocation.js +2 -1
  24. package/dist/cli.d.ts +20 -29
  25. package/dist/cli.js +271 -239
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +217 -54
  29. package/dist/config.js +461 -42
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +3 -5
  33. package/dist/contract-suite.js +75 -47
  34. package/dist/denial.d.ts +36 -3
  35. package/dist/denial.js +79 -10
  36. package/dist/event-reporter.d.ts +77 -0
  37. package/dist/event-reporter.js +776 -0
  38. package/dist/external-registrations-main.d.ts +10 -0
  39. package/dist/external-registrations-main.js +38 -0
  40. package/dist/external-registrations.d.ts +79 -0
  41. package/dist/external-registrations.js +188 -0
  42. package/dist/help-cli.d.ts +39 -0
  43. package/dist/help-cli.js +55 -0
  44. package/dist/hook-timeout.d.ts +64 -0
  45. package/dist/hook-timeout.js +88 -0
  46. package/dist/index.d.ts +28 -23
  47. package/dist/index.js +173 -58
  48. package/dist/lifecycle.d.ts +3 -3
  49. package/dist/lifecycle.js +38 -6
  50. package/dist/local/cli.js +11 -6
  51. package/dist/local/configs.d.ts +74 -18
  52. package/dist/local/configs.js +291 -84
  53. package/dist/local/health.js +7 -7
  54. package/dist/local/index.d.ts +2 -2
  55. package/dist/local/index.js +24 -10
  56. package/dist/local/manager.d.ts +20 -1
  57. package/dist/local/manager.js +159 -36
  58. package/dist/local/ports.d.ts +158 -0
  59. package/dist/local/ports.js +443 -0
  60. package/dist/local/seed.d.ts +13 -5
  61. package/dist/local/seed.js +62 -38
  62. package/dist/logger.d.ts +54 -25
  63. package/dist/logger.js +329 -63
  64. package/dist/mcp.d.ts +2 -2
  65. package/dist/mcp.js +10 -5
  66. package/dist/mirror-bootstrap.d.ts +48 -0
  67. package/dist/mirror-bootstrap.js +254 -0
  68. package/dist/opl.d.ts +289 -0
  69. package/dist/opl.js +446 -0
  70. package/dist/permission-mode.d.ts +87 -0
  71. package/dist/permission-mode.js +307 -0
  72. package/dist/permissions-cli.d.ts +13 -49
  73. package/dist/permissions-cli.js +154 -348
  74. package/dist/permissions.d.ts +148 -38
  75. package/dist/permissions.js +591 -45
  76. package/dist/post-install.d.ts +33 -0
  77. package/dist/post-install.js +127 -0
  78. package/dist/read-credential.d.ts +65 -0
  79. package/dist/read-credential.js +86 -0
  80. package/dist/registry/cli.js +5 -2
  81. package/dist/registry/config.d.ts +0 -17
  82. package/dist/registry/config.js +0 -23
  83. package/dist/registry/index.d.ts +1 -1
  84. package/dist/registry/index.js +2 -2
  85. package/dist/registry/manager.d.ts +4 -21
  86. package/dist/registry/manager.js +21 -37
  87. package/dist/runtime-credential.d.ts +140 -0
  88. package/dist/runtime-credential.js +572 -0
  89. package/dist/runtime.d.ts +408 -0
  90. package/dist/runtime.js +748 -0
  91. package/dist/setup.d.ts +23 -47
  92. package/dist/setup.js +59 -116
  93. package/dist/skills.js +0 -7
  94. package/dist/status-cli.d.ts +27 -11
  95. package/dist/status-cli.js +121 -115
  96. package/dist/status-data.d.ts +116 -17
  97. package/dist/status-data.js +121 -38
  98. package/dist/subject.d.ts +126 -20
  99. package/dist/subject.js +215 -30
  100. package/dist/testing.d.ts +74 -38
  101. package/dist/testing.js +185 -68
  102. package/dist/tool-catalog.d.ts +53 -11
  103. package/dist/tool-catalog.js +164 -13
  104. package/dist/tool-metadata.d.ts +7 -6
  105. package/dist/tool-metadata.js +6 -5
  106. package/dist/types.d.ts +11 -1
  107. package/dist/uninstall.d.ts +71 -34
  108. package/dist/uninstall.js +217 -85
  109. package/dist/user-login.d.ts +9 -10
  110. package/dist/user-login.js +56 -75
  111. package/dist/watch-cli.d.ts +6 -0
  112. package/dist/watch-cli.js +217 -0
  113. package/package.json +4 -27
  114. package/assets/commands/dashboard.md +0 -34
  115. package/dist/dashboard-cli.d.ts +0 -8
  116. package/dist/dashboard-cli.js +0 -70
  117. package/dist/dev.d.ts +0 -103
  118. package/dist/dev.js +0 -583
  119. package/dist/interactive-setup.d.ts +0 -286
  120. package/dist/interactive-setup.js +0 -1734
  121. package/dist/local/jaeger-main.d.ts +0 -13
  122. package/dist/local/jaeger-main.js +0 -85
  123. package/dist/local/jaeger.d.ts +0 -50
  124. package/dist/local/jaeger.js +0 -162
  125. package/dist/otel/exporter.d.ts +0 -17
  126. package/dist/otel/exporter.js +0 -12
  127. package/dist/otel/index.d.ts +0 -2
  128. package/dist/otel/index.js +0 -8
  129. package/dist/otel/otlp.d.ts +0 -103
  130. package/dist/otel/otlp.js +0 -385
  131. package/dist/project-api-key.d.ts +0 -69
  132. package/dist/project-api-key.js +0 -147
  133. package/dist/setup-actions.d.ts +0 -232
  134. package/dist/setup-actions.js +0 -507
  135. package/dist/tracer.d.ts +0 -190
  136. package/dist/tracer.js +0 -481
  137. package/dist/watch-sandbox.d.ts +0 -9
  138. package/dist/watch-sandbox.js +0 -81
  139. package/dist/web/api.d.ts +0 -33
  140. package/dist/web/api.js +0 -294
  141. package/dist/web/launch.d.ts +0 -11
  142. package/dist/web/launch.js +0 -96
  143. package/dist/web/server.d.ts +0 -20
  144. package/dist/web/server.js +0 -233
  145. package/dist/web/types.d.ts +0 -65
  146. package/dist/web/types.js +0 -2
  147. package/dist/webapp/assets/index-Wucl4SZs.css +0 -1
  148. package/dist/webapp/assets/index-m-GtEdq0.js +0 -49
  149. package/dist/webapp/favicon.ico +0 -0
  150. package/dist/webapp/index.html +0 -15
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ory-temporal-worker
3
- description: Scaffold a [Temporal](https://temporal.io) TypeScript worker where every Activity execution is gated by Ory — the user is authenticated, the worker's agent identity is resolved via DCR, each Activity invocation runs an Ory Permission check, and the full lifecycle emits trace spans. Use when the user asks to "add Ory to my Temporal worker", "wire Ory permissions into Temporal activities", "create a Temporal worker with Ory auth", "build a Temporal TypeScript project with the Ory agent client", or any close variant. The skill scaffolds the project in the user's repo following <https://docs.temporal.io/develop/typescript/set-up-your-local-typescript> — it does not run the worker.
3
+ description: Scaffold a [Temporal](https://temporal.io) TypeScript worker where every Activity execution is gated by Ory — the user is authenticated, the worker's agent identity is resolved via DCR, each Activity invocation runs an Ory Permission check, and the full lifecycle emits structured activity events. Use when the user asks to "add Ory to my Temporal worker", "wire Ory permissions into Temporal activities", "create a Temporal worker with Ory auth", "build a Temporal TypeScript project with the Ory agent client", or any close variant. The skill scaffolds the project in the user's repo following <https://docs.temporal.io/develop/typescript/set-up-your-local-typescript> — it does not run the worker.
4
4
  ---
5
5
 
6
6
  # Ory-authed Temporal TypeScript worker
@@ -9,7 +9,7 @@ You are helping the user scaffold a [Temporal](https://temporal.io) TypeScript
9
9
  worker where every Activity execution is gated by Ory: the user is
10
10
  authenticated, the worker's agent identity is resolved via OAuth2 Dynamic Client
11
11
  Registration, each Activity invocation runs an Ory Permission check, and the
12
- full lifecycle emits trace spans. **Workflows stay deterministic** — only
12
+ full lifecycle emits structured activity events. **Workflows stay deterministic** — only
13
13
  Activities call out to Ory.
14
14
 
15
15
  This skill carries the workflow. You generate the files in the user's repo; the
@@ -70,7 +70,7 @@ import {
70
70
  } from "@ory/argus";
71
71
 
72
72
  // One client per worker process. `harness` is a label that shows up on
73
- // every trace span so worker-originated audit lives in its own namespace
73
+ // every activity event so worker-originated audit lives in its own namespace
74
74
  // alongside the CLI plugins.
75
75
  const ory = OryAgentClient.fromEnv("temporal");
76
76
 
@@ -84,7 +84,7 @@ function bootstrap(): Promise<void> {
84
84
  binName: "temporal-worker",
85
85
  harness: "temporal",
86
86
  // The user gate runs every invocation and never blocks: it refreshes
87
- // tokens and emits the audit span, but the worker proceeds even if the
87
+ // tokens and emits the audit event, but the worker proceeds even if the
88
88
  // user is unauthenticated. Enforcement happens at the per-Activity
89
89
  // permission check (permissionMode).
90
90
  });
@@ -102,13 +102,13 @@ async function gate(toolName: string, userSubject: string): Promise<void> {
102
102
  const decision = await checkAndDecide(
103
103
  ory,
104
104
  {
105
- namespace: process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools",
105
+ namespace: process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTool",
106
106
  object: toolName,
107
107
  relation: "use",
108
108
  ...subject,
109
109
  },
110
110
  {
111
- spanAttributes: {
111
+ activityAttributes: {
112
112
  toolName,
113
113
  workflowId: Context.current().info.workflowExecution.workflowId,
114
114
  activityId: Context.current().info.activityId,
@@ -147,9 +147,9 @@ Key choices:
147
147
  identity, but always proceeds. Enforcement is at the permission check, which
148
148
  throws on deny — Temporal will mark the Activity as failed and surface the
149
149
  error via the Workflow result or retry policy.
150
- - **`harness: "temporal"`.** Distinguishes worker-originated spans in the trace
151
- file from CLI plugin spans.
152
- - **Span attributes carry the Workflow + Activity IDs.** This is how operators
150
+ - **`harness: "temporal"`.** Distinguishes worker-originated events in the
151
+ unified activity log.
152
+ - **Activity attributes carry the Workflow + Activity IDs.** This is how operators
153
153
  correlate Ory denials back to Temporal executions in the Web UI.
154
154
 
155
155
  ## Step 4 — Pass the user subject through the Workflow
@@ -215,7 +215,7 @@ cd temporal-worker
215
215
  export ORY_PROJECT_URL=http://localhost:4000
216
216
  export ORY_AUTH_GATE=1
217
217
  export ORY_AGENT_DEBUG=true
218
- export ORY_AGENT_TRACE_FILE=$PWD/ory-trace.ndjson
218
+ export ORY_AGENT_LOG_FILE=$PWD/ory-agent-debug.log
219
219
  npm run start # boots the worker, polls task queue
220
220
  ```
221
221
 
@@ -226,49 +226,47 @@ cd temporal-worker
226
226
  npm run workflow
227
227
  ```
228
228
 
229
- Tail the trace file to confirm the gates fired:
229
+ Tail the activity log to confirm the gates fired:
230
230
 
231
231
  ```bash
232
- tail -f ory-trace.ndjson | jq .
232
+ tail -f ory-agent-debug.log | jq .
233
233
  ```
234
234
 
235
235
  You should see:
236
236
 
237
- - exactly one `user.auth` span (the worker's first Activity triggered
237
+ - exactly one `user.auth` activity event (the worker's first Activity triggered
238
238
  `bootstrap()`),
239
- - exactly one `agent.auth` span,
240
- - one `tool.invoke` (allow) or `tool.block` (deny) span **per Activity
239
+ - exactly one `agent.auth` activity event,
240
+ - one `tool.invoke` (allow) or `tool.block` (deny) event **per Activity
241
241
  execution**.
242
242
 
243
- The Workflow itself produces no Ory spans — only its Activities do.
243
+ 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
247
  The worker starts in `observe` mode by default: denies pass through but each is
248
- recorded as a `permission.observe_deny` audit span. Once the user has confirmed
249
- the deny set is what they expect, promote to enforcement:
248
+ recorded as a `permission.observe_deny` activity event. Once the user has confirmed
249
+ the deny set is what they expect, an admin promotes the posture to `enforce` in
250
+ the Ory Console (Agent Security). It is a permission on the project, read on
251
+ every session, so the worker picks it up on its next run with no redeploy.
250
252
 
251
- ```bash
252
- export ORY_PERMISSION_MODE=enforce
253
- ```
254
-
255
- On a fresh Ory project, run the permissions bootstrap once before flipping the
256
- switch so the `use` tuples for each Activity name (`send_email`, …) exist —
257
- see {{REF_PERMISSIONS_ONBOARDING}}.
253
+ On a hosted Ory project, grant `use` on each Activity name (`send_email`, …) in
254
+ the Ory Console (Agent Security) before promoting the posture there — otherwise
255
+ enforcement blocks every Activity. See {{REF_PERMISSIONS_ONBOARDING}}.
258
256
 
259
- To exercise the deny path locally, write a tuple that explicitly removes `use`
260
- for the test user against one Activity object, kick the Workflow, and watch the
261
- Activity fail with the `Ory denied use of …` error in the Temporal Web UI.
257
+ To exercise the deny path locally, kick the Workflow with an Activity name the
258
+ local stack never granted, and watch it fail with the `Ory denied use of …`
259
+ error in the Temporal Web UI.
262
260
 
263
261
  ## Step 7 — Beyond the dev server
264
262
 
265
263
  This skill stops at the local dev server. For production:
266
264
 
267
- - Pin a static agent identity with `ORY_AGENT_API_KEY` (single key) or
268
- `ORY_AGENT_CLIENT_ID + ORY_AGENT_CLIENT_SECRET` (client_credentials) so the
269
- worker doesn't re-register on every cold start.
270
- - Persist the worker's `ory-trace.ndjson` somewhere durable, or replace the
271
- file tracer with an OpenTelemetry exporter wired up around `ory.tracer`.
265
+ - Pin a static agent identity with `ORY_AGENT_CLIENT_ID +
266
+ ORY_AGENT_CLIENT_SECRET` (client_credentials) so the worker doesn't
267
+ re-register on every cold start.
268
+ - Set retention and access controls for the worker's unified activity log.
269
+ Ory Agent Security owns service telemetry; the worker does not export traces.
272
270
  - Use Temporal Cloud or a self-hosted Temporal cluster instead of
273
271
  `temporal server start-dev`; the worker code does not change.
274
272
 
@@ -1,32 +1,57 @@
1
1
  ---
2
2
  name: ory-permissions-onboarding
3
- description: Onboard a fresh install onto Ory Permissions for AI agent tool calls. Use when the user has just installed the Ory plugin and wants to enforce per-tool authorization without first getting blocked by missing tuples. Walks through observe-mode observation, idempotent tuple bootstrap, and promotion to enforce mode.
3
+ description: Onboard a fresh install onto Ory Permissions for AI agent tool calls. Use when the user has just installed the Ory plugin and wants per-tool authorization enforced without first getting blocked by missing permissions. Covers connecting the plugin, reading coverage in observe mode, and what has to happen in the Ory Console (grants, blocks, enforce posture).
4
4
  ---
5
5
 
6
6
  # Onboard onto Ory Permissions for Agent Tool Calls
7
7
 
8
8
  You are helping a user move a freshly-installed Ory agent plugin from
9
- "permissions are running but never block" (the default after install)
10
- to "permissions are enforcing" (the production posture), without the
11
- common first-run failure mode of getting every tool call blocked
12
- because no tuples exist yet.
9
+ "disconnected" to "permissions are enforcing", without the common
10
+ first-run failure mode of getting every tool call blocked because the
11
+ project has no grants yet.
12
+
13
+ **The plugin only reads permissions. It never writes them.** Everything
14
+ that provisions the project — the permission model, per-tool grants,
15
+ explicit blocks, and the observe/enforce posture — is done in the **Ory
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.
20
+
21
+ Two things govern behavior:
22
+
23
+ - **Whether Agent Security is connected** — it runs when a project URL,
24
+ canonical Agent Security origin, and public OAuth2 client id resolve. Existing
25
+ configurations fall back to the project URL when the canonical origin is
26
+ unset. Not connected means no login and no permission checks, with
27
+ tool calls recorded locally. Skills, commands, the local dev stack, and
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.
34
+
35
+ The journey: **install → connect → observe → grant in the Console →
36
+ enforce in the Console.**
37
+
38
+ ## Step 1: Connect the plugin to the project
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**:
13
43
 
14
- The plugin ships with two orthogonal switches:
15
-
16
- - **`auditOnly`** — a kill switch. When set, Ory is *off entirely* (no
17
- auth, no permission checks). This is for users who want only audit
18
- logging of tool invocations. Not what onboarding is about.
19
- - **`permissionMode`** — the dial this skill is about. `observe` (the
20
- default) runs every permission check, logs denials, but allows the
21
- tool to proceed. `enforce` blocks on deny. Mode is read from
22
- `ORY_PERMISSION_MODE` first, then the shared config file, then
23
- defaults to `observe`.
24
-
25
- The journey: **install → observe → bootstrap → enforce.**
44
+ ```sh
45
+ {{NPX}} configure --project-url <URL> --agent-security-url <URL>
46
+ ```
26
47
 
27
- ## Step 1: Confirm the plugin is installed and configured
48
+ - **project URL** the project's SDK URL (`https://<slug>.projects.oryapis.com`).
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.
28
53
 
29
- Verify the plugin is wired up and pointing at the right Ory project:
54
+ Then confirm what the plugin resolved:
30
55
 
31
56
  ```sh
32
57
  {{NPX}} status
@@ -35,133 +60,135 @@ Verify the plugin is wired up and pointing at the right Ory project:
35
60
  What you want to see:
36
61
 
37
62
  - **Project URL**: a real Ory URL or your local dev gateway (not "NOT SET").
38
- - **API Key**: set (Ory Network) or unset (local Keto OSS — that's fine).
39
- - **Mode**: not "audit-only" that would mean Ory is disabled.
40
-
41
- If any of those are wrong, fix them before continuing:
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.
42
66
 
43
- ```sh
44
- {{NPX}} configure --project-url <URL> --oauth2-client-id <CLIENT_ID> [--api-key <KEY>]
45
- ```
46
-
47
- `--oauth2-client-id` is the public OAuth2 client registered in your Ory project (needed for the per-session user login to complete its PKCE browser flow; see the *Register the user OAuth2 client* section of the plugin README).
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.
48
70
 
49
71
  ## Step 2: Look at the current permission posture
50
72
 
51
73
  ```sh
52
- {{NPX}} permissions status
74
+ {{NPX}} permissions
53
75
  ```
54
76
 
55
77
  This prints:
56
78
 
57
- - The current **permission mode** (`observe` or `enforce`) and where it
58
- was resolved from (env / config / default).
59
- - The **subject** (the user identity tuples are checked against).
79
+ - The live **permission mode** (`observe` or `enforce`) and whether it
80
+ came from the project, a cached value, or the default.
81
+ - The **subject** the user identity permissions are checked against.
60
82
  - For each tool in this harness's built-in catalog, **allowed / denied /
61
- errored** based on a real `checkPermission` call against your Ory
62
- project right now.
83
+ blocked / errored**, from a real check against the project right now.
84
+ - A one-line coverage summary for decomposed shell command words.
63
85
 
64
- On a fresh install you will almost always see every tool reported as
65
- **denied** — because no tuples exist yet for the current user. That's
66
- expected, and in observe mode it is *not blocking anyone*. The tool
67
- calls are running through; you are simply seeing what *would* be
68
- blocked once enforce mode is turned on.
86
+ On a freshly-connected project you will usually see every tool reported
87
+ as **denied** — no grants exist for this user yet. In observe mode that
88
+ blocks nobody: tool calls run through, and you are seeing what *would*
89
+ be blocked under enforce.
69
90
 
70
- If the status command prints `Mode: enforce` but tuples are missing,
71
- stop here: tool calls *are* being blocked right now. Either bootstrap
72
- (next step) or run `{{NPX}} permissions observe` first to flip back to
73
- non-blocking.
91
+ If status prints `Mode: enforce` while tools are denied, tool calls
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.
74
94
 
75
- ## Step 3: Bootstrap tuples for the harness's built-in tools
95
+ ## Step 3: Run a real session in observe mode
76
96
 
77
- Grant the current user `use` on every tool the harness ships with:
97
+ Observe mode is the discovery phase. Use the harness normally, then look
98
+ at what was recorded:
78
99
 
79
100
  ```sh
80
- {{NPX}} permissions bootstrap
101
+ {{NPX}} status # summary + recent activity
102
+ {{NPX}} watch # live event, decision, and error view
81
103
  ```
82
104
 
83
- This is **idempotent** Keto returns 409 on a tuple that already
84
- exists, and the command treats that as success. Safe to re-run any time
85
- (e.g. after the harness adds a new tool to its catalog).
86
-
87
- The command writes `<namespace>:<tool>#use@<userSubject>` for each tool
88
- in the harness's known catalog. MCP server tools are *not* covered —
89
- they are discovered dynamically per session, so they need tuples added
90
- out-of-band as they come into scope.
91
-
92
- ### When bootstrap can't write tuples
105
+ Launch the harness with `ORY_AGENT_DEBUG=true` to include verbose diagnostics in
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.
93
110
 
94
- Two common failure modes:
111
+ ## Step 4: Grant the tools in the Ory Console
95
112
 
96
- 1. **No user identity cached.** Bootstrap needs to know which subject
97
- to grant tuples to. If the user login has never completed (no PKCE login,
98
- no `ORY_USER_SUBJECT_ID`), the command refuses. Run the harness once
99
- (the user login runs every session) to cache a user token, or set
100
- `ORY_USER_SUBJECT_ID=<id>` to target a known subject.
101
- 2. **Credentials lack write scope on the permission namespace.** The
102
- command prints the full tuple list so you can apply them manually
103
- via `keto relation-tuples create`, the Ory Console's *Add
104
- relationship* dialog, or `curl` against Keto's write API. Hand the
105
- list to whoever holds write credentials.
106
-
107
- ### Add tuples for non-default tools
108
-
109
- If the harness has tools beyond the built-in catalog (MCP servers,
110
- custom commands), grant them by writing tuples directly. Use the same
111
- shape:
113
+ In the Ory Console **Agent Security**, grant the subject `use` on the
114
+ tools it needs. The grant shape the runtime checks is:
112
115
 
113
116
  ```
114
- namespace: AgentTools (or whatever ORY_PERMISSION_NAMESPACE is set to)
115
- object: <tool name>
117
+ namespace: AgentTool (or whatever ORY_PERMISSION_NAMESPACE is set to)
118
+ object: <tool name> (e.g. Read, Bash, WebFetch)
116
119
  relation: use
117
- subject: <user subject id>
120
+ subject: User:<id> (a SubjectSet — namespace `User`, object the user id)
118
121
  ```
119
122
 
120
- Re-run `{{NPX}} permissions status` afterwards to confirm coverage.
123
+ Notes worth passing on to the user:
121
124
 
122
- ## Step 4: Promote to enforce mode
125
+ - The subject is a **SubjectSet** (`User:<id>`), which is what the
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.
123
136
 
124
- Once `permissions status` shows the tools you actually use are
125
- `allowed`, flip the dial:
137
+ Re-run `{{NPX}} permissions` afterwards to confirm coverage.
126
138
 
127
- ```sh
128
- {{NPX}} permissions enforce
129
- ```
139
+ ## Step 5: Promote to enforce in the Console
140
+
141
+ Once the tools the user actually needs report `allowed`, an admin flips
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.
130
145
 
131
- This persists `permissionMode = "enforce"` in the shared config file.
132
- Subsequent sessions will block any tool call that returns deny. Mode
133
- can always be flipped back:
146
+ Every session reads the live mode, so the change takes effect without
147
+ reinstalling or reconfiguring anything. Confirm it landed:
134
148
 
135
149
  ```sh
136
- {{NPX}} permissions observe
150
+ {{NPX}} permissions
137
151
  ```
138
152
 
139
- `ORY_PERMISSION_MODE`, when set, overrides the config useful for CI
140
- runs that want enforce regardless of what's persisted, or for one-off
141
- debugging sessions where you want observe without rewriting config.
142
-
143
- ## Step 5: Verify a real session
153
+ ## Step 6: Verify a real session under enforce
144
154
 
145
155
  Start a normal agent session and confirm:
146
156
 
147
157
  - **Allowed tools** invoke without complaint.
148
- - **Denied tools** (if any) are blocked with a clear "Ory: permission
149
- denied" message.
150
- - The trace file shows `permission.check` spans for each tool call and
151
- `tool.block` spans for any denial.
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.
162
+
163
+ If a tool is unexpectedly blocked, run `{{NPX}} permissions` to
164
+ see whether it is `denied` (no grant) or `blocked` (an explicit
165
+ deny-override), then fix it in the Console and re-test.
166
+
167
+ ## Explicit blocks (deny-override)
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.
152
174
 
153
- If a tool is unexpectedly blocked, run `{{NPX}} permissions status` to
154
- confirm whether the tuple exists, then add or fix tuples and re-test.
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.
155
179
 
156
- ## Reference: what each command does
180
+ ## Reference
157
181
 
158
182
  | Command | Effect |
159
183
  |---|---|
160
- | `{{NPX}} permissions status` | Print mode + per-tool allow/deny breakdown for the current user. Read-only. |
161
- | `{{NPX}} permissions bootstrap` | Write `use` tuples for the harness's built-in tools. Idempotent. |
162
- | `{{NPX}} permissions bootstrap --dry-run` | Print what would be written without making any changes. |
163
- | `{{NPX}} permissions observe` | Persist `permissionMode = "observe"` (denies log, allow through). |
164
- | `{{NPX}} permissions enforce` | Persist `permissionMode = "enforce"` (denies block). |
184
+ | `{{NPX}} configure --project-url <URL> --agent-security-url <URL>` | Connect the plugin to the canonical Agent Security broker. |
185
+ | `{{NPX}} configure --disconnect` | Turn sign-in and permission checks back off. Skills, commands, the local stack, and activity logging are unaffected. |
186
+ | `{{NPX}} permissions` | Print the live mode + per-tool allowed/denied/blocked breakdown. Read-only. |
187
+ | `{{NPX}} status` | Configuration, identities, permission coverage, recent activity. |
188
+
189
+ Done in the **Ory Console** (Agent Security), not from the CLI: applying
190
+ the permission model, granting `use` on tools, writing explicit blocks,
191
+ and setting the observe/enforce posture.
165
192
 
166
193
  For deeper background on the authentication side of the flow (which
167
194
  identity is the subject, how the user gate resolves it), see