“Bring your own key” looks like a settings feature. For an AI coding agent, it is also a security boundary: a privileged workload will send repository context, prompts, and credentials toward a network destination selected by configuration.
That boundary deserves more than a successful GET /models
request.
I reviewed MonkeyCode, an open-source AI development platform, at commit c58bcd4. Its source provides a useful concrete case because users can configure model providers while task runtimes receive proxy credentials.
This is a source review, not a penetration test. In particular, I am not claiming encryption, rotation, or revocation behavior that the reviewed files do not establish.
The reviewed model API-key schema and model repository separate a configured provider credential from a runtime API key.
The task path then calls CreateRuntimeAPIKey
and gives the runtime a proxy base URL plus that runtime token. In the reviewed repository logic, a token is associated with a user, model, and VM; a token for the same user and VM can be reused and rebound to a model.
That yields two distinct questions:
| Plane | Secret holder | Security question |
|---|---|---|
| Provider plane | Control service | Can this credential call only the intended provider, models, and operations? |
| Runtime plane | Agent VM | Is this delegated token narrowly bound, short-lived, observable, and invalid after its task boundary ends? |
A proxy reduces direct provider-key exposure inside the VM. It does not automatically settle endpoint trust, server-side storage, token lifetime, replay, or revocation.
A configurable model base URL controls where sensitive payloads leave the platform. Audit it like a webhook destination or package registry:
TLS answers “is this encrypted to the named peer?” It does not answer “should this peer receive the repository?” An allow-list or administrator approval supplies that missing policy decision.
Here is a small policy document from the companion files:
{
"endpoint": "https://llm-gateway.example/v1",
"allowed_hosts": ["llm-gateway.example"],
"require_https": true,
"forbid_url_credentials": true,
"key_scope": "model-inference-only",
"runtime_credential": {
"audience": "llm-proxy",
"bound_to": ["user", "model", "vm"],
"max_ttl_seconds": 3600
},
"egress": { "allowed_ports": [443], "follow_redirects": false }
}
The included zero-dependency checker rejects HTTP, URL credentials, unknown hosts, unrestricted key scope, missing bindings, excessive TTL, and redirects:
node check-policy.mjs byok-policy.json
node test-policy.mjs
Expected output:
PASS BYOK endpoint policy
PASS policy accepts the safe fixture and rejects five unsafe properties
This is a configuration gate, not a network scanner. Production enforcement must also validate DNS results at connection time, prevent redirect bypass, use a trusted TLS stack, and keep egress policy outside the agent's control.
For each secret type, write down:
create -> store -> delegate -> use -> observe -> rotate -> revoke -> delete
Then demand evidence for each arrow. A schema field proves persistence, not encryption. A generated token proves delegation, not expiry. A proxy proves mediation, not least privilege.
The runtime-token reuse behavior is also a reason to define model-switch semantics explicitly. If rebinding is intentional, ask what happens to an in-flight request using the prior model, how caches are keyed, and which audit event links old and new authority.
My acceptance gate would be: no provider key enters the task VM; the outbound host is approved after DNS and redirect handling; the runtime token is audience-, user-, model-, and VM-bound; expiry and revocation are tested; and logs prove the decision without exposing content.
Disclosure: I contribute to the MonkeyCode project. The observations and limitations above are based on the linked repository at the pinned commit and the local policy tests described here.