Secrets

Keep API keys and credentials out of your code

API keys, database passwords and private keys don't belong in source code. Encore.py lets you declare a secret in code and give it a value outside it: on your machine, or in Encore Cloud for each environment.

Declaring a secret

Declare a secret as a module-level variable, with the type of its value, str or bytes:

billing/secrets.py
from encore.secrets import Secret stripe_key = Secret[str]("StripeKey")

Read the value with .value where you need it:

billing/payments.py
import httpx from billing.secrets import stripe_key async def charge(customer_id: str, amount: int) -> None: async with httpx.AsyncClient() as client: await client.post( "https://api.stripe.com/v1/charges", headers={"Authorization": f"Bearer {stripe_key.value}"}, data={"customer": customer_id, "amount": amount, "currency": "usd"}, )

A few rules apply, because Encore reads secret declarations from the source:

  • Declare secrets at module level, not inside functions or classes.
  • The name must be a string literal and an identifier: a letter or underscore, then letters, digits and underscores.
  • The type argument is required. Secret[str] decodes the value as UTF-8 and Secret[bytes] returns it unchanged. A variable annotation alone doesn't count: key: Secret[str] = Secret("Key") raises TypeError when you read .value.
  • Use the secret by reading .value (or .name). Encore records which services read which secrets from these accesses, so it rejects passing the secret object around as a value. Pass the value instead.

Reading .value raises KeyError if the secret has no value in the current environment. The value is looked up on first read and cached.

Secret scopes

The class you declare a secret with decides who it belongs to:

ClassBelongs toWhere you can declare it
Secret[T]The service that declares it. Two services can each have a secret with the same name and different values.Inside a service
SharedSecret[T]The whole app. Encore records each service that reads it.Anywhere
GlobalSecret[T]The whole app, available to every service. Reads are not recorded.Anywhere
from encore.secrets import GlobalSecret, Secret, SharedSecret api_key = Secret[str]("ApiKey") # this service's own signing_key = SharedSecret[bytes]("SigningKey") # shared by several services sentry_dsn = GlobalSecret[str]("SentryDSN") # e.g. for library code outside services

Service secrets have a namespace of their own, separate from shared and global secrets, so a service's ApiKey and a shared ApiKey are different secrets, and neither falls back to the other. Shared and global secrets use the same namespace. Declare each shared or global secret once in the app, and import the declaration wherever you need it.

Setting secret values

Use encore secret set. It reads the value from stdin, so you can pipe it in, or it prompts for it without echoing when run in a terminal.

On your machine

By default, encore secret set writes a value for your machine only:

$ encore secret set SigningKey # a shared or global secret
$ encore secret set --service billing ApiKey # the billing service's own secret
$ encore secret set billing.ApiKey # the same thing

Run encore secret set with no name to be asked for each declared secret that has no value on your machine.

These values are stored in .secrets.local.toml in your app's root, next to encore.app. The file is meant to be gitignored. You can also edit it directly:

.secrets.local.toml
[shared] SigningKey = "local-signing-key" [services.billing] ApiKey = "sk_test_123"

[shared] holds shared and global secrets, and [services.<name>] holds each service's own. Values are plain strings.

A running service keeps the values it started with. When a value it uses changes, encore run reports the service as stale until it restarts.

In Encore Cloud

To store a value in Encore Cloud, name the environment types or environments it's for:

$ encore secret set --type production SigningKey
$ encore secret set --type development,preview,local SigningKey
$ encore secret set --env staging SigningKey

The environment types are production (prod), development (dev), preview (pr) and local. A value set for a named environment with --env takes precedence over the value for its type. For an app linked to Encore Cloud, values for the local type reach every developer's encore run and encore test; a value in .secrets.local.toml overrides them on your machine.

You can also manage values in the Encore Cloud dashboard, under Settings > Secrets.

Take care

Encore Cloud doesn't store per-service secret values yet. A service's own Secret can only get a value on your machine, with --service. For values you need in deployed environments, use SharedSecret or GlobalSecret.

Checking and removing values

$ encore secret list # which secrets have values, and where
$ encore secret list --missing # only declared secrets without a value; exits non-zero if any
$ encore secret unset SigningKey # remove this machine's value
$ encore secret unset --type production SigningKey

encore secret list never prints values. encore secret unset takes the same target flags as set.

Secrets in tests

encore test gives your tests the same values as encore run: the local values from Encore Cloud, overridden by .secrets.local.toml. A secret with no value raises KeyError when the test reads it.

Secrets in configuration

A config can have fields that read a secret rather than hold a value, so code reading the config gets the secret along with its other settings. See Secrets in config.