Skip to content

Commit 7589dcb

Browse files
committed
added agent gateway to secret example
1 parent 97bcd70 commit 7589dcb

File tree

4 files changed

+76
-44
lines changed

4 files changed

+76
-44
lines changed

EXAMPLES.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Runnable examples live in [`examples/`](./examples).
1212
- [Devbox Snapshot and Resume](#devbox-snapshot-resume)
1313
- [Devbox Tunnel (HTTP Server Access)](#devbox-tunnel)
1414
- [MCP Hub + Claude Code + GitHub](#mcp-github-tools)
15-
- [Secrets with Devbox (Create, Inject, Verify, Delete)](#secrets-with-devbox)
15+
- [Secrets with Devbox via Agent Gateway](#secrets-with-devbox)
1616

1717
<a id="blueprint-with-build-context"></a>
1818
## Blueprint with Build Context
@@ -168,20 +168,18 @@ uv run pytest -m smoketest tests/smoketests/examples/
168168
**Source:** [`examples/mcp_github_tools.py`](./examples/mcp_github_tools.py)
169169

170170
<a id="secrets-with-devbox"></a>
171-
## Secrets with Devbox (Create, Inject, Verify, Delete)
171+
## Secrets with Devbox via Agent Gateway
172172

173-
**Use case:** Create a secret, inject it into a devbox as an environment variable, verify access, and clean up.
173+
**Use case:** Create a secret, proxy it into a devbox through agent gateway, verify the devbox only gets gateway credentials, and clean up.
174174

175-
**Tags:** `secrets`, `devbox`, `environment-variables`, `cleanup`
175+
**Tags:** `secrets`, `devbox`, `agent-gateway`, `credentials`, `cleanup`
176176

177177
### Workflow
178-
- Create a secret with a test value
179-
- Create a devbox with the secret mapped to an env var
180-
- Execute a command that reads the secret from the environment
181-
- Verify the value matches
182-
- Update the secret and verify
183-
- List secrets and verify the secret appears
184-
- Shutdown devbox and delete secret
178+
- Create a secret with a test credential
179+
- Create an agent gateway config for an upstream API
180+
- Launch a devbox with the gateway wired to the secret
181+
- Verify the devbox receives a gateway URL and token instead of the raw secret
182+
- Shutdown the devbox and delete the gateway config and secret
185183

186184
### Prerequisites
187185
- `RUNLOOP_API_KEY`

examples/registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
},
5656
{
5757
"slug": "secrets-with-devbox",
58-
"title": "Secrets with Devbox (Create, Inject, Verify, Delete)",
58+
"title": "Secrets with Devbox via Agent Gateway",
5959
"file_name": "secrets_with_devbox.py",
6060
"required_env": ["RUNLOOP_API_KEY"],
6161
"run": run_secrets_with_devbox_example,

examples/secrets_with_devbox.py

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
#!/usr/bin/env -S uv run python
22
"""
33
---
4-
title: Secrets with Devbox (Create, Inject, Verify, Delete)
4+
title: Secrets with Devbox via Agent Gateway
55
slug: secrets-with-devbox
6-
use_case: Create a secret, inject it into a devbox as an environment variable, verify access, and clean up.
6+
use_case: Create a secret, proxy it into a devbox through agent gateway, verify the devbox only gets gateway credentials, and clean up.
77
workflow:
8-
- Create a secret with a test value
9-
- Create a devbox with the secret mapped to an env var
10-
- Execute a command that reads the secret from the environment
11-
- Verify the value matches
12-
- Update the secret and verify
13-
- List secrets and verify the secret appears
14-
- Shutdown devbox and delete secret
8+
- Create a secret with a test credential
9+
- Create an agent gateway config for an upstream API
10+
- Launch a devbox with the gateway wired to the secret
11+
- Verify the devbox receives a gateway URL and token instead of the raw secret
12+
- Shutdown the devbox and delete the gateway config and secret
1513
tags:
1614
- secrets
1715
- devbox
18-
- environment-variables
16+
- agent-gateway
17+
- credentials
1918
- cleanup
2019
prerequisites:
2120
- RUNLOOP_API_KEY
@@ -33,19 +32,19 @@
3332

3433
# Note: do NOT hardcode secret values in your code!
3534
# This is example code only; use environment variables instead!
36-
_EXAMPLE_SECRET_VALUE = "my-secret-value"
37-
_UPDATED_SECRET_VALUE = "updated-secret-value"
35+
_EXAMPLE_GATEWAY_ENDPOINT = "https://api.example.com"
36+
_EXAMPLE_SECRET_VALUE = "example-upstream-api-key"
3837

3938

4039
def recipe(ctx: RecipeContext) -> RecipeOutput:
41-
"""Create a secret, inject it into a devbox, and verify it is accessible."""
40+
"""Create a secret, proxy it through an agent gateway, and verify the devbox only gets gateway credentials."""
4241
cleanup = ctx.cleanup
4342

4443
sdk = RunloopSDK()
4544
resources_created: list[str] = []
4645
checks: list[ExampleCheck] = []
4746

48-
secret_name = unique_name("RUNLOOP_SDK_EXAMPLE").upper().replace("-", "_")
47+
secret_name = unique_name("agent-gateway-secret")
4948

5049
secret = sdk.secret.create(name=secret_name, value=_EXAMPLE_SECRET_VALUE)
5150
resources_created.append(f"secret:{secret_name}")
@@ -60,10 +59,33 @@ def recipe(ctx: RecipeContext) -> RecipeOutput:
6059
)
6160
)
6261

62+
# Hide upstream credentials from the devbox by routing requests through an
63+
# agent gateway config. This prevents direct secret exfiltration.
64+
gateway_config = sdk.gateway_config.create(
65+
name=unique_name("agent-gateway-config"),
66+
endpoint=_EXAMPLE_GATEWAY_ENDPOINT,
67+
auth_mechanism={"type": "bearer"},
68+
description="Example gateway that keeps upstream credentials off the devbox",
69+
)
70+
resources_created.append(f"gateway_config:{gateway_config.id}")
71+
cleanup.add(f"gateway_config:{gateway_config.id}", gateway_config.delete)
72+
73+
gateway_info = gateway_config.get_info()
74+
checks.append(
75+
ExampleCheck(
76+
name="gateway config created successfully",
77+
passed=(gateway_info.id.startswith("gwc_") and gateway_info.endpoint == _EXAMPLE_GATEWAY_ENDPOINT),
78+
details=f"id={gateway_info.id}, endpoint={gateway_info.endpoint}",
79+
)
80+
)
81+
6382
devbox = sdk.devbox.create(
64-
name=unique_name("secrets-example-devbox"),
65-
secrets={
66-
"MY_SECRET_ENV": secret.name,
83+
name=unique_name("agent-gateway-devbox"),
84+
gateways={
85+
"MY_API": {
86+
"gateway": gateway_config.id,
87+
"secret": secret.name,
88+
}
6789
},
6890
launch_parameters={
6991
"resource_size_request": "X_SMALL",
@@ -73,32 +95,44 @@ def recipe(ctx: RecipeContext) -> RecipeOutput:
7395
resources_created.append(f"devbox:{devbox.id}")
7496
cleanup.add(f"devbox:{devbox.id}", devbox.shutdown)
7597

76-
result = devbox.cmd.exec("echo $MY_SECRET_ENV")
77-
stdout = result.stdout().strip()
98+
devbox_info = devbox.get_info()
7899
checks.append(
79100
ExampleCheck(
80-
name="devbox can read secret as env var",
81-
passed=result.exit_code == 0 and stdout == _EXAMPLE_SECRET_VALUE,
82-
details=f'exit_code={result.exit_code}, stdout="{stdout}"',
101+
name="devbox records gateway wiring",
102+
passed=(
103+
devbox_info.gateway_specs is not None
104+
and devbox_info.gateway_specs.get("MY_API") is not None
105+
and devbox_info.gateway_specs["MY_API"].gateway_config_id == gateway_config.id
106+
),
107+
details=(
108+
f"gateway_config_id={devbox_info.gateway_specs['MY_API'].gateway_config_id}"
109+
if devbox_info.gateway_specs is not None and devbox_info.gateway_specs.get("MY_API") is not None
110+
else "gateway spec missing"
111+
),
83112
)
84113
)
85114

86-
updated_info = sdk.secret.update(secret, _UPDATED_SECRET_VALUE).get_info()
115+
url_result = devbox.cmd.exec("echo $MY_API_URL")
116+
gateway_url = url_result.stdout().strip()
87117
checks.append(
88118
ExampleCheck(
89-
name="secret updated successfully",
90-
passed=updated_info.name == secret_name,
91-
details=f"update_time_ms={updated_info.update_time_ms}",
119+
name="devbox receives gateway URL",
120+
passed=url_result.exit_code == 0 and gateway_url.startswith("http"),
121+
details=f"exit_code={url_result.exit_code}, url={gateway_url}",
92122
)
93123
)
94124

95-
secrets = sdk.secret.list()
96-
found = next((s for s in secrets if s.name == secret_name), None)
125+
token_result = devbox.cmd.exec("echo $MY_API")
126+
gateway_token = token_result.stdout().strip()
97127
checks.append(
98128
ExampleCheck(
99-
name="secret appears in list",
100-
passed=found is not None,
101-
details=f"found name={found.name}" if found else "not found",
129+
name="devbox receives gateway token instead of raw secret",
130+
passed=(
131+
token_result.exit_code == 0
132+
and gateway_token.startswith("gws_")
133+
and gateway_token != _EXAMPLE_SECRET_VALUE
134+
),
135+
details=(f"exit_code={token_result.exit_code}, token_prefix={gateway_token[:4] or 'missing'}"),
102136
)
103137
)
104138

llms.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
- [Devbox lifecycle example](examples/devbox_from_blueprint_lifecycle.py): Create blueprint, launch devbox, run commands, cleanup
1414
- [Devbox snapshot and resume example](examples/devbox_snapshot_resume.py): Snapshot disk, resume from snapshot, verify state isolation
1515
- [MCP GitHub example](examples/mcp_github_tools.py): MCP Hub integration with Claude Code
16-
- [Secrets with Devbox example](examples/secrets_with_devbox.py): Create secret, inject into devbox, verify, cleanup
16+
- [Secrets with Devbox example](examples/secrets_with_devbox.py): Create secret, protect it with agent gateway config, verify gateway credentials in devbox, cleanup
1717

1818
## API Reference
1919

0 commit comments

Comments
 (0)