> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runlayer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Secret Stores

> Keep connector credentials in your own AWS Secrets Manager. Runlayer stores references, not secret values

A **secret store** lets Runlayer resolve a connector's upstream credentials from your secret manager at connection time, instead of storing them in Runlayer. Runlayer's database holds only a reference like `secretref://prod-aws/runlayer/jira#api_key`; the secret value is fetched when a connection is made, held in memory only, and never persisted or logged.

## What This Changes

* **At-rest exposure stays low.** A compromise of Runlayer's database yields references, not credentials.
* **Rotation without Runlayer.** Rotate the secret in AWS; new connections pick up the new value within the cache window (about 2 minutes). Existing connections keep the value resolved when they connected.
* **Runtime access still exists.** The Runlayer data plane still reads the secret at resolve time. Secret stores remove at-rest exposure, not runtime exposure.

Secret stores currently support **AWS Secrets Manager**. Additional backends, including HashiCorp Vault, are on the roadmap.

## Set up an AWS Secrets Manager store

Runlayer generates an **External ID** for each store when you create it — you don't invent one. The setup order is therefore: create the role, register the store, then paste the generated External ID into the role's trust policy.

<Steps>
  <Step title="Create the IAM role">
    In the AWS account holding your secrets, create a role Runlayer can assume.
    Give it read access to the secrets you intend to reference:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "secretsmanager:GetSecretValue",
          "Resource": "arn:aws:secretsmanager:us-east-1:<your-account>:secret:runlayer/*"
        }
      ]
    }
    ```

    Start with a trust policy that allows Runlayer's account to assume it. For
    self-hosted deployments, use your backend's IAM role ARN as the principal
    instead. You will tighten the policy with the External ID after registering
    the store:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": { "AWS": "arn:aws:iam::<runlayer-account-id>:root" },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    ```

    <Note>
      A separate, scoped IAM role is required for every store, including
      self-hosted deployments. Runlayer always assumes that role with the
      store's External ID. It never reads referenced secrets directly with the
      backend's IAM role. Multiple stores can share a role if its trust policy
      allows each store's External ID.
    </Note>
  </Step>

  <Step title="Register the store">
    Open **Settings → Secret Stores**. Click **Add secret store** in the empty
    state, or **Add** in the header. Enter a name, the AWS region for the secrets,
    and the role ARN from the previous step.

    The saved store card shows a generated **External ID** such as `rl:3f2a...`.
    Copy this value.
  </Step>

  <Step title="Add the External ID to the trust policy">
    Add the `sts:ExternalId` condition to the role's trust policy using the value
    from the store card. This is the
    [confused-deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html)
    protection: Runlayer sends this store's External ID on every assume-role,
    and the condition ensures only Runlayer acting for your store can assume the
    role.

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": { "AWS": "arn:aws:iam::<runlayer-account-id>:root" },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringEquals": { "sts:ExternalId": "rl:3f2a..." }
          }
        }
      ]
    }
    ```
  </Step>

  <Step title="Test role assumption">
    After updating the trust policy, return to Runlayer and click **Test role
    assumption** on the store card. This verifies that Runlayer can assume the
    role using the store's External ID; it does not verify
    `secretsmanager:GetSecretValue` for a particular secret. Use the
    connector's connection test to verify access to each referenced secret.
  </Step>
</Steps>

## Reference Secrets from Connectors

Anywhere a connector's transport configuration takes a header value or stdio
environment value, you can use a reference instead:

```text theme={null}
secretref://<store-name>/<secret-name-or-arn>#<json-field>
```

* `<store-name>` — the store name from **Settings → Secret Stores**.
* `<secret-name-or-arn>` — the Secrets Manager secret name or full ARN.
* `#<json-field>` — optional; when the secret value is a JSON object, selects one key. Omit it to use the whole secret string.
* The full value must be the `secretref://...` reference. Embedded references inside a larger string are not resolved.

Examples:

```text theme={null}
# Header value where the AWS field stores the full "Bearer ..." string:
Authorization: secretref://prod-aws/runlayer/jira#authorization

# Secret whose name starts with /:
X-API-Key: secretref://prod-aws//team/key

# Env value for a stdio server:
API_KEY=secretref://prod-aws/my-service-api-key
```

For bearer-token headers, store the complete header value in AWS, such as:

```json theme={null}
{
  "authorization": "Bearer sk-..."
}
```

Then reference the `authorization` field:

```text theme={null}
Authorization: secretref://prod-aws/runlayer/jira#authorization
```

The server's **connection test** resolves references too, so a misconfigured store or missing permission fails with an actionable message before anything ships.

For a safe rotation, keep the old and new credentials valid at the same time
when the upstream supports it. Update the AWS secret, wait for the cache window,
then reconnect or drain existing sessions before revoking the old credential.
Long-lived sessions do not re-resolve secret references in place, so revoking
the old credential immediately can interrupt them.

## Behavior Details

| Behavior | Value |
| - | - |
| Rotation lag | Resolved values are cached in memory for about 2 minutes; new connections pick up the new value within that window, while existing connections keep their originally resolved value until reconnect |
| Feature flag disabled | Store management and new secret resolutions stop after the flag change propagates (up to about 5 minutes). Cached values are not used for new resolutions; existing connections keep their already-resolved values until reconnect |
| AWS outage | Fresh cached values remain usable until their authorization window expires (at most about 2 minutes); failed refreshes then fail closed. Expired values are never served |
| Access revoked or secret deleted in AWS | Fails with an actionable error on the next AWS fetch; fresh cached values may be used for up to about 2 minutes |
| Deleting a store | Allowed even while referenced; subsequent connection preparation fails with "store not found". Preparation already in progress may finish, and existing connections keep their already-resolved values |
| Reusing a store name | Creating another store with a deleted store's name rebinds existing name-based references on subsequent connection preparation. The replacement never reuses the deleted store's cache |
| Secret values | Must be non-empty. A `#field` must select a JSON string; numbers, booleans, nulls, arrays, and objects are rejected instead of converted. Values are not trimmed or otherwise normalized |
| Newlines | Resolved HTTP header values containing CR or LF are rejected. Stdio environment values may be multiline and are preserved exactly |
| Binary secrets | Not supported; `SecretString` only |
| Audit | Store create, update, and delete operations are audit-logged; resolution failures are logged and metered. Secret values never appear in logs or audit entries |

## Permissions

* **Viewing stores**: organization settings admins, connector admins, and Deploy users can view the page when secret stores are enabled.
* **Managing stores**: only organization settings admins can create, update, delete, and test stores.
* **Referencing stores**: connector admins can reference stores in connector config. The store list is visible to them for that purpose.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.