# Storing Secrets and API keys

> Keep credentials out of your code

API keys, database passwords, and private keys don't belong in source code. Encore.rs lets you declare a secret in code, store its value with Encore's secrets manager, and read it at runtime.

## Declaring a secret

Declare a secret as a module-level `static`, then read it with `value()`:

```rust
-- billing/src/stripe.rs --
use encore::secrets::Secret;

static STRIPE_KEY: Secret = Secret::new("StripeKey");

fn authorization() -> Result<String, encore::Error> {
    Ok(format!("Bearer {}", *STRIPE_KEY.value()?))
}
```

Encore reads secret declarations from your source code, so a few rules apply:

- A secret must be declared as a module-level `static`, not inside a function.
- The name must be a string literal. It's an identifier: a letter or underscore, followed by letters, digits, and underscores.
- `new` is `const`, so declaring a secret does nothing at runtime. The value is looked up when you call `value()`.

## Scopes

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

| Type | Scope |
|---|---|
| `Secret` | Belongs to the service that declares it. Two services can each declare a `Secret` with the same name and have different values. It must be declared inside a service. |
| `SharedSecret` | One value for the whole app. Any service can declare it, and each service that reads it is granted access to it. |
| `GlobalSecret` | One value for the whole app, available to every service without being granted. |

```rust
use encore::secrets::{GlobalSecret, Secret, SharedSecret};

// This service's own signing key.
static SIGNING_KEY: Secret = Secret::new("SigningKey");

// Shared with other services that read it.
static GREETING: SharedSecret = SharedSecret::new("Greeting");

// Available everywhere.
static LICENSE: GlobalSecret = GlobalSecret::new("License");
```

Because a `Secret` is service-scoped, adding one to a service can never collide with, or silently reuse, a name another service already uses.

## Reading values

`value()` returns `Result<Value<T>, encore::Error>`. A `Value<T>` is a cheap, cloneable handle that dereferences to the secret's value:

```rust
let key = STRIPE_KEY.value()?;
let header = format!("Bearer {}", *key);
assert!(key.starts_with("sk_")); // String methods work directly
```

A secret's value can change while the app runs. A handle keeps the value it was made with, and the next call to `value()` returns the new one. Call `value()` where you use the secret rather than storing a handle for the lifetime of the process.

`Value<T>`'s `Debug` implementation prints `Value(<secret>)`, so a handle that ends up in a log line or a panic message doesn't leak the secret.

### Errors

`value()` returns an error when:

- The secret has no value in the current environment (`ErrCode::NotFound`).
- The stored bytes can't be read as the secret's type, such as invalid UTF-8 for a `String` or malformed JSON for a `Json<T>` (`ErrCode::Internal`). The error message says where the JSON is wrong, never what it contains.

## Value types

A secret is a `String` unless you give it another type:

```rust
use encore::secrets::{GlobalSecret, Json, Secret};
use serde::Deserialize;

// Raw bytes.
static SIGNING_KEY: Secret<Vec<u8>> = Secret::new("SigningKey");

// A JSON document, deserialized with serde.
#[derive(Deserialize)]
struct Limits {
    max_users: u32,
}

static LIMITS: GlobalSecret<Json<Limits>> = GlobalSecret::new("Limits");

fn max_users() -> Result<u32, encore::Error> {
    // Json<T> dereferences to the T inside.
    Ok(LIMITS.value()?.max_users)
}
```

The built-in types are `String`, `Vec<u8>`, and `Json<T>` for any `T: serde::de::DeserializeOwned`. To read a secret as a type of your own, implement the `SecretValue` trait's `from_bytes(bytes: &[u8]) -> Result<Self, encore::Error>`.

## Setting secret values

Use the `encore secret` command to set values. The value is read from stdin, so you can pipe it in, or you're prompted for it without echo when running interactively.

### On your machine

By default, `encore secret set` stores the value on your machine only, in `.secrets.local.toml` in the app root:

```shell
$ encore secret set StripeKey
```

A bare name refers to the app-wide secret (a `SharedSecret` or `GlobalSecret`). A service's own `Secret` is named with the service, either as `service.Name` or with `--service`:

```shell
$ encore secret set billing.SigningKey
$ encore secret set --service billing SigningKey
```

The name is checked against the secrets your app declares. Run `encore secret set` without a name to be prompted for each declared secret that has no local value yet.

The file is a plain TOML file, and you can also edit it by hand. App-wide secrets go in `[shared]`, and each service's own in `[services.<name>]`:

```toml
-- .secrets.local.toml --
[shared]
StripeKey = "sk_test_123"

[services.billing]
SigningKey = "local-signing-key"
```

Local values are never uploaded, and they take precedence over values from Encore Cloud. Keep the file out of version control; `encore secret set` adds it to `.gitignore` for you.

### For your team and deployed environments

To store a value in Encore Cloud, name the environment types it applies to with `--type`, or specific environments with `--env`:

```shell
$ encore secret set --type production StripeKey
$ encore secret set --type development,preview,local StripeKey
$ encore secret set --env staging StripeKey
```

Environment types are `production`, `development`, `preview`, and `local` (shorthands: `prod`, `dev`, `pr`). A value set for a named environment takes precedence over the value for its type. Values for `local` are synced to every teammate's `encore run` and `encore test`.

> **Note**
>
> Encore Cloud doesn't store per-service secret values yet. A service's own `Secret` can only be given a value on your machine; use a `SharedSecret` or `GlobalSecret` for values that deployed environments need.

### Listing and removing values

`encore secret list` shows which secrets have values and where, without printing the values. `encore secret list --missing` lists the declared secrets that have no value and exits non-zero if there are any, which is useful in CI.

`encore secret unset` removes a value. It takes the same flags as `set`: without a flag it removes your local value, and `--type` or `--env` removes a value from Encore Cloud.

## Secrets in config

A field of a [config](/docs/rs/develop/config) type can read a secret instead of holding a value:

```rust
#[derive(serde::Deserialize, encore::config::ConfigType)]
struct Stripe {
    endpoint: String,
    #[config(secret = "StripeKey")]
    api_key: encore::secrets::Secret,
}
```

The secret takes the config's scope and must also be declared in code. See [Configuration](/docs/rs/develop/config#secrets) for details.

## Testing

Under `encore test`, a test can give a secret a value of its own with `encore::testing::set_secret`. No other test sees it, and it's gone when the test ends:

```rust
#[encore::test]
async fn uses_the_test_key() -> Result<(), encore::Error> {
    encore::testing::set_secret(&STRIPE_KEY, "sk_test_fake")?;
    assert_eq!(*STRIPE_KEY.value()?, "sk_test_fake");

    // Any secret type works: the value is the raw bytes.
    encore::testing::set_secret(&SIGNING_KEY, [1, 2, 3])?;
    encore::testing::set_secret(&LIMITS, r#"{"max_users": 2}"#)?;
    Ok(())
}
```

The value is given as bytes (anything that implements `AsRef<[u8]>`) and read as the secret's type when you call `value()`, so a value that doesn't parse makes `value()` return an error. See [Testing](/docs/rs/develop/testing) for more.

## How secrets are stored

Values you set in Encore Cloud are stored encrypted. When you deploy to your own cloud account on AWS or GCP, Encore replicates them to a secrets manager in that account and provides them to your services at runtime. For local development, `encore run` fetches the `local` values from Encore Cloud and layers your `.secrets.local.toml` on top.
