@ory/argus 0.13.9 → 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 -45
- 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 +43 -97
- package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -18
- package/assets/skills/ory-temporal-worker/SKILL.md +38 -42
- package/assets/skills/permissions-onboarding/SKILL.md +131 -104
- package/dist/adapters.d.ts +93 -30
- package/dist/adapters.js +464 -136
- package/dist/agent-auth.d.ts +258 -68
- package/dist/agent-auth.js +998 -202
- package/dist/auth-store.d.ts +37 -2
- package/dist/auth-store.js +37 -3
- package/dist/auth.d.ts +40 -4
- package/dist/auth.js +247 -19
- package/dist/bash-parser.d.ts +98 -0
- package/dist/bash-parser.js +396 -0
- package/dist/branding.d.ts +128 -0
- package/dist/branding.js +151 -0
- 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 -278
- package/dist/client.d.ts +175 -138
- package/dist/client.js +672 -391
- package/dist/config.d.ts +249 -57
- package/dist/config.js +486 -62
- package/dist/context.d.ts +10 -0
- package/dist/context.js +21 -0
- package/dist/contract-suite.d.ts +8 -8
- package/dist/contract-suite.js +88 -69
- 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 +31 -19
- package/dist/index.js +182 -31
- 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.d.ts +14 -0
- package/dist/local/health.js +50 -4
- 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 +160 -39
- package/dist/local/ports.d.ts +158 -0
- package/dist/local/ports.js +443 -0
- package/dist/local/seed.d.ts +22 -25
- package/dist/local/seed.js +88 -56
- 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 +83 -55
- 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 -28
- package/dist/setup.js +57 -84
- package/dist/status-cli.d.ts +29 -13
- package/dist/status-cli.js +124 -144
- package/dist/status-data.d.ts +195 -0
- package/dist/status-data.js +333 -0
- package/dist/status-system.d.ts +24 -0
- package/dist/status-system.js +56 -0
- 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 +74 -19
- package/dist/uninstall.js +224 -49
- package/dist/user-login.d.ts +22 -16
- package/dist/user-login.js +67 -96
- package/dist/watch-cli.d.ts +6 -0
- package/dist/watch-cli.js +217 -0
- package/package.json +3 -11
- package/dist/dev.d.ts +0 -103
- package/dist/dev.js +0 -584
- package/dist/interactive-setup.d.ts +0 -165
- package/dist/interactive-setup.js +0 -1546
- 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/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/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,45 +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
|
-
await
|
|
46
|
-
binName: "my-harness",
|
|
47
|
-
harness: "my-harness",
|
|
48
|
-
allowBlock: true,
|
|
49
|
-
});
|
|
50
|
-
await ensureAgentIdentity(client, {
|
|
51
|
-
projectUrl: process.env.ORY_PROJECT_URL,
|
|
52
|
-
});
|
|
49
|
+
await sessionStart(client, { harness: "my-harness" });
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
// 2. Before each tool call: check Ory Permissions; block on `deny`.
|
|
56
|
-
async function onBeforeTool(toolName: string, sessionId: string) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
relation: "use",
|
|
61
|
-
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 } : {};
|
|
62
57
|
});
|
|
63
|
-
if (result.allowed === false) {
|
|
64
|
-
return { block: true, reason: result.reason };
|
|
65
|
-
}
|
|
66
58
|
}
|
|
67
59
|
|
|
68
|
-
// 3. After each tool call: record
|
|
69
|
-
async function onAfterTool(toolName: string,
|
|
70
|
-
client
|
|
60
|
+
// 3. After each tool call: record structured completion activity.
|
|
61
|
+
async function onAfterTool(toolName: string, output: unknown) {
|
|
62
|
+
complete(client, { toolName, output });
|
|
71
63
|
}
|
|
72
64
|
```
|
|
73
65
|
|
|
@@ -84,8 +76,8 @@ The wrapped Ory client. One instance per harness session.
|
|
|
84
76
|
| Sessions and tokens | `verifySession`, `introspectToken`, `classifyError` |
|
|
85
77
|
| Permission checks | `checkPermission`, `batchCheckPermissions`, `checkMcpPermission` |
|
|
86
78
|
| Principals (who is acting) | `setUserPrincipal`, `setAgentPrincipal` |
|
|
87
|
-
| Delegation
|
|
88
|
-
|
|
|
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) |
|
|
89
81
|
|
|
90
82
|
### Identity gates
|
|
91
83
|
|
|
@@ -94,23 +86,18 @@ Resolve the user and agent identities at session start. Non-blocking by default;
|
|
|
94
86
|
| Helper | Purpose |
|
|
95
87
|
|---|---|
|
|
96
88
|
| `ensureUserAuthenticated` | Interactive PKCE login, token refresh, or env-token short-circuit |
|
|
97
|
-
| `ensureAgentIdentity` |
|
|
98
|
-
| `ensureSubAgentIdentity` |
|
|
99
|
-
| `resolveUserSubject`, `subjectLabel` | Subject resolution and printable labels for
|
|
100
|
-
|
|
101
|
-
### Tracer
|
|
102
|
-
|
|
103
|
-
`Tracer` is exposed as `client.tracer`. Records every decision as a structured span.
|
|
104
|
-
|
|
105
|
-
- `record(event, attrs)`: emit a span
|
|
106
|
-
- `EventEmitter` `"span"` events for live observers
|
|
107
|
-
- NDJSON file output
|
|
108
|
-
- 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 |
|
|
109
92
|
|
|
110
93
|
### Logger
|
|
111
94
|
|
|
112
|
-
- `DebugLogger`:
|
|
113
|
-
-
|
|
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
|
|
114
101
|
|
|
115
102
|
### Skill and command catalog
|
|
116
103
|
|
|
@@ -125,9 +112,8 @@ Materialize the canonical `SKILL.md` templates into each harness's native skill
|
|
|
125
112
|
### CLI and dev tooling
|
|
126
113
|
|
|
127
114
|
- Shared CLI handlers used by every plugin's CLI: `configure`, `status`, `local`, `setup`
|
|
128
|
-
- `runDevLauncher(...)`: the dev-launcher pipeline used by every plugin
|
|
129
115
|
- Local-stack manager: brings up a local Ory instance in Docker Compose
|
|
130
|
-
- Verdaccio registry manager:
|
|
116
|
+
- Verdaccio registry manager: builds and publishes the workspace for local product development
|
|
131
117
|
|
|
132
118
|
## License
|
|
133
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,45 +75,18 @@ 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 when ORY_USER_LOGIN=true, no-op otherwise.
|
|
90
|
-
const userDecision = await ensureUserAuthenticated(client, {
|
|
91
|
-
binName: "my-agent",
|
|
92
|
-
harness: "my-agent",
|
|
93
|
-
allowBlock: true, // flip to false if your agent can't refuse to start
|
|
94
|
-
});
|
|
95
|
-
if (userDecision.proceed === false) {
|
|
96
|
-
console.error(`Ory user login: ${userDecision.reason}`);
|
|
97
|
-
process.exit(2);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 2. Agent gate — never blocks; resolves machine credentials (DCR by default).
|
|
101
|
-
await ensureAgentIdentity(client, { projectUrl, harness: "my-agent" });
|
|
102
|
-
|
|
103
|
-
// 3. (optional) write the user→agent delegation tuple for audit.
|
|
104
|
-
if (client.userPrincipal.subject && client.agentPrincipal.subject) {
|
|
105
|
-
await client
|
|
106
|
-
.createRelationship({
|
|
107
|
-
namespace: process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools",
|
|
108
|
-
object: `agent:${client.agentPrincipal.subject}`,
|
|
109
|
-
relation: "delegate",
|
|
110
|
-
subjectId: `user:${client.userPrincipal.subject}`,
|
|
111
|
-
})
|
|
112
|
-
.catch(() => undefined); // audit-only — swallow failures
|
|
113
|
-
}
|
|
82
|
+
await sessionStart(client, { harness: "my-agent" });
|
|
114
83
|
```
|
|
115
84
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
85
|
+
The user gate runs on every session and is always non-blocking: it refreshes
|
|
86
|
+
tokens, prompts on TTY, and emits the `user.auth` activity event, but always returns
|
|
87
|
+
`proceed: true`. A missing or declined login never stops the agent — the
|
|
88
|
+
consequence surfaces at the tool gate, where `permissionMode: enforce` denies
|
|
89
|
+
and `observe` audits.
|
|
120
90
|
|
|
121
91
|
## Step 4 — The shared gate body
|
|
122
92
|
|
|
@@ -125,48 +95,25 @@ Put it next to where you construct the client.
|
|
|
125
95
|
|
|
126
96
|
```ts
|
|
127
97
|
import {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
subjectLabel,
|
|
98
|
+
complete,
|
|
99
|
+
gate,
|
|
131
100
|
} from "@ory/argus";
|
|
132
101
|
|
|
133
|
-
async function gateTool(toolName: string,
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
namespace: process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools",
|
|
139
|
-
object: toolName,
|
|
140
|
-
relation: "use",
|
|
141
|
-
...subject,
|
|
142
|
-
},
|
|
143
|
-
{ spanAttributes: { toolName } }
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
switch (decision.kind) {
|
|
147
|
-
case "allow":
|
|
148
|
-
case "observe":
|
|
149
|
-
case "fail_open":
|
|
150
|
-
return { allow: true as const };
|
|
151
|
-
case "deny":
|
|
152
|
-
return {
|
|
153
|
-
allow: false as const,
|
|
154
|
-
message: `Ory denied ${toolName} for ${subjectLabel(subject)}.`,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
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 };
|
|
157
107
|
}
|
|
158
108
|
```
|
|
159
109
|
|
|
160
|
-
After the tool finishes (whichever SDK), record
|
|
110
|
+
After the tool finishes (whichever SDK), record completion activity:
|
|
161
111
|
|
|
162
112
|
```ts
|
|
163
|
-
client
|
|
164
|
-
attributes: { toolName, durationMs },
|
|
165
|
-
});
|
|
113
|
+
complete(client, { toolName, output });
|
|
166
114
|
```
|
|
167
115
|
|
|
168
|
-
`
|
|
169
|
-
(on observe), and `tool.block` (on deny). You only own `tool.complete`.
|
|
116
|
+
`gate` records permission and execution activity; `complete` records the terminal event.
|
|
170
117
|
|
|
171
118
|
## Step 5 — SDK-specific wiring
|
|
172
119
|
|
|
@@ -242,7 +189,7 @@ Agentforce is a declarative agent inside the Salesforce platform — you
|
|
|
242
189
|
operations. The gate runs inside your Node service on every call; denies
|
|
243
190
|
come back as tool errors the agent surfaces to the user.
|
|
244
191
|
|
|
245
|
-
Pre-supply `
|
|
192
|
+
Pre-supply `ORY_USER_OAUTH2_TOKEN` to the
|
|
246
193
|
side-car from a session the user established out-of-band — for example, a
|
|
247
194
|
PKCE flow at sign-on into the Experience Cloud site that fronts the agent.
|
|
248
195
|
A headless side-car cannot run PKCE on its own.
|
|
@@ -340,13 +287,11 @@ Pure-Python agents don't link `@ory/argus` directly. The two supported
|
|
|
340
287
|
patterns:
|
|
341
288
|
|
|
342
289
|
- **Side-car HTTP service.** Run a small Node process that exposes
|
|
343
|
-
`POST /gate` (calls `gateTool`)
|
|
344
|
-
`client.tracer.record(...)`). Your Python agent calls these from inside
|
|
290
|
+
`POST /gate` (calls `gateTool`). Your Python agent calls it from inside
|
|
345
291
|
each `@agent.tool`.
|
|
346
|
-
- **
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
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.
|
|
350
295
|
|
|
351
296
|
Side-car pattern:
|
|
352
297
|
|
|
@@ -404,21 +349,22 @@ The `gateTool` body does not change.
|
|
|
404
349
|
## Step 6 — Test against the local Ory stack
|
|
405
350
|
|
|
406
351
|
Before pointing at production, run the gate against the local stack so the
|
|
407
|
-
PKCE flow, permission tuples, and
|
|
352
|
+
PKCE flow, permission tuples, and activity events are all visible:
|
|
408
353
|
|
|
409
354
|
1. {{REF_LOCAL_UP}} — brings up Kratos / Keto / Hydra on `localhost:4000`
|
|
410
355
|
and seeds a demo user. The banner prints the email + password.
|
|
411
356
|
2. Export the env vars the launcher writes (`ORY_PROJECT_URL`,
|
|
412
|
-
`
|
|
413
|
-
|
|
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.
|
|
414
359
|
3. Start your agent. Confirm the browser opens for PKCE login.
|
|
415
|
-
4. Invoke a gated tool and
|
|
360
|
+
4. Invoke a gated tool and tail `<dataDir>/<harness>/ory-agent-debug.log` with
|
|
361
|
+
`jq` — you
|
|
416
362
|
should see `user.auth` → `agent.auth` → `permission.check` →
|
|
417
363
|
`tool.complete` for every call.
|
|
418
|
-
5. Promote to enforce once the
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
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).
|
|
422
368
|
6. {{REF_LOCAL_DOWN}} when done. Volumes persist, so the seeded user
|
|
423
369
|
survives across runs.
|
|
424
370
|
|
|
@@ -430,9 +376,9 @@ For full env-var coverage (including the user/agent split,
|
|
|
430
376
|
- It does not generate the agent. The user owns the agent loop, tool
|
|
431
377
|
catalog, and deployment shape. This skill only drops `@ory/argus` into
|
|
432
378
|
whatever they already have.
|
|
433
|
-
- It does not
|
|
434
|
-
|
|
435
|
-
|
|
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.
|
|
436
382
|
- It does not adapt one of the existing harness plugins (`@ory/claude-code`,
|
|
437
383
|
`@ory/codex`, `@ory/gemini-cli`, `@ory/openclaw`, `@ory/opencode`). Those
|
|
438
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,9 +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_MODE: "observe",
|
|
71
|
-
ORY_PERMISSION_NAMESPACE: "AgentTools",
|
|
69
|
+
ORY_PERMISSION_NAMESPACE: "AgentTool",
|
|
72
70
|
ORY_AGENT_DEBUG: "true",
|
|
73
71
|
ORY_AGENT_LOG_FILE: "/root/ory-agent-debug.log",
|
|
74
72
|
})
|
|
@@ -115,9 +113,10 @@ E2B_API_KEY=e2b_***
|
|
|
115
113
|
# the template. Listed here so operators know what to plumb through.
|
|
116
114
|
ORY_PROJECT_URL=https://<slug>.projects.oryapis.com
|
|
117
115
|
ORY_OAUTH2_CLIENT_ID=
|
|
118
|
-
|
|
116
|
+
ORY_USER_OAUTH2_TOKEN=
|
|
119
117
|
# Optional: pin a static agent identity instead of DCR.
|
|
120
|
-
#
|
|
118
|
+
# ORY_AGENT_CLIENT_ID=
|
|
119
|
+
# ORY_AGENT_CLIENT_SECRET=
|
|
121
120
|
```
|
|
122
121
|
|
|
123
122
|
### `integrations/e2b/package.json`
|
|
@@ -153,8 +152,8 @@ Generate a README that captures:
|
|
|
153
152
|
`npm run build:prod`.
|
|
154
153
|
- A `Sandbox.create("agent-ory", { envs: { ... } })` example showing which env
|
|
155
154
|
vars to inject at runtime (`ORY_PROJECT_URL`, `ORY_OAUTH2_CLIENT_ID`,
|
|
156
|
-
`
|
|
157
|
-
`
|
|
155
|
+
`ORY_USER_OAUTH2_TOKEN`, optional
|
|
156
|
+
`ORY_AGENT_CLIENT_ID` + `ORY_AGENT_CLIENT_SECRET`).
|
|
158
157
|
- A pointer to {{REF_AUTH_SETUP}} for full env-var coverage and to
|
|
159
158
|
{{REF_LOCAL_DEV}} for testing the same plugin locally before publishing the
|
|
160
159
|
template.
|
|
@@ -190,7 +189,7 @@ const sbx = await Sandbox.create("agent-ory", {
|
|
|
190
189
|
ORY_PROJECT_URL: process.env.ORY_PROJECT_URL!,
|
|
191
190
|
ORY_OAUTH2_CLIENT_ID: process.env.ORY_OAUTH2_CLIENT_ID!,
|
|
192
191
|
// Pre-supply a user token so the headless sandbox skips PKCE.
|
|
193
|
-
|
|
192
|
+
ORY_USER_OAUTH2_TOKEN: process.env.ORY_USER_OAUTH2_TOKEN!,
|
|
194
193
|
},
|
|
195
194
|
});
|
|
196
195
|
|
|
@@ -198,18 +197,19 @@ await sbx.commands.run("{{BIN}} --version"); // sanity check
|
|
|
198
197
|
```
|
|
199
198
|
|
|
200
199
|
Sandboxes are headless, so the user **must** pre-supply
|
|
201
|
-
`
|
|
200
|
+
`ORY_USER_OAUTH2_TOKEN` — otherwise user login's
|
|
202
201
|
PKCE browser flow has no target and hangs. See {{REF_AUTH_SETUP}} for the full
|
|
203
202
|
env-var matrix.
|
|
204
203
|
|
|
205
204
|
## Step 5 — Promotion path
|
|
206
205
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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`.
|
|
213
213
|
|
|
214
214
|
## What this skill does NOT do
|
|
215
215
|
|