@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.
- package/README.md +31 -46
- package/assets/commands/temporal-up.md +1 -1
- package/assets/skills/auth-setup/SKILL.md +1 -1
- package/assets/skills/local-dev/SKILL.md +17 -7
- package/assets/skills/ory-build-agent/SKILL.md +39 -91
- package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -17
- package/assets/skills/ory-temporal-worker/SKILL.md +31 -33
- package/assets/skills/permissions-onboarding/SKILL.md +131 -104
- package/dist/adapters.d.ts +88 -32
- package/dist/adapters.js +443 -164
- package/dist/agent-auth.d.ts +226 -67
- package/dist/agent-auth.js +951 -205
- package/dist/auth-store.d.ts +37 -2
- package/dist/auth-store.js +37 -3
- package/dist/auth.d.ts +33 -4
- package/dist/auth.js +163 -20
- package/dist/bash-parser.d.ts +98 -0
- package/dist/bash-parser.js +396 -0
- package/dist/branding.d.ts +77 -16
- package/dist/branding.js +93 -23
- package/dist/build-info.json +4 -4
- package/dist/cli-invocation.d.ts +1 -1
- package/dist/cli-invocation.js +2 -1
- package/dist/cli.d.ts +20 -29
- package/dist/cli.js +271 -239
- package/dist/client.d.ts +175 -138
- package/dist/client.js +672 -391
- package/dist/config.d.ts +217 -54
- package/dist/config.js +461 -42
- package/dist/context.d.ts +10 -0
- package/dist/context.js +21 -0
- package/dist/contract-suite.d.ts +3 -5
- package/dist/contract-suite.js +75 -47
- package/dist/denial.d.ts +36 -3
- package/dist/denial.js +79 -10
- package/dist/event-reporter.d.ts +77 -0
- package/dist/event-reporter.js +776 -0
- package/dist/external-registrations-main.d.ts +10 -0
- package/dist/external-registrations-main.js +38 -0
- package/dist/external-registrations.d.ts +79 -0
- package/dist/external-registrations.js +188 -0
- package/dist/help-cli.d.ts +39 -0
- package/dist/help-cli.js +55 -0
- package/dist/hook-timeout.d.ts +64 -0
- package/dist/hook-timeout.js +88 -0
- package/dist/index.d.ts +28 -23
- package/dist/index.js +173 -58
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +38 -6
- package/dist/local/cli.js +11 -6
- package/dist/local/configs.d.ts +74 -18
- package/dist/local/configs.js +291 -84
- package/dist/local/health.js +7 -7
- package/dist/local/index.d.ts +2 -2
- package/dist/local/index.js +24 -10
- package/dist/local/manager.d.ts +20 -1
- package/dist/local/manager.js +159 -36
- package/dist/local/ports.d.ts +158 -0
- package/dist/local/ports.js +443 -0
- package/dist/local/seed.d.ts +13 -5
- package/dist/local/seed.js +62 -38
- package/dist/logger.d.ts +54 -25
- package/dist/logger.js +329 -63
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +10 -5
- package/dist/mirror-bootstrap.d.ts +48 -0
- package/dist/mirror-bootstrap.js +254 -0
- package/dist/opl.d.ts +289 -0
- package/dist/opl.js +446 -0
- package/dist/permission-mode.d.ts +87 -0
- package/dist/permission-mode.js +307 -0
- package/dist/permissions-cli.d.ts +13 -49
- package/dist/permissions-cli.js +154 -348
- package/dist/permissions.d.ts +148 -38
- package/dist/permissions.js +591 -45
- package/dist/post-install.d.ts +33 -0
- package/dist/post-install.js +127 -0
- package/dist/read-credential.d.ts +65 -0
- package/dist/read-credential.js +86 -0
- package/dist/registry/cli.js +5 -2
- package/dist/registry/config.d.ts +0 -17
- package/dist/registry/config.js +0 -23
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +2 -2
- package/dist/registry/manager.d.ts +4 -21
- package/dist/registry/manager.js +21 -37
- package/dist/runtime-credential.d.ts +140 -0
- package/dist/runtime-credential.js +572 -0
- package/dist/runtime.d.ts +408 -0
- package/dist/runtime.js +748 -0
- package/dist/setup.d.ts +23 -47
- package/dist/setup.js +59 -116
- package/dist/skills.js +0 -7
- package/dist/status-cli.d.ts +27 -11
- package/dist/status-cli.js +121 -115
- package/dist/status-data.d.ts +116 -17
- package/dist/status-data.js +121 -38
- package/dist/subject.d.ts +126 -20
- package/dist/subject.js +215 -30
- package/dist/testing.d.ts +74 -38
- package/dist/testing.js +185 -68
- package/dist/tool-catalog.d.ts +53 -11
- package/dist/tool-catalog.js +164 -13
- package/dist/tool-metadata.d.ts +7 -6
- package/dist/tool-metadata.js +6 -5
- package/dist/types.d.ts +11 -1
- package/dist/uninstall.d.ts +71 -34
- package/dist/uninstall.js +217 -85
- package/dist/user-login.d.ts +9 -10
- package/dist/user-login.js +56 -75
- package/dist/watch-cli.d.ts +6 -0
- package/dist/watch-cli.js +217 -0
- package/package.json +4 -27
- package/assets/commands/dashboard.md +0 -34
- package/dist/dashboard-cli.d.ts +0 -8
- package/dist/dashboard-cli.js +0 -70
- package/dist/dev.d.ts +0 -103
- package/dist/dev.js +0 -583
- package/dist/interactive-setup.d.ts +0 -286
- package/dist/interactive-setup.js +0 -1734
- package/dist/local/jaeger-main.d.ts +0 -13
- package/dist/local/jaeger-main.js +0 -85
- package/dist/local/jaeger.d.ts +0 -50
- package/dist/local/jaeger.js +0 -162
- package/dist/otel/exporter.d.ts +0 -17
- package/dist/otel/exporter.js +0 -12
- package/dist/otel/index.d.ts +0 -2
- package/dist/otel/index.js +0 -8
- package/dist/otel/otlp.d.ts +0 -103
- package/dist/otel/otlp.js +0 -385
- package/dist/project-api-key.d.ts +0 -69
- package/dist/project-api-key.js +0 -147
- package/dist/setup-actions.d.ts +0 -232
- package/dist/setup-actions.js +0 -507
- package/dist/tracer.d.ts +0 -190
- package/dist/tracer.js +0 -481
- package/dist/watch-sandbox.d.ts +0 -9
- package/dist/watch-sandbox.js +0 -81
- package/dist/web/api.d.ts +0 -33
- package/dist/web/api.js +0 -294
- package/dist/web/launch.d.ts +0 -11
- package/dist/web/launch.js +0 -96
- package/dist/web/server.d.ts +0 -20
- package/dist/web/server.js +0 -233
- package/dist/web/types.d.ts +0 -65
- package/dist/web/types.js +0 -2
- package/dist/webapp/assets/index-Wucl4SZs.css +0 -1
- package/dist/webapp/assets/index-m-GtEdq0.js +0 -49
- package/dist/webapp/favicon.ico +0 -0
- package/dist/webapp/index.html +0 -15
package/README.md
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
# Ory Argus: Agent and Developer Experience
|
|
2
2
|
|
|
3
|
-
The core API behind every Ory Agent Plugin and Extension. Argus wraps [Ory Identities](https://www.ory.com/ory-ecosystem), [Ory Permissions](https://ory.com/permissions), MCP authorization, and
|
|
3
|
+
The core API behind every Ory Agent Plugin and Extension. Argus wraps [Ory Identities](https://www.ory.com/ory-ecosystem), [Ory Permissions](https://ory.com/permissions), MCP authorization, and privacy-safe activity auditing into a single client. Each harness package (`@ory/claude-code`, `@ory/codex`, `@ory/gemini-cli`, and the rest) is a thin adapter that maps one harness's hook contract onto Argus.
|
|
4
4
|
|
|
5
5
|
Argus is also published on its own so you can build new harness plugins or extensions, embed Ory into a custom agent runtime, or instrument any SDK that exposes event lifecycle hooks for session start, tool execution, and tool completion.
|
|
6
6
|
|
|
7
7
|
## Use
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import { OryAgentClient } from "@ory/argus";
|
|
10
|
+
import { OryAgentClient, resolveUserSubject } from "@ory/argus";
|
|
11
11
|
|
|
12
12
|
const client = OryAgentClient.fromEnv("my-harness");
|
|
13
13
|
|
|
14
14
|
const session = await client.verifySession(sessionToken);
|
|
15
15
|
|
|
16
|
+
// Every principal is addressed as a SubjectSet (`User:<id>` by default,
|
|
17
|
+
// `Session:<id>` for the fallback). resolveUserSubject builds the right shape.
|
|
16
18
|
const result = await client.checkPermission({
|
|
17
|
-
namespace: "
|
|
19
|
+
namespace: "AgentTool",
|
|
18
20
|
object: "Bash",
|
|
19
21
|
relation: "use",
|
|
20
|
-
|
|
22
|
+
...resolveUserSubject(client, `session:${sessionId}`),
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
if (!result.allowed) {
|
|
@@ -29,46 +31,35 @@ if (!result.allowed) {
|
|
|
29
31
|
|
|
30
32
|
## Build a new plugin, extension, or custom integration
|
|
31
33
|
|
|
32
|
-
Any agent runtime, framework, or SDK that exposes lifecycle hooks for **session start**, **tool execution**, and **tool completion** can use Argus as its authentication, authorization, and audit layer.
|
|
34
|
+
Any agent runtime, framework, or SDK that exposes lifecycle hooks for **session start**, **tool execution**, and **tool completion** can use Argus as its authentication, authorization, and audit layer. Core adapters own the shared behavior; integrations only translate host fields and block outputs:
|
|
33
35
|
|
|
34
36
|
```typescript
|
|
35
37
|
import {
|
|
38
|
+
complete,
|
|
39
|
+
gate,
|
|
36
40
|
OryAgentClient,
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
sessionStart,
|
|
42
|
+
withHookContext,
|
|
39
43
|
} from "@ory/argus";
|
|
40
44
|
|
|
41
45
|
const client = OryAgentClient.fromEnv("my-harness");
|
|
42
46
|
|
|
43
47
|
// 1. Session start: authenticate the human and the agent process.
|
|
44
48
|
async function onSessionStart() {
|
|
45
|
-
|
|
46
|
-
// for attribution; enforcement happens at the tool gate via permissionMode.
|
|
47
|
-
await ensureUserAuthenticated(client, {
|
|
48
|
-
binName: "my-harness",
|
|
49
|
-
harness: "my-harness",
|
|
50
|
-
});
|
|
51
|
-
await ensureAgentIdentity(client, {
|
|
52
|
-
projectUrl: process.env.ORY_PROJECT_URL,
|
|
53
|
-
});
|
|
49
|
+
await sessionStart(client, { harness: "my-harness" });
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
// 2. Before each tool call: check Ory Permissions; block on `deny`.
|
|
57
|
-
async function onBeforeTool(toolName: string, sessionId: string) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
relation: "use",
|
|
62
|
-
subjectId: `session:${sessionId}`,
|
|
53
|
+
async function onBeforeTool(toolName: string, args: unknown, sessionId: string) {
|
|
54
|
+
return withHookContext(client, { sessionId }, async () => {
|
|
55
|
+
const result = await gate(client, { harness: "my-harness", toolName, toolArgs: args });
|
|
56
|
+
return result.blocked ? { block: true, reason: result.denialMessage } : {};
|
|
63
57
|
});
|
|
64
|
-
if (result.allowed === false) {
|
|
65
|
-
return { block: true, reason: result.reason };
|
|
66
|
-
}
|
|
67
58
|
}
|
|
68
59
|
|
|
69
|
-
// 3. After each tool call: record
|
|
70
|
-
async function onAfterTool(toolName: string,
|
|
71
|
-
client
|
|
60
|
+
// 3. After each tool call: record structured completion activity.
|
|
61
|
+
async function onAfterTool(toolName: string, output: unknown) {
|
|
62
|
+
complete(client, { toolName, output });
|
|
72
63
|
}
|
|
73
64
|
```
|
|
74
65
|
|
|
@@ -85,8 +76,8 @@ The wrapped Ory client. One instance per harness session.
|
|
|
85
76
|
| Sessions and tokens | `verifySession`, `introspectToken`, `classifyError` |
|
|
86
77
|
| Permission checks | `checkPermission`, `batchCheckPermissions`, `checkMcpPermission` |
|
|
87
78
|
| Principals (who is acting) | `setUserPrincipal`, `setAgentPrincipal` |
|
|
88
|
-
| Delegation
|
|
89
|
-
|
|
|
79
|
+
| Delegation | `recordDelegation` (via the Ory Agent Security broker — the only write path; Keto is read-only here) |
|
|
80
|
+
| Activity and debug | `logger` (see Logger below) |
|
|
90
81
|
|
|
91
82
|
### Identity gates
|
|
92
83
|
|
|
@@ -95,23 +86,18 @@ Resolve the user and agent identities at session start. Non-blocking by default;
|
|
|
95
86
|
| Helper | Purpose |
|
|
96
87
|
|---|---|
|
|
97
88
|
| `ensureUserAuthenticated` | Interactive PKCE login, token refresh, or env-token short-circuit |
|
|
98
|
-
| `ensureAgentIdentity` |
|
|
99
|
-
| `ensureSubAgentIdentity` |
|
|
100
|
-
| `resolveUserSubject`, `subjectLabel` | Subject resolution and printable labels for
|
|
101
|
-
|
|
102
|
-
### Tracer
|
|
103
|
-
|
|
104
|
-
`Tracer` is exposed as `client.tracer`. Records every decision as a structured span.
|
|
105
|
-
|
|
106
|
-
- `record(event, attrs)`: emit a span
|
|
107
|
-
- `EventEmitter` `"span"` events for live observers
|
|
108
|
-
- NDJSON file output
|
|
109
|
-
- OTLP / HTTP export
|
|
89
|
+
| `ensureAgentIdentity` | Injected/Talos runtime authentication with OS-store persistence and session-start enrollment |
|
|
90
|
+
| `ensureSubAgentIdentity` | Spawn-scoped child Talos identity issued through the authenticated parent |
|
|
91
|
+
| `resolveUserSubject`, `subjectLabel` | Subject resolution and printable labels for activity and denial messages |
|
|
110
92
|
|
|
111
93
|
### Logger
|
|
112
94
|
|
|
113
|
-
- `DebugLogger`:
|
|
114
|
-
-
|
|
95
|
+
- `DebugLogger`: recursively redacted structured activity plus verbose diagnostics in one NDJSON stream
|
|
96
|
+
- Privacy-safe activity always persists to `<dataDir>/<harness>/ory-agent-debug.log` by default
|
|
97
|
+
- `ORY_AGENT_LOG_FILE` overrides the path; an empty value disables persistence
|
|
98
|
+
- `ORY_AGENT_DEBUG=true` adds the complete live JSON stream to stderr and verbose local diagnostics, including raw shell commands
|
|
99
|
+
- `runWatchCommand` follows the unified log with human-readable event, decision, and error context or raw NDJSON
|
|
100
|
+
- Ory Agent Security owns service telemetry; Argus has no OTel exporter or trace-context surface
|
|
115
101
|
|
|
116
102
|
### Skill and command catalog
|
|
117
103
|
|
|
@@ -126,9 +112,8 @@ Materialize the canonical `SKILL.md` templates into each harness's native skill
|
|
|
126
112
|
### CLI and dev tooling
|
|
127
113
|
|
|
128
114
|
- Shared CLI handlers used by every plugin's CLI: `configure`, `status`, `local`, `setup`
|
|
129
|
-
- `runDevLauncher(...)`: the dev-launcher pipeline used by every plugin
|
|
130
115
|
- Local-stack manager: brings up a local Ory instance in Docker Compose
|
|
131
|
-
- Verdaccio registry manager:
|
|
116
|
+
- Verdaccio registry manager: builds and publishes the workspace for local product development
|
|
132
117
|
|
|
133
118
|
## License
|
|
134
119
|
|
|
@@ -26,7 +26,7 @@ The process runs in the foreground. Stop it with `Ctrl+C`; data is preserved.
|
|
|
26
26
|
For a clean reset, delete `~/.config/temporalio/` and start again.
|
|
27
27
|
|
|
28
28
|
For the matching Ory side of the stack (Identities, Permissions, OAuth2, login
|
|
29
|
-
UI
|
|
29
|
+
UI), use {{REF_LOCAL_UP}}. The two stacks are independent — Temporal
|
|
30
30
|
runs on `:7233/:8233`, Ory runs on `:4000` and friends — so they can run side
|
|
31
31
|
by side without port conflicts.
|
|
32
32
|
|
|
@@ -399,7 +399,7 @@ right place:
|
|
|
399
399
|
route, or in the browser Network tab, is an **app/config** issue — work the
|
|
400
400
|
four contract items and the checks above.
|
|
401
401
|
- **The agent plugin** governs *this coding session* — it authenticates the
|
|
402
|
-
agent, checks Ory Permissions before each tool call, and writes
|
|
402
|
+
agent, checks Ory Permissions before each tool call, and writes activity events. It
|
|
403
403
|
never serves your app's HTTP routes. Diagnose it with
|
|
404
404
|
`{{NPX}} status` and the debug log (`ORY_AGENT_DEBUG=true`), **not** by looking
|
|
405
405
|
at your app's auth pages.
|
|
@@ -38,6 +38,7 @@ After {{REF_LOCAL_UP}} completes:
|
|
|
38
38
|
| Service | URL | Purpose |
|
|
39
39
|
|---------------|----------------------------------|--------------------------------------|
|
|
40
40
|
| Gateway | `http://localhost:4000` | Unified API (mirrors Ory Network) |
|
|
41
|
+
| Agent Security| `https://agents.console.ory:8080`| Canonical permission/delegation broker |
|
|
41
42
|
| Kratos public | `http://localhost:4433` | Self-service flows (login, etc.) |
|
|
42
43
|
| Kratos admin | `http://localhost:4434` | Identity & session admin |
|
|
43
44
|
| Keto read | `http://localhost:4466` | Permission checks |
|
|
@@ -49,7 +50,7 @@ The seed step also produces:
|
|
|
49
50
|
|
|
50
51
|
- Test identity: `agent@ory-local.dev`
|
|
51
52
|
- A live session token (printed at the end of {{REF_LOCAL_UP}})
|
|
52
|
-
- Permission tuples in the `
|
|
53
|
+
- Permission tuples in the `AgentTool` namespace for common tool names
|
|
53
54
|
- An OAuth2 client (`ory-agent-plugins-local`, `client_credentials` grant)
|
|
54
55
|
|
|
55
56
|
## Step 1: Verify Docker is running
|
|
@@ -73,10 +74,13 @@ Tell the user to run {{REF_LOCAL_UP}}, or the equivalent shell command:
|
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
Wait for the command to print "All services are running!" and the seed
|
|
76
|
-
output.
|
|
77
|
+
output. Configure these connection values:
|
|
77
78
|
|
|
78
79
|
- `ORY_PROJECT_URL` (always `http://localhost:4000`)
|
|
79
|
-
- `
|
|
80
|
+
- `ORY_AGENT_SECURITY_URL` (`https://agents.console.ory:8080`; run the Agent Security service separately)
|
|
81
|
+
- `ORY_OAUTH2_CLIENT_ID` (the seeded PKCE login client)
|
|
82
|
+
- the seeded user's email + password, which you type into the login UI
|
|
83
|
+
when the browser opens
|
|
80
84
|
|
|
81
85
|
If the user wants to skip seeding (to bring their own data), use
|
|
82
86
|
`{{NPX}} local up --no-seed` instead.
|
|
@@ -134,14 +138,20 @@ Two ways to exercise the flows:
|
|
|
134
138
|
with password `ory-agent-local-dev-password!` to test session-bearing
|
|
135
139
|
pages without going through registration.
|
|
136
140
|
|
|
137
|
-
For automated tests against the running stack,
|
|
138
|
-
token
|
|
141
|
+
For automated tests against the running stack, log in through Kratos to
|
|
142
|
+
get a session token and call the session API with it:
|
|
139
143
|
|
|
140
144
|
```bash
|
|
141
|
-
|
|
145
|
+
KRATOS_SESSION_TOKEN=$(...) # from a Kratos login flow
|
|
146
|
+
curl -H "X-Session-Token: $KRATOS_SESSION_TOKEN" \
|
|
142
147
|
http://localhost:4000/sessions/whoami
|
|
143
148
|
```
|
|
144
149
|
|
|
150
|
+
That token is a **Kratos session token** — it authenticates the Kratos
|
|
151
|
+
session API only. It is not what the plugin uses: the plugin's env
|
|
152
|
+
credential (`ORY_USER_OAUTH2_TOKEN`) must be an OAuth2 access token,
|
|
153
|
+
because it authenticates delegation and can bootstrap the agent's OAuth2 client.
|
|
154
|
+
|
|
145
155
|
## Step 6: Iterate
|
|
146
156
|
|
|
147
157
|
Common day-to-day commands:
|
|
@@ -191,7 +201,7 @@ plugin updates the bundled service configs.
|
|
|
191
201
|
config change is needed. For non-localhost dev URLs, switch back to
|
|
192
202
|
the Ory Tunnel + a Network project.
|
|
193
203
|
- **Permission checks always deny** — the seeded tuples live in the
|
|
194
|
-
`
|
|
204
|
+
`AgentTool` namespace. If your app uses a different namespace, set
|
|
195
205
|
`ORY_PERMISSION_NAMESPACE` before `local seed`, or write the tuples
|
|
196
206
|
manually via the Keto write API on `:4467`.
|
|
197
207
|
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ory-build-agent
|
|
3
|
-
description: Build your own AI agent that authenticates the user, authorizes every tool call against Ory Permissions, and
|
|
3
|
+
description: Build your own AI agent that authenticates the user, authorizes every tool call against Ory Permissions, and records structured activity — by dropping `@ory/argus` directly into the Claude Agent SDK, OpenAI Agents SDK, Mastra, Vercel AI SDK, PydanticAI / LangGraph, or as an external service called by Salesforce Agentforce. Use when the user wants to wire Ory into a custom agent they own — phrases like "add Ory to my own agent", "build a custom agent with Ory auth", "wrap my Claude Agent SDK tools with Ory permissions", "OpenAI Agents SDK with Ory", "Mastra agent with Ory permissions", "Agentforce action with Ory", "use `@ory/argus` directly". For wiring Ory into an existing agent harness (Claude Code, Codex, Gemini CLI, OpenClaw, OpenCode) use the corresponding `@ory/<harness>` plugin instead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Build your own agent with `@ory/argus`
|
|
7
7
|
|
|
8
|
-
You are helping the user wire Ory Identities, Permissions, and
|
|
8
|
+
You are helping the user wire Ory Identities, Permissions, and activity auditing into
|
|
9
9
|
**an agent they are building themselves**. They are not extending Claude Code,
|
|
10
10
|
Codex, or one of the other harness plugins — they own the agent loop and
|
|
11
11
|
choose where to intercept tool calls.
|
|
12
12
|
|
|
13
13
|
The integration is the same three moves regardless of SDK:
|
|
14
14
|
|
|
15
|
-
1. **
|
|
16
|
-
|
|
17
|
-
2. **
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
3. **Permission check on every tool call.** Wrap the SDK's tool dispatch with
|
|
21
|
-
`checkAndDecide(client, …)` and branch on `decision.kind`. Record a
|
|
22
|
-
`tool.complete` span after the tool returns.
|
|
15
|
+
1. **Session start.** `sessionStart(client, …)` runs user and agent identity,
|
|
16
|
+
delegation, permission-mode warming, and fail-open handling.
|
|
17
|
+
2. **Permission check on every tool call.** Wrap the SDK's tool dispatch with
|
|
18
|
+
`gate(client, …)` and translate `result.blocked` into the SDK's veto signal.
|
|
19
|
+
3. **Completion.** Call `complete(client, …)` after the tool returns.
|
|
23
20
|
|
|
24
21
|
`@ory/argus` ships every helper and handles fail-open semantics (network
|
|
25
22
|
errors, rate limits, unconfigured project → allow). The SDKs differ only in
|
|
@@ -52,7 +49,7 @@ Also establish:
|
|
|
52
49
|
|
|
53
50
|
- **Interactive vs headless.** Desktop / terminal agents can run PKCE login.
|
|
54
51
|
Headless services (CI, daemons, Salesforce side-cars) must pre-supply
|
|
55
|
-
`
|
|
52
|
+
`ORY_USER_OAUTH2_TOKEN`.
|
|
56
53
|
- **Which tools to gate.** Usually all of them. Some SDKs have built-in
|
|
57
54
|
"safe" steps (an LLM-only reasoning step, a model-provided memory tool)
|
|
58
55
|
that don't need a permission check.
|
|
@@ -70,7 +67,7 @@ That's the only Ory dependency you need. The Ory SDK clients
|
|
|
70
67
|
(`@ory/client`) and the OAuth2/PKCE plumbing are re-exported and ready to
|
|
71
68
|
use.
|
|
72
69
|
|
|
73
|
-
## Step 3 — Construct the client and
|
|
70
|
+
## Step 3 — Construct the client and start the session
|
|
74
71
|
|
|
75
72
|
Put this at the top of the agent's bootstrap, before the agent loop starts
|
|
76
73
|
processing the first message:
|
|
@@ -78,40 +75,15 @@ processing the first message:
|
|
|
78
75
|
```ts
|
|
79
76
|
import {
|
|
80
77
|
OryAgentClient,
|
|
81
|
-
|
|
82
|
-
ensureAgentIdentity,
|
|
83
|
-
resolveConfig,
|
|
78
|
+
sessionStart,
|
|
84
79
|
} from "@ory/argus";
|
|
85
80
|
|
|
86
81
|
const client = OryAgentClient.fromEnv("my-agent");
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// 1. User gate — interactive PKCE; runs every session and never blocks.
|
|
90
|
-
// Establishes the user identity for attribution; a declined/skipped login
|
|
91
|
-
// is audited and the flow proceeds (enforcement is at the tool gate).
|
|
92
|
-
await ensureUserAuthenticated(client, {
|
|
93
|
-
binName: "my-agent",
|
|
94
|
-
harness: "my-agent",
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// 2. Agent gate — never blocks; resolves machine credentials (DCR by default).
|
|
98
|
-
await ensureAgentIdentity(client, { projectUrl, harness: "my-agent" });
|
|
99
|
-
|
|
100
|
-
// 3. (optional) write the user→agent delegation tuple for audit.
|
|
101
|
-
if (client.userPrincipal.subject && client.agentPrincipal.subject) {
|
|
102
|
-
await client
|
|
103
|
-
.createRelationship({
|
|
104
|
-
namespace: process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools",
|
|
105
|
-
object: `agent:${client.agentPrincipal.subject}`,
|
|
106
|
-
relation: "delegate",
|
|
107
|
-
subjectId: `user:${client.userPrincipal.subject}`,
|
|
108
|
-
})
|
|
109
|
-
.catch(() => undefined); // audit-only — swallow failures
|
|
110
|
-
}
|
|
82
|
+
await sessionStart(client, { harness: "my-agent" });
|
|
111
83
|
```
|
|
112
84
|
|
|
113
85
|
The user gate runs on every session and is always non-blocking: it refreshes
|
|
114
|
-
tokens, prompts on TTY, and emits the `user.auth`
|
|
86
|
+
tokens, prompts on TTY, and emits the `user.auth` activity event, but always returns
|
|
115
87
|
`proceed: true`. A missing or declined login never stops the agent — the
|
|
116
88
|
consequence surfaces at the tool gate, where `permissionMode: enforce` denies
|
|
117
89
|
and `observe` audits.
|
|
@@ -123,48 +95,25 @@ Put it next to where you construct the client.
|
|
|
123
95
|
|
|
124
96
|
```ts
|
|
125
97
|
import {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
subjectLabel,
|
|
98
|
+
complete,
|
|
99
|
+
gate,
|
|
129
100
|
} from "@ory/argus";
|
|
130
101
|
|
|
131
|
-
async function gateTool(toolName: string,
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
namespace: process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools",
|
|
137
|
-
object: toolName,
|
|
138
|
-
relation: "use",
|
|
139
|
-
...subject,
|
|
140
|
-
},
|
|
141
|
-
{ spanAttributes: { toolName } }
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
switch (decision.kind) {
|
|
145
|
-
case "allow":
|
|
146
|
-
case "observe":
|
|
147
|
-
case "fail_open":
|
|
148
|
-
return { allow: true as const };
|
|
149
|
-
case "deny":
|
|
150
|
-
return {
|
|
151
|
-
allow: false as const,
|
|
152
|
-
message: `Ory denied ${toolName} for ${subjectLabel(subject)}.`,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
102
|
+
async function gateTool(toolName: string, toolArgs?: unknown) {
|
|
103
|
+
const result = await gate(client, { harness: "my-agent", toolName, toolArgs });
|
|
104
|
+
return result.blocked
|
|
105
|
+
? { allow: false as const, message: result.denialMessage }
|
|
106
|
+
: { allow: true as const };
|
|
155
107
|
}
|
|
156
108
|
```
|
|
157
109
|
|
|
158
|
-
After the tool finishes (whichever SDK), record
|
|
110
|
+
After the tool finishes (whichever SDK), record completion activity:
|
|
159
111
|
|
|
160
112
|
```ts
|
|
161
|
-
client
|
|
162
|
-
attributes: { toolName, durationMs },
|
|
163
|
-
});
|
|
113
|
+
complete(client, { toolName, output });
|
|
164
114
|
```
|
|
165
115
|
|
|
166
|
-
`
|
|
167
|
-
(on observe), and `tool.block` (on deny). You only own `tool.complete`.
|
|
116
|
+
`gate` records permission and execution activity; `complete` records the terminal event.
|
|
168
117
|
|
|
169
118
|
## Step 5 — SDK-specific wiring
|
|
170
119
|
|
|
@@ -240,7 +189,7 @@ Agentforce is a declarative agent inside the Salesforce platform — you
|
|
|
240
189
|
operations. The gate runs inside your Node service on every call; denies
|
|
241
190
|
come back as tool errors the agent surfaces to the user.
|
|
242
191
|
|
|
243
|
-
Pre-supply `
|
|
192
|
+
Pre-supply `ORY_USER_OAUTH2_TOKEN` to the
|
|
244
193
|
side-car from a session the user established out-of-band — for example, a
|
|
245
194
|
PKCE flow at sign-on into the Experience Cloud site that fronts the agent.
|
|
246
195
|
A headless side-car cannot run PKCE on its own.
|
|
@@ -338,13 +287,11 @@ Pure-Python agents don't link `@ory/argus` directly. The two supported
|
|
|
338
287
|
patterns:
|
|
339
288
|
|
|
340
289
|
- **Side-car HTTP service.** Run a small Node process that exposes
|
|
341
|
-
`POST /gate` (calls `gateTool`)
|
|
342
|
-
`client.tracer.record(...)`). Your Python agent calls these from inside
|
|
290
|
+
`POST /gate` (calls `gateTool`). Your Python agent calls it from inside
|
|
343
291
|
each `@agent.tool`.
|
|
344
|
-
- **
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
them in Python.
|
|
292
|
+
- **Native Python integration.** Use `ory-argus`, which routes checks through
|
|
293
|
+
the canonical Agent Security broker and preserves the same fail-open,
|
|
294
|
+
observe/enforce, identity, and activity behavior as the TypeScript core.
|
|
348
295
|
|
|
349
296
|
Side-car pattern:
|
|
350
297
|
|
|
@@ -402,21 +349,22 @@ The `gateTool` body does not change.
|
|
|
402
349
|
## Step 6 — Test against the local Ory stack
|
|
403
350
|
|
|
404
351
|
Before pointing at production, run the gate against the local stack so the
|
|
405
|
-
PKCE flow, permission tuples, and
|
|
352
|
+
PKCE flow, permission tuples, and activity events are all visible:
|
|
406
353
|
|
|
407
354
|
1. {{REF_LOCAL_UP}} — brings up Kratos / Keto / Hydra on `localhost:4000`
|
|
408
355
|
and seeds a demo user. The banner prints the email + password.
|
|
409
356
|
2. Export the env vars the launcher writes (`ORY_PROJECT_URL`,
|
|
410
|
-
`ORY_OAUTH2_CLIENT_ID`,
|
|
411
|
-
|
|
357
|
+
`ORY_OAUTH2_CLIENT_ID`, and optionally `ORY_AGENT_LOG_FILE` to override the
|
|
358
|
+
default unified NDJSON log). The user login runs every session.
|
|
412
359
|
3. Start your agent. Confirm the browser opens for PKCE login.
|
|
413
|
-
4. Invoke a gated tool and
|
|
360
|
+
4. Invoke a gated tool and tail `<dataDir>/<harness>/ory-agent-debug.log` with
|
|
361
|
+
`jq` — you
|
|
414
362
|
should see `user.auth` → `agent.auth` → `permission.check` →
|
|
415
363
|
`tool.complete` for every call.
|
|
416
|
-
5. Promote to enforce once the
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
permissions
|
|
364
|
+
5. Promote to enforce once the tools are granted. The posture is a
|
|
365
|
+
permission on the Ory project, read on every session — an admin sets it
|
|
366
|
+
in the Ory Console (Agent Security); see the resolved value with
|
|
367
|
+
`... permissions` via any harness CLI (same shared config file).
|
|
420
368
|
6. {{REF_LOCAL_DOWN}} when done. Volumes persist, so the seeded user
|
|
421
369
|
survives across runs.
|
|
422
370
|
|
|
@@ -428,9 +376,9 @@ For full env-var coverage (including the user/agent split,
|
|
|
428
376
|
- It does not generate the agent. The user owns the agent loop, tool
|
|
429
377
|
catalog, and deployment shape. This skill only drops `@ory/argus` into
|
|
430
378
|
whatever they already have.
|
|
431
|
-
- It does not
|
|
432
|
-
|
|
433
|
-
|
|
379
|
+
- It does not grant permissions. Those are provisioned in the Ory Console
|
|
380
|
+
(Agent Security); `@ory/argus` only reads them. Locally, the dev stack's
|
|
381
|
+
seed step grants the catalog for you.
|
|
434
382
|
- It does not adapt one of the existing harness plugins (`@ory/claude-code`,
|
|
435
383
|
`@ory/codex`, `@ory/gemini-cli`, `@ory/openclaw`, `@ory/opencode`). Those
|
|
436
384
|
are for users running those harnesses — not building a custom agent.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ory-e2b-sandbox
|
|
3
|
-
description: Scaffold an E2B (e2b.dev) sandbox template that boots with {{PKG}} preinstalled, so every Claude/Codex/Gemini/OpenClaw/OpenCode session running inside the sandbox is gated by Ory auth
|
|
3
|
+
description: Scaffold an E2B (e2b.dev) sandbox template that boots with {{PKG}} preinstalled, so every Claude/Codex/Gemini/OpenClaw/OpenCode session running inside the sandbox is gated by Ory auth and permissions and records structured activity without any per-sandbox setup. Use when the user asks to "create an E2B sandbox with Ory agent security", "build an E2B template with the Ory plugin", "make an E2B image that includes Ory auth", or any close variant. The skill generates the template files in the user's project — it does not deploy them.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# E2B sandbox with Ory agent security
|
|
@@ -9,8 +9,8 @@ You are helping the user scaffold an [E2B](https://e2b.dev) sandbox template
|
|
|
9
9
|
that preinstalls and registers the {{PKG}} plugin. The resulting template
|
|
10
10
|
publishes a named image; every `Sandbox.create("<tag>")` call from their SDK
|
|
11
11
|
gets a runtime where the agent's tool calls are already authenticated against
|
|
12
|
-
Ory Identities, authorized against Ory Permissions, and
|
|
13
|
-
|
|
12
|
+
Ory Identities, authorized against Ory Permissions, and recorded as structured
|
|
13
|
+
activity events — with **no** install step at sandbox boot.
|
|
14
14
|
|
|
15
15
|
This skill carries the full workflow and the file contents. You generate the
|
|
16
16
|
files in the user's repo; the user runs the build.
|
|
@@ -66,8 +66,7 @@ export const template = Template()
|
|
|
66
66
|
// Sandbox runtime defaults. Per-tenant secrets (project URL, tokens, client
|
|
67
67
|
// IDs) MUST be passed at Sandbox.create() time, never baked into the image.
|
|
68
68
|
.setEnvs({
|
|
69
|
-
|
|
70
|
-
ORY_PERMISSION_NAMESPACE: "AgentTools",
|
|
69
|
+
ORY_PERMISSION_NAMESPACE: "AgentTool",
|
|
71
70
|
ORY_AGENT_DEBUG: "true",
|
|
72
71
|
ORY_AGENT_LOG_FILE: "/root/ory-agent-debug.log",
|
|
73
72
|
})
|
|
@@ -114,9 +113,10 @@ E2B_API_KEY=e2b_***
|
|
|
114
113
|
# the template. Listed here so operators know what to plumb through.
|
|
115
114
|
ORY_PROJECT_URL=https://<slug>.projects.oryapis.com
|
|
116
115
|
ORY_OAUTH2_CLIENT_ID=
|
|
117
|
-
|
|
116
|
+
ORY_USER_OAUTH2_TOKEN=
|
|
118
117
|
# Optional: pin a static agent identity instead of DCR.
|
|
119
|
-
#
|
|
118
|
+
# ORY_AGENT_CLIENT_ID=
|
|
119
|
+
# ORY_AGENT_CLIENT_SECRET=
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
### `integrations/e2b/package.json`
|
|
@@ -152,8 +152,8 @@ Generate a README that captures:
|
|
|
152
152
|
`npm run build:prod`.
|
|
153
153
|
- A `Sandbox.create("agent-ory", { envs: { ... } })` example showing which env
|
|
154
154
|
vars to inject at runtime (`ORY_PROJECT_URL`, `ORY_OAUTH2_CLIENT_ID`,
|
|
155
|
-
`
|
|
156
|
-
`
|
|
155
|
+
`ORY_USER_OAUTH2_TOKEN`, optional
|
|
156
|
+
`ORY_AGENT_CLIENT_ID` + `ORY_AGENT_CLIENT_SECRET`).
|
|
157
157
|
- A pointer to {{REF_AUTH_SETUP}} for full env-var coverage and to
|
|
158
158
|
{{REF_LOCAL_DEV}} for testing the same plugin locally before publishing the
|
|
159
159
|
template.
|
|
@@ -189,7 +189,7 @@ const sbx = await Sandbox.create("agent-ory", {
|
|
|
189
189
|
ORY_PROJECT_URL: process.env.ORY_PROJECT_URL!,
|
|
190
190
|
ORY_OAUTH2_CLIENT_ID: process.env.ORY_OAUTH2_CLIENT_ID!,
|
|
191
191
|
// Pre-supply a user token so the headless sandbox skips PKCE.
|
|
192
|
-
|
|
192
|
+
ORY_USER_OAUTH2_TOKEN: process.env.ORY_USER_OAUTH2_TOKEN!,
|
|
193
193
|
},
|
|
194
194
|
});
|
|
195
195
|
|
|
@@ -197,18 +197,19 @@ await sbx.commands.run("{{BIN}} --version"); // sanity check
|
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
Sandboxes are headless, so the user **must** pre-supply
|
|
200
|
-
`
|
|
200
|
+
`ORY_USER_OAUTH2_TOKEN` — otherwise user login's
|
|
201
201
|
PKCE browser flow has no target and hangs. See {{REF_AUTH_SETUP}} for the full
|
|
202
202
|
env-var matrix.
|
|
203
203
|
|
|
204
204
|
## Step 5 — Promotion path
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
A sandbox starts in observe mode, so first-run sandboxes never block. The
|
|
207
|
+
deny posture is a property of the **Ory project**, read on every session: an
|
|
208
|
+
admin promotes it to `enforce` in the Ory Console (Agent Security), project-wide
|
|
209
|
+
or for a single principal, and running sandboxes pick that up without a rebuild.
|
|
210
|
+
Grant `use` on the tools first, also in the Console — otherwise flipping to
|
|
211
|
+
enforce blocks everything. Check what a sandbox resolves with
|
|
212
|
+
`{{NPX}} permissions`.
|
|
212
213
|
|
|
213
214
|
## What this skill does NOT do
|
|
214
215
|
|