Skip to content
GigadriveDocs

Credential injection

Let code in a microVM call an authenticated API without the API key ever entering the microVM, by adding headers to its outbound HTTPS requests.

Credential injection adds the headers you configure to a microVM's outbound HTTPS requests to the domains you name. Use it to let an AI agent's code call OpenAI, GitHub or your own API while the key stays out of the microVM: no environment variable, file or process inside it can read the value.

Add credentials when you create a microVM

List the rules in network.inject. Each rule names a domain and the headers to set on every request to it:

import { MicroVM } from '@gigadrive/sdk';

const vm = await MicroVM.create({
  image: 'python3.13',
  network: {
    egress: 'allow',
    inject: [
      { domain: 'api.openai.com', headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` } },
      { domain: '*.github.com', headers: { Authorization: `token ${process.env.GITHUB_TOKEN}` } },
    ],
  },
});

const { stdout } = await vm.run('curl -s https://api.openai.com/v1/models');

The key is read from your own environment and sent to Gigadrive Network; the command in the microVM sends no key.

Code in the microVM then calls the API without a key, and the request arrives with the header set:

curl https://api.openai.com/v1/models

The response never shows a value. It lists each rule's domain and lowercase header names:

{
  "network": {
    "egress": "allow",
    "inject": [
      { "domain": "api.openai.com", "headers": ["authorization"] },
      { "domain": "*.github.com", "headers": ["authorization"] }
    ]
  }
}

Audit log entries for the microVM show the same redacted form.

How requests are matched

  • A rule applies to every HTTPS request on port 443 to a name it covers. Its headers replace any header of the same name that the code in the microVM sent.
  • domain follows the rules of a domain entry in network.allow: *.github.com covers every subdomain of github.com but not github.com itself. IPv4 networks are not accepted.
  • When several rules cover a name, the most specific wins: an exact name over a wildcard, and a longer wildcard over a shorter one.
  • A rule's domain is reachable even when network.allow does not list it.
  • Plain HTTP to a rule's domain is refused, so a credential never travels unencrypted. Other ports are not covered.

Requests reach the destination over HTTP/1.1, which every HTTP client supports.

Trusting the connection

The microVM's HTTPS connections to a rule's domain end at Gigadrive Network, which adds the headers and opens its own verified connection to the destination. For that, every microVM trusts its own certificate authority, created for that microVM alone. It is added to the system certificate bundles and saved as /etc/gigadrive/egress-ca.pem, and these environment variables point to it unless your image or your command already sets them:

VariableUsed by
NODE_EXTRA_CA_CERTSNode.js
DENO_CERTDeno
SSL_CERT_FILEOpenSSL-based clients, Python httpx
REQUESTS_CA_BUNDLEPython requests

A program that ships its own fixed set of root certificates, such as Java with its bundled cacerts, fails the TLS handshake with a rule's domain. Add /etc/gigadrive/egress-ca.pem to its trust store. Its connections to every other destination are unaffected.

A program that resolves names without the system resolver, for example over DNS over HTTPS or by connecting to a hardcoded address, bypasses injection. Its requests go out without the headers.

Change credentials while it runs

PATCH /microvms/{id}/network takes inject too. Because the values are never returned, the call treats inject differently from egress and allow:

BodyEffect on the rules
No inject fieldKept as they are
"inject": [...]Replaced by the new list
"inject": []Removed
"egress": "deny" without injectRemoved, and not restored by a later allow
"egress": "deny" with a non-empty injectRefused with 422 and invalid_network

To rotate a key, send the full list with the new value:

import { MicroVM } from '@gigadrive/sdk';

const vm = await MicroVM.get('0199a7c4-5b1e-7c3d-9f20-333333333333');

await vm.updateNetwork({
  egress: 'allow',
  inject: [{ domain: 'api.openai.com', headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` } }],
});

Leave out inject to keep the rules, and pass inject: [] to remove them.

The new rules apply to a running microVM before the call answers, and to a paused one when it resumes. A microVM started before credential injection was available answers 409 with microvm_egress_inject_unsupported; stop it and create a new one.

Snapshots

A microVM created from a snapshot gets only the rules in its own create request, and its own certificate authority. It never inherits the rules of the microVM the snapshot was taken from. A snapshot taken before credential injection was available cannot start a microVM with rules: create that microVM from an image or a newer snapshot.

Limits

LimitValue
Rules per microVM8, each with a different domain
Headers per rule1 to 8
Header nameAn HTTP header name of at most 64 characters
Header value1 to 8192 bytes of visible ASCII, spaces and tabs, with no leading or trailing space
All names and values together32 KiB

The service refuses with 400 and invalid_request:

  • the headers Host, Content-Length, Transfer-Encoding, Connection, Upgrade, Keep-Alive, TE, Trailer and any Proxy-* header;
  • a wildcard over a public suffix that anyone can register names under, such as *.co.uk, *.vercel.app or *.github.io. Name the exact domain, such as myproject.supabase.co, instead.

inject together with "egress": "deny" answers 422 with invalid_network. These limits are the same on every plan, and no error message repeats a header value.

  • Ports and previews covers egress and allow.
  • Scopes lists the scope each call needs: creating a microVM and changing its network need network:microvms:write.