Skip to content

Commit bc5872f

Browse files
authored
docs: add ephemeral deployment guide for identity persistence (#22)
* docs: add ephemeral deployment guide for identity persistence - Create how-to/security/ephemeral-deployment.md covering both Agent SDK and MCP Guard - Add Mermaid diagram, Docker/K8s/Lambda/Cloud Run examples - Cross-reference from production guide (5-production.md) - Add to mkdocs nav under Security section * fix(docs): correct did:key → did:web in Mermaid diagram The registry assigns did:web when an API key is used. did:key is only for local dev mode without a registry.
1 parent 9d1edcb commit bc5872f

3 files changed

Lines changed: 288 additions & 0 deletions

File tree

docs/getting-started/secure/5-production.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ RUN pip install -r requirements.txt
192192
CMD ["python", "main.py"]
193193
```
194194

195+
!!! tip "Using `CapiscIO.connect()` in containers?"
196+
If you're using the "Let's Encrypt" style `CapiscIO.connect()` or `MCPServerIdentity.connect()`
197+
in Docker, Lambda, or other ephemeral environments, see the
198+
[Ephemeral Deployment Guide](../../how-to/security/ephemeral-deployment.md) for
199+
environment variable key injection.
200+
195201
---
196202

197203
## Production Architecture
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
---
2+
title: Deploying to Ephemeral Environments
3+
description: Persist agent and MCP server identity in Docker, Lambda, Cloud Run, and Kubernetes
4+
---
5+
6+
# Deploying to Ephemeral Environments
7+
8+
Both the **CapiscIO Python SDK** (agent identity) and **MCP Guard** (MCP server identity) use a "Let's Encrypt" style setup that generates cryptographic keys on first run and stores them locally. In ephemeral environments — Docker containers, serverless functions, CI runners — that local storage is lost on every restart.
9+
10+
This guide shows how to persist identity across restarts using environment variable key injection.
11+
12+
---
13+
14+
## The Problem
15+
16+
```mermaid
17+
sequenceDiagram
18+
participant C as Container Start
19+
participant SDK as CapiscIO SDK
20+
participant R as Registry
21+
22+
C->>SDK: connect()
23+
Note over SDK: No keys on disk
24+
SDK->>SDK: Generate new keypair
25+
SDK->>R: Register public key
26+
R-->>SDK: did:web assigned
27+
Note over SDK: Badge issued to DID-A
28+
29+
C->>C: Container restarts
30+
C->>SDK: connect()
31+
Note over SDK: No keys on disk (wiped)
32+
SDK->>SDK: Generate DIFFERENT keypair
33+
SDK->>R: Register DIFFERENT key
34+
R-->>SDK: DIFFERENT did:web
35+
Note over SDK: Old badge invalid!
36+
```
37+
38+
Without key persistence, each restart creates a **new identity** with a **different DID**. Any badges, trust relationships, or audit trails linked to the old DID are lost.
39+
40+
---
41+
42+
## The Solution: Environment Variable Key Injection
43+
44+
Both SDKs accept the private key as an environment variable. The key is loaded on startup, the same DID is recovered, and any existing badges remain valid.
45+
46+
=== "Agent SDK (capiscio-sdk-python)"
47+
48+
| Variable | Format | Description |
49+
|----------|--------|-------------|
50+
| `CAPISCIO_AGENT_PRIVATE_KEY_JWK` | JSON string | Ed25519 private JWK with `kid` containing the DID |
51+
52+
```bash
53+
CAPISCIO_AGENT_PRIVATE_KEY_JWK='{"kty":"OKP","crv":"Ed25519","d":"...","x":"...","kid":"did:key:z6Mk..."}'
54+
```
55+
56+
=== "MCP Guard (capiscio-mcp-python)"
57+
58+
| Variable | Format | Description |
59+
|----------|--------|-------------|
60+
| `CAPISCIO_SERVER_PRIVATE_KEY_PEM` | PEM string | PKCS#8-encoded Ed25519 private key |
61+
62+
```bash
63+
CAPISCIO_SERVER_PRIVATE_KEY_PEM='-----BEGIN PRIVATE KEY-----\nMC4CAQ...xYz\n-----END PRIVATE KEY-----\n'
64+
```
65+
66+
---
67+
68+
## Step 1: Capture the Key
69+
70+
On the very first run, the SDK generates a keypair and logs a **capture hint** to stderr:
71+
72+
=== "Agent SDK"
73+
74+
```
75+
╔══════════════════════════════════════════════════════════════════╗
76+
║ New agent identity generated — save key for persistence ║
77+
╚══════════════════════════════════════════════════════════════════╝
78+
79+
Add to your secrets manager / .env:
80+
81+
CAPISCIO_AGENT_PRIVATE_KEY_JWK='{"kty":"OKP","crv":"Ed25519","d":"nWGx...","x":"11qY...","kid":"did:key:z6MkEnv..."}'
82+
83+
The DID will be recovered automatically from the JWK on startup.
84+
```
85+
86+
=== "MCP Guard"
87+
88+
```
89+
╔══════════════════════════════════════════════════════════════════╗
90+
║ New server identity generated — save key for persistence ║
91+
╚══════════════════════════════════════════════════════════════════╝
92+
93+
Add to your secrets manager / .env:
94+
95+
CAPISCIO_SERVER_PRIVATE_KEY_PEM='-----BEGIN PRIVATE KEY-----\nMC4CAQ...xYz\n-----END PRIVATE KEY-----\n'
96+
97+
The DID will be recovered automatically from the key on startup.
98+
```
99+
100+
Run your agent or MCP server once locally, copy the key from stderr, and store it in your secrets manager.
101+
102+
---
103+
104+
## Step 2: Inject the Key
105+
106+
### Docker Compose
107+
108+
=== "Agent"
109+
110+
```yaml
111+
services:
112+
my-agent:
113+
build: .
114+
environment:
115+
CAPISCIO_API_KEY: "${CAPISCIO_API_KEY}"
116+
CAPISCIO_AGENT_PRIVATE_KEY_JWK: "${AGENT_KEY_JWK}"
117+
```
118+
119+
=== "MCP Server"
120+
121+
```yaml
122+
services:
123+
mcp-server:
124+
build: .
125+
environment:
126+
CAPISCIO_SERVER_ID: "550e8400-..."
127+
CAPISCIO_API_KEY: "${CAPISCIO_API_KEY}"
128+
CAPISCIO_SERVER_PRIVATE_KEY_PEM: "${MCP_SERVER_KEY}"
129+
```
130+
131+
### Kubernetes
132+
133+
```yaml
134+
apiVersion: v1
135+
kind: Secret
136+
metadata:
137+
name: capiscio-identity
138+
type: Opaque
139+
stringData:
140+
api-key: "sk_live_..."
141+
# Choose the appropriate key for your use case:
142+
agent-private-key-jwk: '{"kty":"OKP","crv":"Ed25519","d":"...","x":"...","kid":"did:key:z6Mk..."}'
143+
server-private-key-pem: |
144+
-----BEGIN PRIVATE KEY-----
145+
MC4CAQ...xYz
146+
-----END PRIVATE KEY-----
147+
---
148+
apiVersion: apps/v1
149+
kind: Deployment
150+
metadata:
151+
name: my-agent
152+
spec:
153+
template:
154+
spec:
155+
containers:
156+
- name: agent
157+
env:
158+
- name: CAPISCIO_API_KEY
159+
valueFrom:
160+
secretKeyRef:
161+
name: capiscio-identity
162+
key: api-key
163+
- name: CAPISCIO_AGENT_PRIVATE_KEY_JWK
164+
valueFrom:
165+
secretKeyRef:
166+
name: capiscio-identity
167+
key: agent-private-key-jwk
168+
```
169+
170+
### AWS Lambda
171+
172+
```bash
173+
aws lambda update-function-configuration \
174+
--function-name my-agent \
175+
--environment "Variables={
176+
CAPISCIO_API_KEY=sk_live_...,
177+
CAPISCIO_AGENT_PRIVATE_KEY_JWK=$(cat agent-key.json)
178+
}"
179+
```
180+
181+
!!! tip "Use AWS Secrets Manager"
182+
For production, store the key in AWS Secrets Manager and reference it
183+
via a Lambda layer or the Secrets Manager SDK rather than inline env vars.
184+
185+
### Google Cloud Run
186+
187+
```yaml
188+
apiVersion: serving.knative.dev/v1
189+
kind: Service
190+
metadata:
191+
name: my-agent
192+
spec:
193+
template:
194+
spec:
195+
containers:
196+
- image: gcr.io/my-project/my-agent
197+
env:
198+
- name: CAPISCIO_API_KEY
199+
valueFrom:
200+
secretKeyRef:
201+
name: capiscio-api-key
202+
key: latest
203+
- name: CAPISCIO_AGENT_PRIVATE_KEY_JWK
204+
valueFrom:
205+
secretKeyRef:
206+
name: agent-private-key
207+
key: latest
208+
```
209+
210+
---
211+
212+
## Key Resolution Priority
213+
214+
Both SDKs follow the same priority order:
215+
216+
| Priority | Source | When Used |
217+
|----------|--------|-----------|
218+
| **1** | Environment variable | Containers, serverless, CI |
219+
| **2** | Local key file on disk | Persistent VMs, bare metal |
220+
| **3** | Generate new keypair | First run only |
221+
222+
If the env var is set, it always wins — even if a different key exists on disk. This lets you override identity for testing or migration.
223+
224+
!!! warning "DID Changes on New Key Generation"
225+
If neither the env var nor local files are available, the SDK generates a **new** keypair with a **different** DID. Any badges issued to the old DID will no longer be valid. Always persist the key in ephemeral environments.
226+
227+
---
228+
229+
## Key Rotation
230+
231+
To rotate either an agent or MCP server identity:
232+
233+
1. **Unset** the environment variable (`CAPISCIO_AGENT_PRIVATE_KEY_JWK` or `CAPISCIO_SERVER_PRIVATE_KEY_PEM`)
234+
2. **Remove** local key files if present
235+
3. **Restart** the service — a new keypair and DID will be generated
236+
4. **Capture** the new key from the log hint
237+
5. **Store** the new key in your secrets manager
238+
239+
!!! danger "Badge Invalidation"
240+
Rotating keys changes the DID, which invalidates all existing badges.
241+
The SDK will automatically request a new badge on the next `connect()` call.
242+
Plan rotation during maintenance windows.
243+
244+
See [Key Rotation](key-rotation.md) for more advanced patterns.
245+
246+
---
247+
248+
## Complete Environment Variable Reference
249+
250+
### Agent SDK (`CapiscIO.from_env()`)
251+
252+
| Variable | Required | Description |
253+
|----------|----------|-------------|
254+
| `CAPISCIO_API_KEY` | Yes | Registry API key |
255+
| `CAPISCIO_AGENT_NAME` | No | Agent name for lookup/creation |
256+
| `CAPISCIO_AGENT_ID` | No | Specific agent UUID |
257+
| `CAPISCIO_SERVER_URL` | No | Registry URL |
258+
| `CAPISCIO_DEV_MODE` | No | Enable dev mode |
259+
| `CAPISCIO_AGENT_PRIVATE_KEY_JWK` | No | Ed25519 private JWK (JSON string) |
260+
261+
### MCP Guard (`MCPServerIdentity.from_env()`)
262+
263+
| Variable | Required | Description |
264+
|----------|----------|-------------|
265+
| `CAPISCIO_SERVER_ID` | Yes | Server UUID from dashboard |
266+
| `CAPISCIO_API_KEY` | Yes | Registry API key |
267+
| `CAPISCIO_SERVER_URL` | No | Registry URL |
268+
| `CAPISCIO_SERVER_DOMAIN` | No | Domain for badge issuance |
269+
| `CAPISCIO_SERVER_PRIVATE_KEY_PEM` | No | Ed25519 private key (PEM string) |
270+
271+
### Security Middleware (`SecurityConfig.from_env()`)
272+
273+
See [Configuration Reference](../../reference/configuration.md) for the full list of `SecurityConfig` environment variables.
274+
275+
---
276+
277+
## Next Steps
278+
279+
- [Key Rotation](key-rotation.md) — Advanced rotation patterns
280+
- [Badge Keeper](badge-keeper.md) — Automatic badge renewal
281+
- [Dev Mode](dev-mode.md) — Local development without a registry

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ nav:
211211
- Trust Badges: how-to/security/badges.md
212212
- Badge Keeper: how-to/security/badge-keeper.md
213213
- Security Gateway: how-to/security/gateway-setup.md
214+
- Ephemeral Deployment: how-to/security/ephemeral-deployment.md
214215
- Dev Mode: how-to/security/dev-mode.md
215216
- Key Rotation: how-to/security/key-rotation.md
216217
- Trust Store: how-to/security/trust-store.md

0 commit comments

Comments
 (0)