@@ -122,26 +122,26 @@ Use [`age-plugin-se`](https://github.com/remko/age-plugin-se) to bind `secrets.a
122122flowchart TD
123123 subgraph setup["One-time setup"]
124124 direction LR
125- KG["age-plugin-se keygen\n --access-control none"]
126- SE["Secure Enclave\nT2 / M-series"]
127- SE -->|"generates & stores\nprivate key in hardware"| KG
128- KG -->|"identity reference\n (AGE-PLUGIN-SE-1…)"| IDFILE["~/.botlockbox/identity.txt"]
129- KG -->|"public key\n (age1se1q…)"| SEAL
130- CREDS["credentials\n (stdin, plaintext)"] --> SEAL["botlockbox seal\n --recipient age1se1q…"]
131- SEAL -->|"device-bound\nciphertext "| SAGE["secrets.age"]
125+ KG["age-plugin-se keygen<br/> --access-control none"]
126+ SE["Secure Enclave<br/>T2 / M-series"]
127+ SE -->|"generates and stores private key in hardware"| KG
128+ KG -->|"identity reference (AGE-PLUGIN-SE-1…)"| IDFILE["~/.botlockbox/identity.txt"]
129+ KG -->|"public key (age1se1q…)"| SEAL
130+ CREDS["credentials<br/> (stdin, plaintext)"] --> SEAL["botlockbox seal<br/> --recipient age1se1q…"]
131+ SEAL -->|"device-bound ciphertext "| SAGE["secrets.age"]
132132 end
133133
134134 subgraph runtime["Every login — launchd user-session agent"]
135135 direction LR
136- LAUNCHD["launchd"] -->|"starts at login\nKeepAlive : true"| SERVE["botlockbox serve\n --identity identity.txt"]
136+ LAUNCHD["launchd"] -->|"starts at login, KeepAlive : true"| SERVE["botlockbox serve<br/> --identity identity.txt"]
137137 IDFILE -->|"identity ref"| SERVE
138138 SAGE -->|"ciphertext"| SERVE
139- SERVE -->|"SE decrypts silently\nno Touch ID"| MEM["secrets in\nmemguard enclaves"]
140- AGENT["AI agent / MCP / CLI\nno credentials"] -->|"HTTPS_PROXY=\nlocalhost :8080"| SERVE
141- SERVE -->|"Authorization injected\nfrom enclave"| API["External API"]
139+ SERVE -->|"decrypts silently, no Touch ID"| MEM["secrets in<br/>memguard enclaves"]
140+ AGENT["AI agent / MCP / CLI<br/>no credentials"] -->|"HTTPS_PROXY=localhost :8080"| SERVE
141+ SERVE -->|"Authorization injected from enclave"| API["External API"]
142142 end
143143
144- SAGE -.->|"useless on\nany other Mac"| LOCK["🔒 device-bound"]
144+ SAGE -.->|"useless on any other Mac"| LOCK["🔒 device-bound"]
145145```
146146
147147** One-time setup:**
@@ -196,29 +196,29 @@ flowchart TD
196196 direction TB
197197
198198 subgraph setup["Job startup (root / botlockbox user)"]
199- GHS["GitHub Actions secrets\nOPENAI_KEY , GITHUB_TOKEN…"]
200- KEYGEN["age-keygen\nephemeral keypair"]
201- PUBKEY["public key\nshell variable only"]
202- PRIVKEY["private key\nshell variable only"]
199+ GHS["GitHub Actions secrets<br/>OPENAI_KEY , GITHUB_TOKEN…"]
200+ KEYGEN["age-keygen<br/>ephemeral keypair"]
201+ PUBKEY["public key<br/>shell variable only"]
202+ PRIVKEY["private key<br/>shell variable only"]
203203 KEYGEN --> PUBKEY
204204 KEYGEN --> PRIVKEY
205- GHS --> SEAL["botlockbox seal\n --recipient \ $PUBKEY"]
205+ GHS --> SEAL["botlockbox seal<br/> --recipient $PUBKEY"]
206206 PUBKEY --> SEAL
207- SEAL --> SAGE["secrets.age\nrun -scoped"]
208- PRIVKEY -->|"piped via stdin"| SERVE["botlockbox serve\n --identity-stdin"]
207+ SEAL --> SAGE["secrets.age<br/>run -scoped"]
208+ PRIVKEY -->|"piped via stdin"| SERVE["botlockbox serve<br/> --identity-stdin"]
209209 SAGE --> SERVE
210- SERVE -->|"decrypts once\nscrambles key buffer"| MEM["secrets in\nmemguard enclaves"]
210+ SERVE -->|"decrypts, scrambles key buffer"| MEM["secrets in<br/>memguard enclaves"]
211211 end
212212
213213 subgraph run["Agent execution (unprivileged user)"]
214- AGENT["AI agent\nuid : agent"]
215- AGENT -->|"HTTPS_PROXY=localhost:8080\nno credentials in env"| SERVE
216- SERVE -->|"Authorization injected\nfrom enclave"| API["External API"]
214+ AGENT["AI agent<br/>uid : agent"]
215+ AGENT -->|"HTTPS_PROXY=localhost:8080, no credentials in env"| SERVE
216+ SERVE -->|"Authorization injected from enclave"| API["External API"]
217217 end
218218 end
219219
220- KEYGEN -.->|"shell vars gone\nafter step exits"| GONE["🗑 ephemeral"]
221- SERVE -.->|"cannot read identity.txt\n(it does not exist) "| NOFILE["no key on disk"]
220+ KEYGEN -.->|"shell vars gone after step exits"| GONE["🗑 ephemeral"]
221+ SERVE -.->|"no identity.txt on disk "| NOFILE["no key on disk"]
222222```
223223
224224** Workflow pattern:**
@@ -290,24 +290,24 @@ This works with any MCP server that respects standard HTTP proxy environment var
290290```mermaid
291291flowchart TD
292292 subgraph mac["Mac Host"]
293- SE["Secure Enclave\n (age-plugin-se)"]
294- BLB["botlockbox\nlisten: 0.0.0.0:8080\n (launchd agent)"]
293+ SE["Secure Enclave<br/> (age-plugin-se)"]
294+ BLB["botlockbox<br/> 0.0.0.0:8080<br/> (launchd agent)"]
295295 CAPEM["~/.botlockbox/ca.pem"]
296- SE -->|"decrypts silently\nno Touch ID "| BLB
296+ SE -->|"decrypts silently"| BLB
297297 BLB -->|"--ca-cert"| CAPEM
298298 end
299299
300300 subgraph docker["Docker Desktop (bridge network)"]
301- MCP1["MCP server A\nPython\nREQUESTS_CA_BUNDLE=\n/etc/botlockbox/ca.pem "]
302- MCP2["MCP server B\nNode.js\nNODE_EXTRA_CA_CERTS=\n/etc/botlockbox/ca.pem "]
301+ MCP1["MCP server A<br/>(Python) "]
302+ MCP2["MCP server B<br/>(Node.js) "]
303303 MCP1 & MCP2 -->|"volume mount :ro"| VOL["/etc/botlockbox/ca.pem"]
304304 end
305305
306306 CAPEM -->|"bind mount"| VOL
307307
308- MCP1 -->|"HTTPS_PROXY=\nhost .docker.internal:8080"| BLB
309- MCP2 -->|"HTTPS_PROXY=\nhost .docker.internal:8080"| BLB
310- BLB -->|"Authorization injected\nfrom enclave"| API["External APIs\nOpenAI , GitHub…"]
308+ MCP1 -->|"HTTPS_PROXY=host .docker.internal:8080"| BLB
309+ MCP2 -->|"HTTPS_PROXY=host .docker.internal:8080"| BLB
310+ BLB -->|"Authorization injected from enclave"| API["External APIs<br/>OpenAI , GitHub…"]
311311```
312312
313313** Prerequisites:**
0 commit comments