Secrets
A Secret is a named, encrypted value scoped to one Project Environment. Create it
once and use its name as the public secret identifier in code, the Console,
CLI, SDK and API. Secrets persist
independently of Computers, so you can reuse them in new Computers and rotate
values without changing their names. Read APIs return metadata, not the value.
A binding specifies how a Computer uses a Secret. Bindings are fixed at Computer creation; later Sessions use those bindings and cannot override them.
Choose a delivery mode
| Placement | What the Computer receives | Use for |
|---|---|---|
| Protected env | A placeholder that Helmr replaces in outgoing HTTP headers for approved HTTPS origins. | API tokens used by proxy-compatible CLI tools and SDKs. |
| Raw env | The Secret value in an environment variable. | Clients that need the actual value, such as database drivers or request-signing libraries. |
| File | The Secret value as file contents. | SSH keys and other credentials that a tool reads from disk. |
Raw env and file values can be read by processes in the Computer. Protected env keeps the value outside the Computer unless you also expose the same Secret through a raw binding. Helmr never automatically falls back from protected to raw.
const computer = await client.computers.create({
template: "reviewer",
secrets: [
{
secret: "GITHUB_TOKEN",
env: {
name: "GH_TOKEN",
mode: "protected",
allowedOrigins: ["https://api.github.com"],
},
},
{ secret: "DATABASE_PASSWORD", env: { name: "PGPASSWORD", mode: "raw" } },
{ secret: "CLIENT_KEY", file: { path: "/run/secrets/client.key" } },
],
})
Secret identifiers are immutable and unique within an Environment. A Secret keeps its identity for its whole life: rotation and revocation change its state, not which Secret it is. Reuse the same identifier in different Environments for Environment-specific values. New Deployments and Computers require every declared Secret to be active in their target Environment. Successful retries keep their original binding, and rotation changes the value without changing the identifier.
Secret identifiers must match ^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$. Each binding has
exactly one env or file target. Environment bindings require an explicit
mode, and targets must be unique within the Computer.
Do not define a bound environment variable in image ENV or execution env, even
with an empty value. Execution requests reject these collisions; image ENV
collisions fail at launch. Managed runtime paths, runtime variables such as
NODE_OPTIONS and LD_*, and CA environment variables are reserved.
Protected environment variables
Use the environment variable directly in an HTTP header, such as
Authorization: Bearer <value>. Helmr’s proxy checks the Computer’s execution
authorization and the destination before replacing the placeholder with the
Secret’s current value. Invalid placeholders in inspected headers fail before the
request is sent upstream. A request that needs a revoked Secret fails with
502 Protected Secret is revoked; rotate it to resume; requests that use other
Secrets on the same Computer are unaffected.
Allowed origins
allowedOrigins lists the exact HTTPS origins that may receive the Secret. For
example, https://api.github.com permits credential use on that origin, while
https://api.example.com:8443 specifies a non-default port. Only approve services
you trust: they receive the real credential, and Helmr does not redact responses
that return it.
Origins must use DNS hostnames. Hostnames are normalized to lowercase, the default
port 443 is omitted, and a trailing / is accepted. Wildcards, IP literals,
user information, paths, queries, and fragments are not allowed.
A Computer accepts up to 64 Secret bindings, 16 origins per protected binding, and 256 origin entries in total. Duplicate origins within a binding count once; the same origin in different bindings and different ports count separately.
Every connection also passes the Computer’s phase network policy and mandatory
installation denies. Public IPv4 destinations remain subject to those checks.
Private HTTPS requires installation-approved business ranges, routing, DNS and
TLS trust; allowlist mode additionally requires explicit per-host ipRanges.
IPv6 is unsupported. See Execution policies.
A binding grants no extra network access. Under full blocking, protected bindings
are rejected. Under allowlist mode, each exact origin must use port 443 and a
concrete hostname matching an allowed pattern; every later dial must satisfy one
matching entry’s address bounds. Ordinary connectivity retains explicit HTTPS
origin ports. Runtime secrets and prepareSecrets are validated against their
own resolved phases; nonempty prepareSecrets requires a prepare callback.
Raw env and file values remain readable by guest code and never widen networking.
Allowed origins control where a Secret can be used, not all Computer network access. Requests without credentials can still reach permitted destinations. HTTPS requests to other origins are tunneled without Secret substitution; a placeholder sent there remains a placeholder. The proxy does not automatically forward credentials to a new origin after a redirect.
Client requirements
Helmr routes traffic at the VM boundary. Programs use ordinary sockets without proxy environment variables or route configuration. Same-guest localhost HTTP, SSE, and WebSocket control traffic stays local. Only TCP ports named by the Computer’s protected origins enter the credential transport; all other ports retain ordinary network policy. On captured ports, unrelated TLS passes through without termination, preserving the upstream certificate and negotiated protocol.
Protected HTTPS requires visible TLS SNI and trust in the Computer’s public CA.
Helmr provides a CA bundle through SSL_CERT_FILE and adds the CA for Node through
NODE_EXTRA_CA_CERTS. Other certificate stores require explicit public-CA trust
setup. Certificate pinning, mutual TLS, encrypted ClientHello (ECH), TLS without
SNI, and HTTP/3/QUIC cannot use protected header substitution. Opaque traffic can
remain permitted under ordinary connectivity, but carries only the placeholder; Helmr
cannot diagnose a protected origin hidden inside encryption. Never disable TLS
verification to make a client work.
HTTP/1.1 and HTTP/2 request/response streaming are supported, including SSE and streaming uploads. Every credential-bearing request or HTTP/2 stream checks live Computer authorization. Upstream TLS verifies the exact service hostname at the policy-checked original IPv4 address. A shared address or matching port does not grant Secret access; HTTP authority must match the selected HTTPS origin.
The placeholder must remain unchanged in an HTTP header. Basic authentication
encoding, local request signing, SSH authentication, and other transformations are
not supported. Helmr does not substitute values in request bodies, query strings,
or files. HTTP/1.1 WebSocket Upgrade over TLS is supported: authentication
headers are resolved during the handshake, then frames pass through unchanged.
Placeholders in Sec-WebSocket-* negotiation headers are rejected. HTTP/2
extended CONNECT, general CONNECT, and request trailers return an
unsupported-mode error; these limits do not apply to guest-local services or
unrelated end-to-end traffic. Non-HTTP ALPN on a protected origin fails TLS
negotiation. Clients with unsupported trust or TLS modes receive a TLS error; the
transport cannot distinguish every pinning failure from another client TLS error.
Helmr does not follow upstream redirects. A guest client following a redirect sends its unchanged placeholder and the new request must satisfy network and Secret policy again. The transport does not replay a request the upstream may have processed. The maintained HTTP/2 transport may retry an explicitly unprocessed request within the same authorized request context. Guest HTTP/2 streams are independent; upstream connections are not pooled across requests.
An established WebSocket holds one captured connection and one active request slot until both transfer directions have terminated. There is no proxy idle timeout after Upgrade; endpoints control their application-level idle behavior.
Each runtime admits up to 256 simultaneous TCP connections on protected-origin
ports, including unrelated traffic sharing those ports, and up to 256 active
protected HTTP requests. Excess captured connections close immediately; excess
HTTP requests receive 503 Protected HTTPS concurrent request limit reached.
Streaming requests hold request capacity until they finish or are cancelled.
Credential resolution runs up to 64 requests at a time; admitted requests waiting
for resolution can be cancelled. HTTP/2 allows four active streams per connection;
clients can queue or open another connection. Guest-local traffic and ports that
are not captured do not consume these limits.
Parking, restoring, and runtime fencing close captured connections and cancel in-flight streams. Clients must reconnect. No live connection or resolved Secret value is restored from a guest snapshot.
Delegation
Code running in a Computer can use only its existing Secret permissions when using runtime-bound Agent creation and explicit Computer placement. It cannot add a Secret, expand allowed origins, or change protected access to raw access. If it already has raw access to a Secret, it can delegate that Secret in either mode.
Users who can create or deploy bindings choose the mode and allowed origins. Protected env protects against disclosure to Computer processes; it does not restrict authorized administrators from choosing raw delivery.
Rotation and revocation
Rotating a Secret sets its next version. The Secret keeps its identifier and bindings, and every Computer bound to it uses the new value without being recreated:
- Protected requests resolve the Secret’s current value per request, so the next request uses the new value. Established WebSocket connections keep the value resolved at their handshake.
- Raw env and file values are selected when a Session process or Command starts. Rotation does not rewrite running processes or values restored from snapshots; the next process or Command receives the new value.
Revoking a Secret stops Helmr from providing it. Nothing else changes:
- Protected requests that need the Secret fail until it is rotated. Running Sessions and Commands keep running.
- New Session processes and Commands that need a raw value of the Secret are
refused with
secret_unavailable. Running ones continue, and the values already delivered stay where they are, including in snapshots and images. - New Computers and Deployments cannot bind the Secret.
To resume, rotate the revoked Secret with its new value. Rotation reactivates
the same Secret; there is no need to create it again, and creating a Secret with
a revoked name is refused with secret_revoked.
Revocation cannot recall a value that was already delivered or a request already
sent upstream. Revoke the credential at its provider; that is the containment.
Then find the Computers that received a raw value with
helmr computer list --secret NAME, or from the Secret in the Console, and
delete the ones you no longer trust. Computers marked as holding the value
through their image received it during image preparation.
Helmr does not refresh OAuth tokens or save credential changes made by a CLI back to a Secret. Manage token renewal separately and rotate the stored value when it changes. Storing a credential does not manage a provider’s subscription session.
Computer lifetime
Protected env uses a Computer-specific CA that is reused after restore. Its trust expires ten years after Computer creation; after expiry, create a new Computer to continue using protected env. Certificate private keys are not delivered to the Computer. This technical limit does not change the recommended lifecycle: retain a Computer for the work that needs it, then delete it.
See Use secrets for creation, binding, rotation, and revocation commands. Keep credentials out of inputs, results, logs, source archives, image literals and human content.