@ory/argus 1.2.3 → 1.3.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 +25 -0
- package/assets/skills/ory-build-agent/SKILL.md +1 -1
- package/assets/skills/ory-deploy-agent/SKILL.md +315 -0
- package/assets/skills/ory-deploy-openai-e2b/SKILL.md +289 -0
- package/assets/skills/ory-e2b-sandbox/SKILL.md +10 -6
- package/assets/skills/ory-temporal-worker/SKILL.md +4 -4
- package/dist/adapters.d.ts +11 -3
- package/dist/adapters.js +55 -13
- package/dist/agent-auth.d.ts +18 -7
- package/dist/agent-auth.js +120 -10
- package/dist/auth-store.d.ts +3 -3
- package/dist/auth-store.js +56 -12
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +1 -1
- package/dist/bash-parser.js +212 -47
- package/dist/build-info.json +4 -4
- package/dist/cli.js +5 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +24 -7
- package/dist/config.d.ts +23 -3
- package/dist/config.js +202 -18
- package/dist/contract-suite.d.ts +0 -8
- package/dist/contract-suite.js +0 -1
- package/dist/event-reporter.d.ts +1 -0
- package/dist/event-reporter.js +19 -2
- package/dist/hook-timeout.d.ts +2 -2
- package/dist/hook-timeout.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +13 -6
- package/dist/lifecycle.js +2 -1
- package/dist/local/configs.d.ts +2 -4
- package/dist/local/configs.js +2 -4
- package/dist/local/manager.js +4 -4
- package/dist/local/seed.d.ts +4 -4
- package/dist/local/seed.js +8 -10
- package/dist/permissions.d.ts +3 -3
- package/dist/permissions.js +26 -1
- package/dist/runtime-credential.d.ts +35 -0
- package/dist/runtime-credential.js +252 -1
- package/dist/runtime.d.ts +19 -12
- package/dist/runtime.js +69 -21
- package/dist/setup.js +3 -3
- package/dist/skills.d.ts +3 -5
- package/dist/skills.js +14 -5
- package/dist/subject.d.ts +2 -3
- package/dist/subject.js +10 -11
- package/dist/testing.d.ts +17 -0
- package/dist/testing.js +53 -0
- package/dist/tool-catalog.d.ts +17 -15
- package/dist/tool-catalog.js +239 -52
- package/dist/user-login.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,31 @@ Resolve the user and agent identities at session start. Non-blocking by default;
|
|
|
90
90
|
| `ensureSubAgentIdentity` | Spawn-scoped child Talos identity issued through the authenticated parent |
|
|
91
91
|
| `resolveUserSubject`, `subjectLabel` | Subject resolution and printable labels for activity and denial messages |
|
|
92
92
|
|
|
93
|
+
For SPIFFE deployments, call
|
|
94
|
+
`createSpiffeFileRequestAuthenticator({ generationPath, generationFile,
|
|
95
|
+
expectedServerSpiffeId })`. `expectedServerSpiffeId` requires that exact URI SAN on
|
|
96
|
+
the peer leaf certificate; normal CA-chain verification remains enabled, while DNS
|
|
97
|
+
SANs and other workload identities in the same bundle do not satisfy the check.
|
|
98
|
+
The helper returns a concrete `RuntimeRequestAuthenticator`. The generation directory
|
|
99
|
+
defaults to `svid.pem`, `svid-key.pem`, and `bundle.pem`; names are configurable.
|
|
100
|
+
It uses TLS 1.3 mutual TLS and never adds a forwarded identity header. Atomically
|
|
101
|
+
switch the generation directory symlink to rotate all three PEM files without mixing
|
|
102
|
+
generations or restarting the process. When `generationFile` is set, its contents are
|
|
103
|
+
also checked before and after each snapshot. Hosted Web-PKI servers must explicitly
|
|
104
|
+
set `verifyHostname: true` instead of `expectedServerSpiffeId`; that mode reads the
|
|
105
|
+
bundle as part of the material snapshot but uses system trust roots and ordinary
|
|
106
|
+
HTTPS hostname verification. Server-SPIFFE mode trusts the workload bundle and
|
|
107
|
+
requires the exact sole URI SAN. Exactly one identity mode is required.
|
|
108
|
+
The legacy three-path form remains available but cannot guarantee a coherent snapshot.
|
|
109
|
+
One request setup is retried if rotation races with certificate/key validation.
|
|
110
|
+
|
|
111
|
+
Headless deployments normally supply only `ORY_AGENT_API_KEY` (or an injected runtime
|
|
112
|
+
authenticator such as SPIFFE) plus `ORY_PROJECT_URL` and `ORY_AGENT_SECURITY_URL`.
|
|
113
|
+
Before PKCE, Argus resolves the administrator-provisioned subject and opaque root from
|
|
114
|
+
Agent Security, keeps them in process memory, and reuses the root unchanged for child
|
|
115
|
+
lineage. `ORY_AGENT_SUBJECT_ID` and `ORY_AGENT_ROOT_DELEGATION` remain compatibility
|
|
116
|
+
overrides; a complete legacy package skips the context request.
|
|
117
|
+
|
|
93
118
|
### Logger
|
|
94
119
|
|
|
95
120
|
- `DebugLogger`: recursively redacted structured activity plus verbose diagnostics in one NDJSON stream
|
|
@@ -370,7 +370,7 @@ PKCE flow, permission tuples, and activity events are all visible:
|
|
|
370
370
|
survives across runs.
|
|
371
371
|
|
|
372
372
|
For full env-var coverage (including the user/agent split,
|
|
373
|
-
`ORY_USER_SUBJECT_NAMESPACE`,
|
|
373
|
+
`ORY_USER_SUBJECT_NAMESPACE`, and `ORY_AGENT_API_KEY`), see {{REF_AUTH_SETUP}}.
|
|
374
374
|
|
|
375
375
|
## What this skill does NOT do
|
|
376
376
|
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ory-deploy-agent
|
|
3
|
+
description: Build and deploy a headless TypeScript agent through Ory Agent Security using the Claude Agent SDK, `@ory/claude-agent-sdk`, the `ory-agent` deployment CLI, and either a generated Talos credential or customer-operated SPIFFE on Generic Docker or Kubernetes. Use for an end-to-end managed agent deployment or Agent Security deployment validation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build and deploy an Agent Security managed agent
|
|
7
|
+
|
|
8
|
+
Use this playbook for the canonical validated application stack:
|
|
9
|
+
|
|
10
|
+
- TypeScript on Node.js 22
|
|
11
|
+
- `@anthropic-ai/claude-agent-sdk` with Ory's `createOryHooks`
|
|
12
|
+
- Generic Docker with Docker Compose, or Kubernetes
|
|
13
|
+
- Agent Security's `talos_api_key`, or customer-operated `spiffe_x509_svid`
|
|
14
|
+
|
|
15
|
+
Do not substitute another SDK in this workflow. Choose one runtime and credential
|
|
16
|
+
path deliberately; do not combine Talos and SPIFFE credentials.
|
|
17
|
+
Use `ory-build-agent` when the user asks for a different integration.
|
|
18
|
+
Use {{REF_DEPLOY_OPENAI_E2B}} when the user asks to deploy a Python OpenAI
|
|
19
|
+
Agents SDK application on E2B; that skill owns the Talos and sandbox lifecycle.
|
|
20
|
+
|
|
21
|
+
## Safety
|
|
22
|
+
|
|
23
|
+
Work autonomously, but ask before overwriting an existing application. Never
|
|
24
|
+
print, inspect, commit, or place in a prompt:
|
|
25
|
+
|
|
26
|
+
- `ORY_USER_OAUTH2_TOKEN`
|
|
27
|
+
- `ANTHROPIC_API_KEY`
|
|
28
|
+
- any value from `.ory/credentials.env`
|
|
29
|
+
- SPIFFE private keys or SVID file contents
|
|
30
|
+
|
|
31
|
+
The user OAuth2 token administers deployments as the signed-in identity. The
|
|
32
|
+
generated `ORY_AGENT_API_KEY` or customer-issued SVID is a separate runtime
|
|
33
|
+
credential. Never exchange their roles. Do not revoke a deployment or workload
|
|
34
|
+
registration unless the user explicitly asks.
|
|
35
|
+
|
|
36
|
+
## Prerequisites
|
|
37
|
+
|
|
38
|
+
Require Node.js 22 or newer, npm, Docker with `docker compose`, and Go 1.24 or
|
|
39
|
+
newer unless `ory-agent` is installed. Require only this application environment
|
|
40
|
+
value:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
ANTHROPIC_API_KEY
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Agent Security must have a connected project URL, but it need not come from an
|
|
47
|
+
environment variable: the CLI reads the shared plugin config and its persisted
|
|
48
|
+
user login. Headless CI sets `ORY_PROJECT_URL` as connection configuration and
|
|
49
|
+
`ORY_USER_OAUTH2_TOKEN` to a user OAuth2 access token.
|
|
50
|
+
`ORY_AGENT_SECURITY_URL` is optional and defaults to
|
|
51
|
+
`https://agents.console.ory.com`.
|
|
52
|
+
|
|
53
|
+
Check the authenticated context without exposing a token:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
ory-agent auth status
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The server response is authoritative for the connected project and owner.
|
|
60
|
+
|
|
61
|
+
## Build the agent
|
|
62
|
+
|
|
63
|
+
Inspect the repository first and preserve its package manager and conventions.
|
|
64
|
+
For a new TypeScript application, install:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install @ory/argus @ory/claude-agent-sdk @anthropic-ai/claude-agent-sdk zod
|
|
68
|
+
npm install --save-dev typescript @types/node
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Build a headless service with `GET /healthz` and `POST /run`. Wire the SDK at
|
|
72
|
+
its supported hook boundary, and consume the query stream:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import {
|
|
76
|
+
createSdkMcpServer,
|
|
77
|
+
query,
|
|
78
|
+
tool,
|
|
79
|
+
type Options,
|
|
80
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
81
|
+
import { OryAgentClient } from "@ory/argus";
|
|
82
|
+
import { createOryHooks } from "@ory/claude-agent-sdk";
|
|
83
|
+
import { z } from "zod";
|
|
84
|
+
|
|
85
|
+
const deploymentProbe = tool(
|
|
86
|
+
"deployment_probe",
|
|
87
|
+
"Return a fixed deployment health marker",
|
|
88
|
+
{ marker: z.literal("ready").default("ready") },
|
|
89
|
+
async ({ marker }) => ({
|
|
90
|
+
content: [{ type: "text", text: marker }],
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const deploymentServer = createSdkMcpServer({
|
|
95
|
+
name: "deployment",
|
|
96
|
+
version: "1.0.0",
|
|
97
|
+
tools: [deploymentProbe],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const ory = OryAgentClient.fromEnv("claude-agent-sdk");
|
|
101
|
+
|
|
102
|
+
function options(): Options {
|
|
103
|
+
return {
|
|
104
|
+
tools: [],
|
|
105
|
+
mcpServers: { deployment: deploymentServer },
|
|
106
|
+
allowedTools: ["mcp__deployment__deployment_probe"],
|
|
107
|
+
// The packages use compatible hook shapes without sharing SDK types.
|
|
108
|
+
hooks: createOryHooks({ client: ory }) as unknown as Options["hooks"],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function runClaude(prompt: string) {
|
|
113
|
+
const messages = query({
|
|
114
|
+
prompt,
|
|
115
|
+
options: options(),
|
|
116
|
+
});
|
|
117
|
+
for await (const message of messages) {
|
|
118
|
+
// Convert the SDK result into the service's response shape.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// POST /run passes the request prompt to runClaude.
|
|
123
|
+
// POST /probe passes a fixed prompt that requires deployment_probe.
|
|
124
|
+
await runClaude("Call deployment_probe exactly once and return its result.");
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
All tool execution must pass through these hooks. Add a local
|
|
128
|
+
`deployment_probe` tool using the Claude Agent SDK's supported tool/MCP surface.
|
|
129
|
+
Expose `POST /probe`, but do not invoke the tool implementation directly: run a
|
|
130
|
+
fixed Claude SDK `query` that instructs Claude to call `deployment_probe`, with
|
|
131
|
+
the same `createOryHooks()` wiring as `/run`. This intentionally makes a model
|
|
132
|
+
request so the validation exercises the real `SessionStart`, `PreToolUse`, and
|
|
133
|
+
`PostToolUse` lifecycle instead of claiming that a direct MCP call is secured by
|
|
134
|
+
hooks that never ran.
|
|
135
|
+
|
|
136
|
+
Add focused tests for health, the fixed probe query, hook wiring on both SDK
|
|
137
|
+
query paths, and the absence of embedded credentials. Run formatting, type
|
|
138
|
+
checking, tests, and the production build before deployment.
|
|
139
|
+
|
|
140
|
+
## Containerize
|
|
141
|
+
|
|
142
|
+
Create a production Dockerfile and either `compose.yaml` or Kubernetes workload
|
|
143
|
+
manifests. The service must receive `ANTHROPIC_API_KEY` by secret reference, bind
|
|
144
|
+
to `0.0.0.0`, expose the application port, and define a health check. Ignore
|
|
145
|
+
credentials, deployment state, dependencies, build output, and local environment
|
|
146
|
+
files in Git and the Docker context.
|
|
147
|
+
|
|
148
|
+
For Talos, load `.ory/credentials.env` with Compose `env_file` or the equivalent
|
|
149
|
+
Kubernetes Secret. For SPIFFE, use the file-backed environment contract in
|
|
150
|
+
`examples/spiffe`: mount the parent of the atomically switched `current`
|
|
151
|
+
generation read-only, and point `ORY_AGENT_SPIFFE_GENERATION_PATH` at `current`.
|
|
152
|
+
TypeScript `OryAgentClient.fromEnv()` and Python `OryAgentClient.from_env()` use
|
|
153
|
+
the same variables and reload the certificate, private key, and trust bundle for
|
|
154
|
+
every broker request.
|
|
155
|
+
|
|
156
|
+
Run `docker compose config` without printing its environment section.
|
|
157
|
+
|
|
158
|
+
## Initialize and plan
|
|
159
|
+
|
|
160
|
+
Install a published, pinned coordinated CLI release, never `latest`. Obtain the
|
|
161
|
+
version from the release or package selected by the user; do not invent or
|
|
162
|
+
hardcode a future version in generated guidance:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
go install github.com/ory/ory-agent-plugins/cmd/ory-agent@<release-version>
|
|
166
|
+
ory-agent init
|
|
167
|
+
ory-agent validate
|
|
168
|
+
ory-agent deployment plan
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The generated manifest uses this identity-free shape:
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
api_version: agent-security.ory.sh/v1
|
|
175
|
+
kind: AgentDeployment
|
|
176
|
+
name: my-sdk-agent
|
|
177
|
+
agent_type: claude-agent-sdk
|
|
178
|
+
runtime_id: generic-docker
|
|
179
|
+
package_recipe: docker-compose
|
|
180
|
+
package_recipe_version: v1
|
|
181
|
+
credential_method: talos_api_key
|
|
182
|
+
credential_ttl_seconds: 2592000
|
|
183
|
+
credential_rate_limit:
|
|
184
|
+
quota: 600
|
|
185
|
+
window_seconds: 60
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
For customer-operated SPIFFE connecting to hosted Agent Security, replace the
|
|
189
|
+
credential-specific fields with:
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
credential_method: spiffe_x509_svid
|
|
193
|
+
spiffe_id: spiffe://customer.example/agent/my-sdk-agent
|
|
194
|
+
spiffe_trust_relationship_id: <active-trust-relationship-uuid>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Hosted mode omits both `agent_security_mtls_url` and `spiffe_server_id`. The
|
|
198
|
+
server supplies its Web-PKI mTLS endpoint, and the runtime uses system trust
|
|
199
|
+
roots with HTTPS hostname verification. A customer-operated portable Agent
|
|
200
|
+
Security deployment requires its explicit `agent_security_mtls_url` and may add
|
|
201
|
+
an exact `spiffe_server_id`; use the values supplied for that environment.
|
|
202
|
+
Do not attempt a hosted SPIFFE deployment until Agent Security advertises the
|
|
203
|
+
capability; hosted rollout remains gated on DNS and certificate convergence.
|
|
204
|
+
The deployment package supplies non-secret runtime identity configuration; the
|
|
205
|
+
customer's workload identity system supplies the SVID, private key, and bundle.
|
|
206
|
+
Do not put PEM material in the manifest or deployment package.
|
|
207
|
+
|
|
208
|
+
Do not add project or owner fields unless the user deliberately requests an
|
|
209
|
+
override. The CLI resolves both through remote `auth status`; an explicit
|
|
210
|
+
project is sent to the owner-scoped server route for validation, while an
|
|
211
|
+
explicit owner must equal the authenticated identity.
|
|
212
|
+
|
|
213
|
+
## Create and deploy
|
|
214
|
+
|
|
215
|
+
Create and decrypt the one-time package:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
ory-agent deployment ensure --credential-file .ory/credentials.env
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The CLI creates an ephemeral age identity, verifies the package digest, and
|
|
222
|
+
atomically writes `.ory/credentials.env` and `.ory/deployment.json` with mode
|
|
223
|
+
`0600`. Verify only that the files exist, are non-empty, ignored by Git, and
|
|
224
|
+
excluded from the Docker build context.
|
|
225
|
+
|
|
226
|
+
If an existing deployment is reused but the credential file is absent, stop.
|
|
227
|
+
For Talos, the one-time secret cannot be reissued. Explain that replacement
|
|
228
|
+
requires an explicit revoke, but do not revoke without approval. SPIFFE key
|
|
229
|
+
material is never issued by this command; a missing SVID is an operator-side
|
|
230
|
+
workload identity problem and must not trigger Talos fallback.
|
|
231
|
+
|
|
232
|
+
Start and validate:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
docker compose up --build --detach
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Verify `/healthz` and invoke `/probe` first. The guarded probe makes the first
|
|
239
|
+
authenticated runtime request that activates a ready deployment. Then run:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
ory-agent deployment wait --for active --timeout 10m
|
|
243
|
+
ory-agent deployment status
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Finally invoke `/run` with a prompt that calls `deployment_probe`. Diagnose
|
|
247
|
+
failures with secret-redacted logs.
|
|
248
|
+
|
|
249
|
+
## Customer-operated SPIFFE
|
|
250
|
+
|
|
251
|
+
SPIFFE is an integration contract, not an Ory-managed SPIRE installation. The
|
|
252
|
+
customer exclusively owns and operates:
|
|
253
|
+
|
|
254
|
+
- SPIRE or another conforming SPIFFE implementation
|
|
255
|
+
- workload and node attestation, selectors, and registration entries
|
|
256
|
+
- SVID and trust-bundle issuance, filesystem delivery, rotation, and revocation
|
|
257
|
+
- helper/CSI/sidecar availability and the permissions on mounted key material
|
|
258
|
+
|
|
259
|
+
Ory records the expected workload identity and authenticates broker requests.
|
|
260
|
+
Neither `ory-agent deployment ensure` nor the Ory packages install SPIRE, choose
|
|
261
|
+
selectors, create registrations, issue SVIDs, rotate files, or revoke workload
|
|
262
|
+
entries.
|
|
263
|
+
|
|
264
|
+
Use the generated file-backed contract exactly:
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
ORY_AGENT_CREDENTIAL_METHOD=spiffe_x509_svid
|
|
268
|
+
ORY_AGENT_SPIFFE_ID=spiffe://customer.example/agent/my-sdk-agent
|
|
269
|
+
ORY_AGENT_SPIFFE_GENERATION_PATH=/run/ory-spiffe/current
|
|
270
|
+
ORY_SPIFFE_GENERATION_FILE=generation
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`ORY_SPIFFE_GENERATION_FILE` is optional. When used, the customer-side writer
|
|
274
|
+
must update one generation directory completely, update its marker, and
|
|
275
|
+
atomically switch `current`. The application mount must include the parent
|
|
276
|
+
directory rather than bind-mounting the old symlink target. Direct-path mode is
|
|
277
|
+
also supported with all three of `ORY_AGENT_SPIFFE_SVID_PATH`,
|
|
278
|
+
`ORY_AGENT_SPIFFE_KEY_PATH`, and `ORY_AGENT_SPIFFE_TRUST_BUNDLE_PATH`, but an
|
|
279
|
+
atomic generation directory is preferred for rotation.
|
|
280
|
+
Hosted mode must leave `ORY_AGENT_SPIFFE_SERVER_ID` unset so the runtime uses
|
|
281
|
+
system roots and hostname verification. For customer-operated portable Agent
|
|
282
|
+
Security, set `ORY_AGENT_SECURITY_URL` to its explicit mTLS URL and optionally
|
|
283
|
+
set `ORY_AGENT_SPIFFE_SERVER_ID` to the exact server workload ID.
|
|
284
|
+
|
|
285
|
+
Use `examples/spiffe/docker/compose.yaml` as the Generic Docker contract. It
|
|
286
|
+
expects the customer's material writer on the host and mounts its output
|
|
287
|
+
read-only. Use `examples/spiffe/kubernetes/deployment.yaml` as the Kubernetes
|
|
288
|
+
contract. It expects a customer-provisioned `customer-spiffe-material` PVC; adapt
|
|
289
|
+
that volume to the customer's CSI/helper pattern without adding SPIRE lifecycle
|
|
290
|
+
resources to the application deployment.
|
|
291
|
+
|
|
292
|
+
Before production, run the local contract tests and then validate these states
|
|
293
|
+
in the customer's non-production identity domain:
|
|
294
|
+
|
|
295
|
+
1. Package configuration: both TypeScript and Python resolve an injected SPIFFE
|
|
296
|
+
credential and do not use `ORY_AGENT_API_KEY`.
|
|
297
|
+
2. Authentication: a valid SVID reaches Agent Security over TLS 1.3 mTLS;
|
|
298
|
+
hosted mode verifies the Web-PKI hostname, while portable mode matches the
|
|
299
|
+
exact server SPIFFE URI SAN when one is configured.
|
|
300
|
+
3. Authorization: an allowed `deployment_probe` runs; the same tool is blocked
|
|
301
|
+
after an explicit deny in enforce mode.
|
|
302
|
+
4. Rotation: atomically switch `current`; the next request uses the new SVID
|
|
303
|
+
without restarting the application.
|
|
304
|
+
5. Revocation: revoke the customer-owned registration/SVID and confirm the next
|
|
305
|
+
request fails authentication without falling back to Talos. Separately,
|
|
306
|
+
`ory-agent deployment revoke` revokes the Ory deployment record only; it does
|
|
307
|
+
not delete a SPIRE registration.
|
|
308
|
+
|
|
309
|
+
## Report
|
|
310
|
+
|
|
311
|
+
Report changed files, format/typecheck/test/build results, Docker health,
|
|
312
|
+
non-secret deployment ID and status, fixed-query and user-query probe results,
|
|
313
|
+
activation status, and blockers. Never report credentials, environment values,
|
|
314
|
+
authorization headers, or credential-file contents. Leave resources running for
|
|
315
|
+
inspection unless the user asks for cleanup.
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ory-deploy-openai-e2b
|
|
3
|
+
description: Build and deploy a Python OpenAI Agents SDK application in E2B with `ory-openai-agents`, the `ory-agent` CLI, and an Agent Security Talos deployment credential. Use when the user asks to build, secure, or deploy an OpenAI agent on E2B, including a prompted web-page generator.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build and deploy an OpenAI agent on E2B
|
|
7
|
+
|
|
8
|
+
Use this playbook to turn the user's intent into a complete, secured deployment:
|
|
9
|
+
|
|
10
|
+
- Python with the OpenAI Agents SDK
|
|
11
|
+
- `ory-openai-agents` at every function-tool boundary
|
|
12
|
+
- an immutable, credential-free E2B template
|
|
13
|
+
- Agent Security runtime `e2b` and recipe `e2b-sandbox` `v1`
|
|
14
|
+
- a generated `talos_api_key` managed through the `ory-agent` CLI
|
|
15
|
+
|
|
16
|
+
The user should be able to ask for an agent in product terms. Inspect the repository,
|
|
17
|
+
preserve its package manager and conventions, scaffold the application and controller,
|
|
18
|
+
run validation, and deploy it through the commands below. Ask before overwriting an
|
|
19
|
+
existing application or revoking a deployment. Ask for missing credentials; never
|
|
20
|
+
fabricate them.
|
|
21
|
+
|
|
22
|
+
Use `ory-build-agent` for another SDK and `ory-deploy-agent` for the canonical Claude
|
|
23
|
+
Agent SDK Docker/Kubernetes deployment. Use `ory-e2b-sandbox` only when installing an
|
|
24
|
+
Ory coding-harness plugin into an E2B image; do not combine that recipe with this Agent
|
|
25
|
+
SDK application deployment.
|
|
26
|
+
|
|
27
|
+
## Security boundaries
|
|
28
|
+
|
|
29
|
+
Keep these components separate:
|
|
30
|
+
|
|
31
|
+
1. The trusted controller holds `E2B_API_KEY`, `OPENAI_API_KEY`, and the decrypted
|
|
32
|
+
`.ory/credentials.env` values.
|
|
33
|
+
2. The E2B template contains only source and pinned dependencies. It contains no
|
|
34
|
+
credentials and no Agent start command.
|
|
35
|
+
3. The sandbox receives `OPENAI_API_KEY` and `ORY_*` only in the Agent command's
|
|
36
|
+
`envs`. A credential-free preview process must not inherit them.
|
|
37
|
+
4. Agent Security owns the Agent identity, root delegation, Talos credential, policy,
|
|
38
|
+
activation, and activity records. It does not provision E2B resources.
|
|
39
|
+
|
|
40
|
+
Never print, inspect, commit, upload, or place in a prompt:
|
|
41
|
+
|
|
42
|
+
- `OPENAI_API_KEY` or `E2B_API_KEY`
|
|
43
|
+
- `ORY_AGENT_API_KEY`
|
|
44
|
+
- any value from `.ory/credentials.env`
|
|
45
|
+
- a user OAuth2 token
|
|
46
|
+
|
|
47
|
+
Never inject `ORY_USER_OAUTH2_TOKEN` into E2B. The deployment package is a complete
|
|
48
|
+
headless runtime identity and skips browser login. Always kill the sandbox after use;
|
|
49
|
+
never pause, snapshot, or fork it after credentials have entered it.
|
|
50
|
+
|
|
51
|
+
## Prerequisites
|
|
52
|
+
|
|
53
|
+
Require:
|
|
54
|
+
|
|
55
|
+
- a pinned published `ory-agent` release
|
|
56
|
+
- Python 3.10 or newer and the repository's Python package workflow
|
|
57
|
+
- an authenticated Ory Agent Plugin session for deployment administration
|
|
58
|
+
- `E2B_API_KEY` and `OPENAI_API_KEY` in the trusted controller environment
|
|
59
|
+
- project-level Talos rate limiting enabled before relying on deployment quotas
|
|
60
|
+
|
|
61
|
+
Check Ory authentication without exposing its token:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
ory-agent auth status
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Stop if the catalog-selected coordinated `ory-openai-agents` and `ory-argus` release
|
|
68
|
+
does not exist in the approved Python registry. Pin the tested OpenAI SDK and coordinated
|
|
69
|
+
Ory release, including transitives, in the repository's lock file. Do not use `latest`.
|
|
70
|
+
|
|
71
|
+
## Scaffold the application
|
|
72
|
+
|
|
73
|
+
For a web-page generator, create this shape unless the repository already establishes
|
|
74
|
+
another layout:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
app/agent.py
|
|
78
|
+
app/preview.py
|
|
79
|
+
controller/launch.py
|
|
80
|
+
tests/test_agent.py
|
|
81
|
+
requirements.in
|
|
82
|
+
requirements.lock
|
|
83
|
+
template.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The sandbox must not contain the E2B SDK; only the external controller creates E2B
|
|
87
|
+
resources. Expose narrow function tools instead of shell access. For a static page
|
|
88
|
+
generator, use a fixed output directory, fixed filenames, prompt and output size limits,
|
|
89
|
+
and HTML/CSS only.
|
|
90
|
+
|
|
91
|
+
Use one shared Ory client for the input guardrail and completion hooks. OpenAI tool-input
|
|
92
|
+
guardrails belong on each `FunctionTool`, not on `Agent`:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import sys
|
|
96
|
+
from pathlib import Path
|
|
97
|
+
from uuid import uuid4
|
|
98
|
+
|
|
99
|
+
from agents import Agent, Runner, function_tool
|
|
100
|
+
from ory_argus import OryAgentClient, with_hook_context
|
|
101
|
+
from ory_openai_agents import ory_run_hooks, ory_tool_input_guardrail
|
|
102
|
+
|
|
103
|
+
OUTPUT_DIR = Path("/workspace/output")
|
|
104
|
+
MAX_FILE_BYTES = 256_000
|
|
105
|
+
ory = OryAgentClient.from_env("openai-agents")
|
|
106
|
+
ory_guardrail = ory_tool_input_guardrail(client=ory)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def write_bounded(name: str, content: str) -> None:
|
|
110
|
+
encoded = content.encode("utf-8")
|
|
111
|
+
if len(encoded) > MAX_FILE_BYTES:
|
|
112
|
+
raise ValueError(f"{name} exceeds {MAX_FILE_BYTES} bytes")
|
|
113
|
+
(OUTPUT_DIR / name).write_bytes(encoded)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@function_tool(tool_input_guardrails=[ory_guardrail])
|
|
117
|
+
def write_web_page(index_html: str, styles_css: str) -> str:
|
|
118
|
+
"""Write one static web page to the fixed preview directory."""
|
|
119
|
+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
write_bounded("index.html", index_html)
|
|
121
|
+
write_bounded("styles.css", styles_css)
|
|
122
|
+
return "Wrote index.html and styles.css"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
agent = Agent(
|
|
126
|
+
name="Web Page Builder",
|
|
127
|
+
instructions=(
|
|
128
|
+
"Create an accessible responsive static page. Call write_web_page exactly once. "
|
|
129
|
+
"Do not generate JavaScript, forms, remote resources, network requests, analytics, "
|
|
130
|
+
"automatic navigation, or secrets."
|
|
131
|
+
),
|
|
132
|
+
tools=[write_web_page],
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def run(prompt: str, *, session_id: str | None = None) -> str:
|
|
137
|
+
with with_hook_context(ory, session_id=session_id or uuid4().hex):
|
|
138
|
+
status = "error"
|
|
139
|
+
try:
|
|
140
|
+
result = Runner.run_sync(agent, prompt, hooks=ory_run_hooks(client=ory))
|
|
141
|
+
status = "ok"
|
|
142
|
+
finally:
|
|
143
|
+
ory.logger.activity(
|
|
144
|
+
"session.end",
|
|
145
|
+
status,
|
|
146
|
+
attributes={"harness": "openai-agents"},
|
|
147
|
+
)
|
|
148
|
+
if not ory.flush_events(timeout=3):
|
|
149
|
+
print("Agent Security activity flush did not complete", file=sys.stderr)
|
|
150
|
+
return str(result.final_output)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Every side-effecting local function tool needs the Ory guardrail. Hosted tools and model
|
|
154
|
+
built-ins do not pass through a function-tool guardrail; do not claim they are protected.
|
|
155
|
+
Installing the adapter alone protects nothing. Keep the entire Runner invocation, terminal
|
|
156
|
+
session activity, and event flush inside one `with_hook_context`; use a controller-owned stable
|
|
157
|
+
job or conversation ID when available, otherwise generate a new opaque ID for each run.
|
|
158
|
+
|
|
159
|
+
Add a fixed probe mode that runs the same OpenAI `Runner` and instructs the model to call
|
|
160
|
+
`write_web_page` exactly once with known harmless content. Do not invoke the Python tool
|
|
161
|
+
function directly: the probe must exercise session start, Talos authentication,
|
|
162
|
+
authorization, tool execution, completion activity, and event flush.
|
|
163
|
+
|
|
164
|
+
Serve generated files from a separate process with a restrictive CSP: no scripts,
|
|
165
|
+
connections, forms, framing, base URI, remote resources, or MIME sniffing. Do not pass the
|
|
166
|
+
Agent command environment to the preview process.
|
|
167
|
+
|
|
168
|
+
Test prompt bounds, output bounds, fixed paths, allowed execution, enforce-mode denial,
|
|
169
|
+
shared-client guardrail/hooks wiring, and secret-free source and fixtures. Run formatting,
|
|
170
|
+
lint, unit tests, and a real-SDK compatibility test before building the template.
|
|
171
|
+
|
|
172
|
+
## Build an immutable E2B template
|
|
173
|
+
|
|
174
|
+
Generate a Python E2B template that installs the locked dependencies and copies only the
|
|
175
|
+
application:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from e2b import Template
|
|
179
|
+
|
|
180
|
+
template = (
|
|
181
|
+
Template()
|
|
182
|
+
.from_ubuntu_image("22.04")
|
|
183
|
+
.apt_install(["python3", "python3-pip"])
|
|
184
|
+
.set_workdir("/workspace")
|
|
185
|
+
.copy("requirements.lock", "/workspace/requirements.lock")
|
|
186
|
+
.run_cmd("python3 -m pip install --no-cache-dir -r requirements.lock")
|
|
187
|
+
.copy("app", "/workspace/app")
|
|
188
|
+
.make_dir("/workspace/output", mode=0o700)
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Do not configure the Agent as a template start command: sandbox-creation credentials do
|
|
193
|
+
not exist during the template build. Build in a trusted release environment, scan source
|
|
194
|
+
and logs for secret markers, and record the returned immutable build ID. Production must
|
|
195
|
+
use `<template-name>:<build-id>`, never a moving tag or bare build ID.
|
|
196
|
+
|
|
197
|
+
## Initialize the Agent Security deployment
|
|
198
|
+
|
|
199
|
+
Generate the supported manifest and workflow through the CLI rather than hand-writing
|
|
200
|
+
the API request:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
ory-agent init \
|
|
204
|
+
--name web-page-generator \
|
|
205
|
+
--agent-type openai-agents \
|
|
206
|
+
--runtime-id e2b \
|
|
207
|
+
--package-recipe e2b-sandbox \
|
|
208
|
+
--package-recipe-version v1 \
|
|
209
|
+
--credential-method talos_api_key
|
|
210
|
+
ory-agent validate
|
|
211
|
+
ory-agent deployment plan
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The resulting `.ory/agent.yaml` must contain this selection and no project, owner, or
|
|
215
|
+
credential values:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
agent_type: openai-agents
|
|
219
|
+
runtime_id: e2b
|
|
220
|
+
package_recipe: e2b-sandbox
|
|
221
|
+
package_recipe_version: v1
|
|
222
|
+
credential_method: talos_api_key
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Review the plan. Then create the one-time package:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
ory-agent deployment ensure --credential-file .ory/credentials.env
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The CLI verifies the package and atomically writes `.ory/credentials.env` and the
|
|
232
|
+
non-secret `.ory/deployment.json` with mode `0600`. Verify only existence, mode,
|
|
233
|
+
non-empty size, Git ignore coverage, and exclusion from build contexts. Never read the
|
|
234
|
+
credential file into chat or terminal output. If a deployment is reused but the one-time
|
|
235
|
+
credential file is missing, stop and explain that replacement requires explicit
|
|
236
|
+
revocation. Do not revoke without approval.
|
|
237
|
+
|
|
238
|
+
## Launch through the trusted controller
|
|
239
|
+
|
|
240
|
+
Load `.ory/credentials.env` into a validated in-memory map without logging it. Require
|
|
241
|
+
exactly the deployment variables emitted by Agent Security, including
|
|
242
|
+
`ORY_AGENT_API_KEY`, `ORY_AGENT_SUBJECT_ID`, and `ORY_AGENT_ROOT_DELEGATION`. Add
|
|
243
|
+
`OPENAI_API_KEY` only to the Agent command environment.
|
|
244
|
+
|
|
245
|
+
The controller must:
|
|
246
|
+
|
|
247
|
+
1. Authenticate and rate-limit its caller.
|
|
248
|
+
2. Create a sandbox from the exact `<template-name>:<build-id>` with a bounded lifetime.
|
|
249
|
+
3. Disable public traffic unless preview is required.
|
|
250
|
+
4. Deny outbound traffic by default and allow only the exact OpenAI, Agent Security, Ory
|
|
251
|
+
project, and required application hosts.
|
|
252
|
+
5. Write the bounded prompt to a file rather than a shell argument.
|
|
253
|
+
6. Run the Agent with `sandbox.commands.run(..., envs=agent_env, timeout=...)`.
|
|
254
|
+
7. Start preview separately without `envs=agent_env`.
|
|
255
|
+
8. Kill the sandbox in every failure, cancellation, expiry, and completion path.
|
|
256
|
+
|
|
257
|
+
Do not put deployment values in `Sandbox.create(..., envs=...)`; command-scoped injection
|
|
258
|
+
keeps them away from unrelated processes. E2B environment values remain visible inside
|
|
259
|
+
the sandbox OS, so run only the intended customer application there.
|
|
260
|
+
|
|
261
|
+
When exposing a preview, set `allow_public_traffic` to false, keep the E2B traffic access
|
|
262
|
+
token in the controller, and proxy through a dedicated authenticated preview origin.
|
|
263
|
+
Render generated content in a sandboxed iframe without same-origin, forms, popups, or
|
|
264
|
+
top-navigation privileges. Never return the traffic token to untrusted page content.
|
|
265
|
+
|
|
266
|
+
## Activate and validate
|
|
267
|
+
|
|
268
|
+
Agent Security changes a deployment from `ready` to `active` on its first authenticated
|
|
269
|
+
runtime request. Therefore use this order:
|
|
270
|
+
|
|
271
|
+
1. Launch a canary sandbox from the immutable build.
|
|
272
|
+
2. Run the fixed guarded probe inside it with command-scoped credentials.
|
|
273
|
+
3. Verify the expected output files exist without dumping process environment.
|
|
274
|
+
4. Run `ory-agent deployment wait --for active --timeout 10m`.
|
|
275
|
+
5. Run `ory-agent deployment status`.
|
|
276
|
+
6. Run the user's page-generation prompt and validate the static output.
|
|
277
|
+
|
|
278
|
+
Start in Agent Security `observe`, review the recorded decision and activity, grant the
|
|
279
|
+
expected tool, deliberately promote policy to `enforce`, and prove an explicit deny keeps
|
|
280
|
+
the tool from writing output. Adapter infrastructure errors fail open, so E2B isolation,
|
|
281
|
+
narrow tools, egress restrictions, timeouts, and output validation remain required.
|
|
282
|
+
|
|
283
|
+
## Report
|
|
284
|
+
|
|
285
|
+
Report changed files, dependency locks, checks, immutable E2B build ID, non-secret
|
|
286
|
+
deployment ID and status, probe result, output validation, and blockers. Do not report
|
|
287
|
+
credentials, environment values, authorization headers, traffic tokens, prompts that may
|
|
288
|
+
contain sensitive data, or generated secret material. Leave a preview running only when
|
|
289
|
+
the user explicitly asks to inspect it and its controller owns a bounded cleanup lease.
|
|
@@ -5,6 +5,11 @@ description: Scaffold an E2B (e2b.dev) sandbox template that boots with {{PKG}}
|
|
|
5
5
|
|
|
6
6
|
# E2B sandbox with Ory agent security
|
|
7
7
|
|
|
8
|
+
> **Agent SDK deployment:** If the user owns an OpenAI Agents SDK application and
|
|
9
|
+
> wants Agent Security to issue its headless runtime identity, do not use this
|
|
10
|
+
> coding-harness image recipe. Use {{REF_DEPLOY_OPENAI_E2B}} for the complete
|
|
11
|
+
> `ory-agent`, Talos, immutable-template, and trusted-controller workflow.
|
|
12
|
+
|
|
8
13
|
You are helping the user scaffold an [E2B](https://e2b.dev) sandbox template
|
|
9
14
|
that preinstalls and registers the {{PKG}} plugin. The resulting template
|
|
10
15
|
publishes a named image; every `Sandbox.create("<tag>")` call from their SDK
|
|
@@ -114,9 +119,9 @@ E2B_API_KEY=e2b_***
|
|
|
114
119
|
ORY_PROJECT_URL=https://<slug>.projects.oryapis.com
|
|
115
120
|
ORY_OAUTH2_CLIENT_ID=
|
|
116
121
|
ORY_USER_OAUTH2_TOKEN=
|
|
117
|
-
# Optional:
|
|
118
|
-
#
|
|
119
|
-
#
|
|
122
|
+
# Optional: supply a process-local Talos runtime credential. Otherwise session
|
|
123
|
+
# start loads or enrolls one through the platform's credential-store support.
|
|
124
|
+
# ORY_AGENT_API_KEY=
|
|
120
125
|
```
|
|
121
126
|
|
|
122
127
|
### `integrations/e2b/package.json`
|
|
@@ -152,8 +157,7 @@ Generate a README that captures:
|
|
|
152
157
|
`npm run build:prod`.
|
|
153
158
|
- A `Sandbox.create("agent-ory", { envs: { ... } })` example showing which env
|
|
154
159
|
vars to inject at runtime (`ORY_PROJECT_URL`, `ORY_OAUTH2_CLIENT_ID`,
|
|
155
|
-
`ORY_USER_OAUTH2_TOKEN`, optional
|
|
156
|
-
`ORY_AGENT_CLIENT_ID` + `ORY_AGENT_CLIENT_SECRET`).
|
|
160
|
+
`ORY_USER_OAUTH2_TOKEN`, optional `ORY_AGENT_API_KEY`).
|
|
157
161
|
- A pointer to {{REF_AUTH_SETUP}} for full env-var coverage and to
|
|
158
162
|
{{REF_LOCAL_DEV}} for testing the same plugin locally before publishing the
|
|
159
163
|
template.
|
|
@@ -196,7 +200,7 @@ const sbx = await Sandbox.create("agent-ory", {
|
|
|
196
200
|
await sbx.commands.run("{{BIN}} --version"); // sanity check
|
|
197
201
|
```
|
|
198
202
|
|
|
199
|
-
Sandboxes are headless, so the user **must** pre-supply
|
|
203
|
+
Sandboxes using this interactive coding-harness recipe are headless, so the user **must** pre-supply
|
|
200
204
|
`ORY_USER_OAUTH2_TOKEN` — otherwise user login's
|
|
201
205
|
PKCE browser flow has no target and hangs. See {{REF_AUTH_SETUP}} for the full
|
|
202
206
|
env-var matrix.
|