Skip to content

Environments

A Screenplay model is the same in every environment. What it needs to run - a client ID, an SMTP host, a signing key - is not. If you have used Pulumi stacks, the idea is the same: a project has named environments, and each one carries its own configuration next to the model.

Terminal window
cratis env init # creates "development"
cratis env init production # creates "production"
cratis env select production
cratis config set auth:clientId my-client
echo -n "$SMTP_PASSWORD" | cratis config set smtp:password --secret
cratis config

Each environment is one committed file, .cratis/environments/<name>.yaml. A name is any safe file name: letters, digits, ., _ and -, starting with a letter or digit - development, staging, customer-x.

secretsProvider: passphrase
encryptionSalt: <base64>
encryption:
kdf: pbkdf2-sha256
iterations: 600000
check: <base64>
config:
auth:clientId: my-client
smtp:host: mail.example.com
smtp:password:
secure: <base64>
signing:key:
ref: <vault reference>
EntryMeaning
key: valueA plain value.
key: { secure: <base64> }A secret encrypted by the passphrase provider. Safe to commit.
key: { ref: <name> }A secret kept in Azure Key Vault or HashiCorp Vault. The file holds only the reference.

secretsProvider, encryptionSalt and encryption describe where secrets live; see Secrets.

The environment in effect is, in order of precedence:

  1. --environment <name> (or -e <name> on env and config commands) on the command line
  2. the CRATIS_ENVIRONMENT environment variable
  3. your selection, made with cratis env select <name>
  4. development

The selection is per user and is not committed: it is written to .cratis/local/selected-environment, and cratis init (and cratis env select) add .cratis/local/ to .gitignore.

CommandDescription
cratis env init [name]Create an environment; development without a name. --secrets-provider picks where secrets are kept.
cratis env select <name>Select the environment for this user.
cratis env lsList environments; the one in effect is marked.
cratis env rm <name>Remove an environment (asks to confirm; --yes skips).
cratis config set <key> <value>Set a plain value.
cratis config set <key> --secretSet a secret, read from stdin or a hidden prompt.
cratis config get <key>Print a value; a secret prints as [secret] unless --show-secret.
cratis config rm <key>Remove a value; a secret in a vault is deleted from the vault too.
cratis configList the values; secrets show as [secret].
cratis config --missing <path>List the keys a model needs that the environment lacks.

All of them accept --output json and use the CLI exit codes: 0 success, 1 not found, 4 authentication failed (wrong passphrase), 5 validation (including missing keys for --missing).

cratis config --missing <path> compares the keys a model needs with the environment and exits with 5 when any are missing. <path> is a Screenplay model - a .play file, or a folder of them - or a key list. For a model, the keys are the ones Screenplay derives from its declarations: authentication.providers.<Identity>.clientId and .clientSecret for every identity provider (plus .tenantId for EntraId and .authority for OpenId), and onboarding.email.sender and onboarding.email.server.host, .port, .username and .password when the model declares onboarding. The convention is Screenplay’s, in its environments reference. A model that does not compile exits with 5 and names the first errors, since the keys of a model that is not understood cannot be listed. A key list is a JSON array of strings, or one key per line (blank lines and # comments ignored).

cratis run reads the same keys: it resolves the environment before it starts Docker and fails the same way when they are missing. See Run.

The Cratis.Environments NuGet package reads the same files, so Stage, AuthProxy tooling and other tools resolve values and secrets the same way.

var environments = ProjectEnvironments.Discover(Directory.GetCurrentDirectory());
var selected = environments.Resolve(explicitName: null);
var environment = ProjectEnvironment.Open(environments, selected.Name, PassphraseSources.FromEnvironment());
var clientId = await environment.GetValueAsync("auth:clientId");
var smtpPassword = await environment.GetValueAsync("smtp:password"); // decrypted or fetched from the vault