How to pass secrets to a workspace
This guide shows how to pass sensitive values, such as API keys, tokens or credential files, into a workspace VM. For how secrets are delivered and protected, see Secrets explanation.
jailoc reads every secret on the host at jailoc up and jailoc reload and sends it to the guest over SSH. Environment secrets reach sessions as environment variables only. File secrets land in /run/secrets/<NAME>, a tmpfs, with mode 0400 and owned by the agent user. jailoc never writes secret values to host disk.
Pass an API key or token via environment variable
-
Set the variable on the host:
export HOST_MCP_TOKEN="secret-token-value" -
Add an environment secret to
~/.config/jailoc/config.toml:[workspaces.my-project] paths = ["~/projects/my-project"] [workspaces.my-project.secrets.env.MCP_SERVER_TOKEN] from_env = "HOST_MCP_TOKEN" -
Start the workspace:
jailoc up my-project
Sessions see the value as the MCP_SERVER_TOKEN environment variable.
Pass a credential file
-
Add a file secret pointing to an absolute or
~path on the host:[workspaces.my-project.secrets.file.db_cert] from_file = "~/.config/my-project/db.pem" -
Start the workspace:
jailoc up my-project
The contents land in /run/secrets/db_cert. File secrets aren't exported to the environment.
Credential directories such as ~/.kube, ~/.docker, ~/.aws, ~/.config/gcloud and ~/.netrc can't be mounted or snapshotted. Pass the one file a tool needs as a file secret instead, and point the tool at it with env:
[workspaces.my-project]
env = ["KUBECONFIG=/run/secrets/kubeconfig"]
[workspaces.my-project.secrets.file.kubeconfig]
from_file = "~/.kube/config"
| Tool | Secret name | from_file |
env |
|---|---|---|---|
| kubectl | kubeconfig |
~/.kube/config |
KUBECONFIG=/run/secrets/kubeconfig |
| AWS CLI and SDKs | aws-credentials |
~/.aws/credentials |
AWS_SHARED_CREDENTIALS_FILE=/run/secrets/aws-credentials |
| Google Cloud SDKs | gcloud-credentials |
~/.config/gcloud/application_default_credentials.json |
GOOGLE_APPLICATION_CREDENTIALS=/run/secrets/gcloud-credentials |
Go, Python requests |
netrc |
~/.netrc |
NETRC=/run/secrets/netrc |
A kubeconfig that references certificate or key files by path needs those files embedded (kubectl config view --raw --flatten) or passed as file secrets too. Docker reads config.json from a directory, so copy the secret there inside the VM (mkdir -p ~/.docker && cp -f /run/secrets/docker-config ~/.docker/config.json). ~/.docker is not a persistent home directory, so the copy lives in VM memory and is gone after the VM stops: repeat it after every VM start, and after jailoc reload to pick up a rotated secret. For the GitHub CLI, pass a token instead of ~/.config/gh:
[workspaces.my-project.secrets.env.GH_TOKEN]
from_command = "gh auth token"
Load a host file into an environment variable
Use from_file under secrets.env.<NAME>:
[workspaces.my-project.secrets.env.API_KEY]
from_file = "~/.secrets/api_key.txt"
Sessions get the file contents as API_KEY, with the trailing line break stripped. The value must fit on one line; use a file secret for multi-line content such as a PEM key.
Read a secret from a password manager or keychain
Use from_command to run a command on the host and use its standard output as the value:
[workspaces.my-project.secrets.env.GH_TOKEN]
from_command = "gh auth token"
[workspaces.my-project.secrets.env.OPENAI_API_KEY]
from_command = "op read op://Private/OpenAI/credential"
ttl = "1h"
[workspaces.my-project.secrets.file.npmrc]
from_command = "security find-generic-password -s npmrc -w"
jailoc runs each command with /bin/sh -c and JAILOC_WORKSPACE set to the workspace name at jailoc up and jailoc reload, before it boots or changes the VM. The command gets your terminal, so a password manager can ask you to unlock it. A command that exits non-zero, prints nothing or runs longer than two minutes stops the operation.
Set ttl (for example "15m" or "1h") to cache the command's output in the host system keychain (the login keychain on macOS, the kernel user keyring on Linux) so restarting or reloading the workspace within that window reuses the value without prompting again.
Set global default secrets
Declare secrets under [defaults.secrets.env.<NAME>] or [defaults.secrets.file.<NAME>] to give them to every workspace. from_command entries run per workspace with JAILOC_WORKSPACE set to the target workspace name:
[defaults.secrets.env.API_TOKEN]
from_env = "GLOBAL_API_TOKEN"
[defaults.secrets.env.VAULT_TOKEN]
from_command = "op read op://Dev/${JAILOC_WORKSPACE}/token"
ttl = "1h"
A workspace secret with the same name replaces the default entry completely.
Rotate or remove a secret
Edit the config or the host source, then push the change to the running VM:
jailoc reload my-project
jailoc reads every source again, rewrites /run/secrets and removes files of secrets that aren't configured any more. To force from_command secrets with a ttl to run again before their TTL expires, pass --purge-cache:
jailoc reload --purge-cache my-project
New sessions get the updated environment. Processes that read /run/secrets/<NAME> see the new value immediately.
Note
Running sessions keep the environment they started with. Open a new session with jailoc --new to pick up changed environment secrets. See Sessions.
Warning
The agent can read every secret configured for its workspace. Configure only the secrets it needs, and prefer short-lived, narrowly scoped tokens.