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

# Store and Manage Encrypted Secrets for Your Forwarders

> Create, rotate, and delete encrypted forwarder-scoped secrets that your TypeScript transform functions access through process.env at runtime.

A secret is an encrypted value scoped to one queued forwarder. Functions on that forwarder read secrets from `process.env`, so sensitive credentials like signing keys and auth tokens never need to appear in your function source code. Repost stores values encrypted at rest and never returns plaintext after creation.

## Scope and availability

Secrets belong to a specific forwarder, not to the bucket or workspace as a whole. This means each destination's credentials stay isolated from one another.

<CardGroup cols={3}>
  <Card title="Forwarder scoped" icon="route">
    A secret created on one forwarder is available only to functions on that same forwarder. Other forwarders on the bucket cannot access it.
  </Card>

  <Card title="Queued buckets only" icon="clock">
    Secrets are available on queued bucket forwarders. Proxy bucket forwarders hide the Secrets tab because proxy buckets do not support functions.
  </Card>

  <Card title="Masked values" icon="eye-off">
    The secrets table shows the key name, a masked value placeholder, and the last updated date. Plaintext values are never displayed after creation.
  </Card>
</CardGroup>

## Create a secret

Create secrets before writing function code that reads them. Existing keys appear as `process.env` suggestions in the function editor's autocomplete.

<Steps>
  <Step title="Open the forwarder's Secrets page">
    Navigate to the queued bucket forwarder whose functions need the credential and select the **Secrets** tab.
  </Step>

  <Step title="Create the key">
    Enter a key name using uppercase letters, digits, and underscores. The key must start with an uppercase letter. Valid examples: `STRIPE_SECRET_KEY`, `GITHUB_WEBHOOK_SECRET`, `INTERNAL_WEBHOOK_TOKEN`.
  </Step>

  <Step title="Enter the value">
    Enter a non-empty string value. After you save, Repost replaces the value with a masked placeholder in the dashboard. The value cannot be retrieved again — use **Edit value** to rotate it.
  </Step>

  <Step title="Reference the key from a function">
    Read the secret inside your transform using `process.env.STRIPE_SECRET_KEY`. The key appears in the editor's `process.env` autocomplete once it exists on the forwarder.
  </Step>
</Steps>

## Key naming rules

Secret keys follow standard environment-variable naming conventions so they map cleanly into `process.env`.

<AccordionGroup>
  <Accordion title="Key format">
    Keys must match the pattern `^[A-Z][A-Z0-9_]*$`: uppercase letters, digits, and underscores only, starting with an uppercase letter.
  </Accordion>

  <Accordion title="Valid key examples">
    `STRIPE_SECRET_KEY`, `GITHUB_WEBHOOK_SECRET`, `INTERNAL_WEBHOOK_TOKEN`
  </Accordion>

  <Accordion title="Invalid key examples">
    `stripe_secret` (lowercase), `1PASSWORD` (starts with a digit), `MY-SECRET` (contains a hyphen)
  </Accordion>

  <Accordion title="Duplicate keys">
    Repost rejects a key that already exists on the same forwarder. Use **Edit value** to replace the value behind an existing key instead of creating a duplicate.
  </Accordion>
</AccordionGroup>

## Read a secret in a function

Access secrets inside your transform through `process.env`. If a key is missing — because it was never created or was deleted — the value resolves to `undefined`. Always guard required credentials so your function's error policy can handle the failure cleanly.

```ts theme={"languages":{"custom":["/languages/repost.json"]}}
export function transform(
  req: WebhookRequest,
): TransformResult {
  const token = process.env.INTERNAL_WEBHOOK_TOKEN;

  if (!token) {
    throw new Error("Missing INTERNAL_WEBHOOK_TOKEN");
  }

  return {
    path: req.path,
    headers: {
      ...req.headers,
      authorization: `Bearer ${token}`,
    },
    body: req.body,
  };
}
```

<Tip>
  Run a function test after creating a secret to confirm it resolves correctly. Tests use the same forwarder-scoped secrets as live function execution. Missing keys resolve to `undefined` in both contexts.
</Tip>

## Rotate a secret value

Secret keys are stable identifiers. When you need to update a credential, rotate only the value behind the existing key — you do not need to update your function code.

<Steps>
  <Step title="Open the secret's action menu">
    Select **Edit value** for the key you want to rotate.
  </Step>

  <Step title="Enter the new value">
    The new value replaces the old one immediately for all future function tests and live executions. The key name does not change.
  </Step>

  <Step title="Run a function test">
    Confirm that the function still returns the expected transformed request with the new value in place.
  </Step>

  <Step title="Replay affected events if needed">
    If prior events failed because of the old value, replay the affected events after confirming the rotation is working correctly.
  </Step>
</Steps>

<Note>
  To rename a secret, create a new key with the new name, update your function code to read the new key, save the function, and then delete the old key once traffic has moved to the new key.
</Note>

## Delete a secret

Deleting a secret removes it from all future function tests and live executions. Your function source code may still reference the key, but the runtime value will be `undefined`.

<AccordionGroup>
  <Accordion title="Before you delete">
    Search the forwarder's functions for `process.env.KEY` and update any code that depends on the secret you are about to remove. Save updated functions before deleting the key.
  </Accordion>

  <Accordion title="After you delete">
    Function tests and live executions no longer receive that key. Source code that still reads it compiles without error, but the runtime value is `undefined`.
  </Accordion>

  <Accordion title="Failure handling">
    If a function throws because a deleted secret is missing, the function's **On Error** policy decides whether to send the event to failed deliveries, forward it as-is, or discard it.
  </Accordion>
</AccordionGroup>
