@ory/argus 1.2.4 → 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.
Files changed (49) hide show
  1. package/README.md +25 -0
  2. package/assets/skills/ory-build-agent/SKILL.md +1 -1
  3. package/assets/skills/ory-deploy-agent/SKILL.md +315 -0
  4. package/assets/skills/ory-deploy-openai-e2b/SKILL.md +289 -0
  5. package/assets/skills/ory-e2b-sandbox/SKILL.md +10 -6
  6. package/assets/skills/ory-temporal-worker/SKILL.md +4 -4
  7. package/dist/adapters.d.ts +11 -3
  8. package/dist/adapters.js +55 -13
  9. package/dist/agent-auth.d.ts +18 -7
  10. package/dist/agent-auth.js +120 -10
  11. package/dist/auth-store.d.ts +3 -3
  12. package/dist/auth-store.js +56 -12
  13. package/dist/auth.d.ts +1 -1
  14. package/dist/auth.js +1 -1
  15. package/dist/build-info.json +4 -4
  16. package/dist/cli.js +5 -0
  17. package/dist/client.d.ts +15 -0
  18. package/dist/client.js +24 -7
  19. package/dist/config.d.ts +23 -3
  20. package/dist/config.js +202 -18
  21. package/dist/contract-suite.d.ts +0 -8
  22. package/dist/contract-suite.js +0 -1
  23. package/dist/event-reporter.d.ts +1 -0
  24. package/dist/event-reporter.js +19 -2
  25. package/dist/hook-timeout.d.ts +2 -2
  26. package/dist/hook-timeout.js +2 -2
  27. package/dist/index.d.ts +4 -4
  28. package/dist/index.js +13 -6
  29. package/dist/lifecycle.js +2 -1
  30. package/dist/local/configs.d.ts +2 -4
  31. package/dist/local/configs.js +2 -4
  32. package/dist/local/manager.js +4 -4
  33. package/dist/local/seed.d.ts +4 -4
  34. package/dist/local/seed.js +8 -10
  35. package/dist/runtime-credential.d.ts +35 -0
  36. package/dist/runtime-credential.js +252 -1
  37. package/dist/runtime.d.ts +19 -12
  38. package/dist/runtime.js +69 -21
  39. package/dist/setup.js +3 -3
  40. package/dist/skills.d.ts +3 -5
  41. package/dist/skills.js +14 -5
  42. package/dist/subject.d.ts +2 -3
  43. package/dist/subject.js +10 -11
  44. package/dist/testing.d.ts +17 -0
  45. package/dist/testing.js +53 -0
  46. package/dist/tool-catalog.d.ts +12 -12
  47. package/dist/tool-catalog.js +206 -50
  48. package/dist/user-login.js +1 -1
  49. 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`, agent DCR knobs), see {{REF_AUTH_SETUP}}.
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: pin a static agent identity instead of DCR.
118
- # ORY_AGENT_CLIENT_ID=
119
- # ORY_AGENT_CLIENT_SECRET=
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.