Devsy
Developing in a Workspace

Secrets in a Workspace

Devsy can inject sensitive values into a workspace without putting plaintext in devcontainer.json, command-line arguments or dotenv files. Values come from two places:

  • Devsy-managed secrets, stored in your OS keyring or in an encrypted local file.
  • External sources, such as a SOPS-encrypted file. Devsy reads them when needed and never copies them into its own store.

Both use the same delivery paths: environment variables for lifecycle commands, in-memory files under /run/secrets, and build secrets.

Devsy-managed secrets

Secrets are scoped to the active context. With the default auto backend, Devsy uses the OS keyring when there is one (Keychain, Credential Manager, or Secret Service on Linux). Otherwise it uses an age-encrypted secrets.enc file in the Devsy config directory. Only metadata such as names and timestamps is stored in plaintext.

devsy secret set DB_PASSWORD                                  # hidden prompt
printf '%s' "$MY_VALUE" | devsy secret set DB_PASSWORD --stdin # for scripts
devsy secret set TLS_KEY --from-file ./tls.key
devsy secret list      # never prints values
devsy secret get DB_PASSWORD
devsy secret delete DB_PASSWORD

In Desktop, Workspace Variables manages secrets and environment variables. Secret values are never shown.

External sources (SOPS)

Devsy reads SOPS-encrypted YAML, JSON and dotenv files without the sops executable. Files must contain flat top-level key/value pairs:

DATABASE_PASSWORD: ENC[...]
API_TOKEN: ENC[...]

Register a local file as a named source. Devsy checks that it can decrypt the file first, and stores only the name, type, path and format override:

devsy secret source add sops project ./secrets.enc.yaml
devsy secret source list
devsy secret source remove project

A source cannot be removed while a context secret attachment still references it.

Sources declared by a repository

A repository can declare sources in customizations.devsy in its devcontainer.json:

{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "customizations": {
    "devsy": {
      "secretSources": [
        { "name": "project", "type": "sops", "path": "./secrets.enc.yaml" }
      ],
      "secrets": ["sops:project/DATABASE_PASSWORD"]
    }
  }
}

Paths are relative to the repository root. Paths that escape it, including through symlinks, are rejected. This works for a local checkout (devsy workspace up .) and a remote repository, where Devsy reads the files from the same Git revision.

Credentials for SOPS

Devsy uses SOPS key discovery. For age, set SOPS_AGE_KEY or SOPS_AGE_KEY_FILE, or use the standard age key location. For AWS KMS, GCP KMS, Azure Key Vault and PGP, use the same configuration you would use with SOPS.

Referencing secrets

An unqualified name is a Devsy-managed secret: DB_PASSWORD. An external secret is source-qualified: sops:project/DB_PASSWORD. workspace up fails if any reference cannot be resolved.

Environment variables

--secret is repeatable and exposes the value to lifecycle commands during that workspace up. Later terminal sessions do not see it; use an attached secret for that.

devsy workspace up . --secret DB_PASSWORD
devsy workspace up . --secret sops:project/DATABASE_PASSWORD,target=DB_PASSWORD

target= renames the variable. Repository secrets bindings have the same scope.

Files

type=mount writes the value to an in-memory file under /run/secrets:

devsy workspace up . --secret sops:project/TLS_KEY,type=mount,target=tls.key

The file is /run/secrets/tls.key. Providers without an in-memory mount return an error.

Build secrets

devsy workspace up . --build-secret sops:project/NPM_TOKEN

The build secret ID is the key (NPM_TOKEN), not the full reference. Use it in a Dockerfile with RUN --mount=type=secret,id=NPM_TOKEN ....

Attach a secret to a context

An attached secret is injected during workspace setup and into every new Devsy-managed SSH or terminal session, including devsy workspace ssh and the Desktop terminal:

devsy secret attach DB_PASSWORD
devsy secret attach sops:project/API_TOKEN
devsy secret detach DB_PASSWORD

Devsy stores only the reference. Running sessions keep their current environment. After detaching, run workspace up or recreate the workspace so new sessions lose the value.

If names collide, a session uses the first of: explicit session environment (workspace ssh --set-env), attached secret, the container's own environment.

Where each kind is available

SourceLifecycle environmentNew SSH or terminal sessionsFile mountImage build
Attached secretYesYesNoNo
--secret NAMEYesNoNoNo
--secret NAME,type=mountNoNo/run/secrets/<target>No
--secrets-fileYesNoNoNo
Repository secrets bindingYesNoNoNo
--build-secret NAMENoNoNoBuildKit only

Environment variables are inherited by child processes. Use type=mount for a narrower scope.

Storage backends

Metadata is in secrets.yaml. Each secret records its backend when created and stays there if you change the preference later. Backends:

  • keyring: the OS keyring.
  • file: the age-encrypted secrets.enc.
  • auto: use keyring when available, otherwise file. Applies to new secrets.

Set the preference for a context, or for one command with DEVSY_SECRETS_BACKEND:

devsy context set -o SECRETS_BACKEND=file

Passphrase for the file store

The file store is protected by either an automatically managed key or a passphrase. The mode applies to the whole file, across all contexts.

devsy secret protection status
devsy secret protection set-passphrase
devsy secret protection change-passphrase
devsy secret protection remove-passphrase

Devsy looks for the passphrase in this order. The first credential found is used, and a wrong one is an error:

  1. Explicit input from the UI or process.
  2. DEVSY_SECRETS_PASSPHRASE.
  3. DEVSY_SECRETS_PASSPHRASE_FILE.
  4. A remembered credential in the OS keyring.
  5. An interactive prompt, where supported.

For automation, run devsy secret protection set-passphrase once, then point DEVSY_SECRETS_PASSPHRASE_FILE at a file containing the passphrase (up to 64 KiB). Devsy strips one trailing newline. On Unix the file must be owner-only, such as 0600.

Optionally remember the passphrase in the OS keyring. The CLI and Desktop share it:

devsy secret protection remember
devsy secret protection forget

forget removes the saved credential; secrets stay encrypted. In Desktop, use Manage security on the Secrets tab. Changing protection asks for confirmation in a native dialog.

Recovery

If you lose the passphrase and have no remembered credential, reset the file store as a last resort:

devsy secret protection reset-file-store

The command lists every file-backed secret across all contexts and asks you to type RESET (or pass --yes). It moves secrets.enc to a quarantine file, removes the file-store metadata, and removes the remembered credential. Keyring secrets and env.yaml are untouched. Recreate the lost secrets afterwards.

Managed environment variables

For non-sensitive settings, devsy env stores plaintext values in env.yaml, separate from secrets, and works while the secret store is locked. Do not store sensitive values here.

devsy env set LOG_LEVEL=debug
devsy env set REGION --value us-east-1
devsy env list
devsy env get LOG_LEVEL
devsy env delete LOG_LEVEL

Inject them at startup. The optional right-hand side renames the variable:

devsy workspace up ... --env LOG_LEVEL --env REGION=AWS_REGION

--env takes managed environment variables only. Use --secret for sensitive values.

Attach a variable to the context to inject it on every start or recreate:

devsy env attach LOG_LEVEL
devsy env detach LOG_LEVEL

An explicit --env with a target overrides the attachment for that run. Attaching or detaching affects the next start, not a running workspace. Deleting a value also removes its attachment.

On this page