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

# Secrets management

> How Woku stores and delivers secrets to services in production, and what is expected of the local environment

All of Woku's sensitive credentials (database connection
strings, JWT keys, third-party API keys, Stytch SSO tokens,
etc.) are stored in **AWS SSM Parameter Store** as `SecureString`
and injected into the container in production at startup.

## Model

* **Storage**: AWS SSM Parameter Store, region `us-east-1`,
  under the prefix `/shared/prod/secrets/`. Type `SecureString` (encrypted
  with KMS, key `alias/aws/ssm`).
* **Injection**: AWS Copilot reads each parameter and delivers it to the
  container as an environment variable (`process.env`) when the ECS
  task starts. This happens automatically via the `secrets:` block of
  the application manifest.
* **Permissions**: the ECS task execution role has a policy
  scoped to `ssm:GetParameters` + `kms:Decrypt` only over the prefix
  `/shared/prod/secrets/*`. It cannot read other parameters in the
  account.

## Auditability

* Every read of a parameter is recorded in **AWS CloudTrail**.
* Every change (`PutParameter`, `DeleteParameter`) is recorded in CloudTrail
  as well, with the IAM identity of the author.
* KMS logs `Decrypt` operations separately in CloudTrail
  (data events if they are enabled).

## Why not AWS Secrets Manager?

SSM Parameter Store SecureString offers **the same KMS encryption** as
Secrets Manager and the same isolation guarantee. The main
difference is the **built-in automatic rotation** (RDS / DocumentDB /
Lambda), which we currently do not need. Choosing SSM saves us
the per-secret cost of Secrets Manager (\~\$0.40/month each)
while keeping the encryption and audit guarantees.

## What we do NOT do

* ❌ **We never commit secrets to the repository.** There is a
  `.dockerignore` that excludes `.env` and `.env.*` from the Docker image;
  local files never reach production.
* ❌ **We never pass secrets as inline arguments in CI/CLI.**
  They end up in the shell history and in CI logs. They are exported to the
  environment before running the command that needs them.
* ❌ **We never put secrets in the manifest `variables:`** (that
  section is in plain text inside the CloudFormation stack).

## Environments

| Environment    | Source of secrets                                                                            |
| -------------- | -------------------------------------------------------------------------------------------- |
| **Production** | SSM Parameter Store, read by Copilot when the container starts. This is the source of truth. |
| **Local dev**  | `.env` file in `woku-server/` (each developer has their own).                                |

> **Current state:** some local installations have production values
> mixed with sandbox inside their `.env`. The goal is
> for the local `.env` to contain **only dev/sandbox values** (local
> Mongo, Stytch sandbox project, Paddle sandbox, etc.) and for any
> connection to production resources to go exclusively through SSM when
> the container comes up in AWS.

If you need a new secret in production, follow
[the procedure](#adding-a-new-secret).

## Adding a new secret

1. **Upload the value to SSM.** Export the value in your shell and use the
   helper that lives in the repo:

   ```bash theme={null}
   export MI_SECRET_NUEVO="..."
   ./scripts/upload-secrets-to-ssm.sh
   ```

   The script uploads every variable that is exported to
   `/shared/prod/secrets/<NOMBRE>` as `SecureString`. It silently
   skips the ones that are not set.

2. **Reference it from the manifest** in
   `copilot/woku-server/manifest.yml`:

   ```yaml theme={null}
   secrets:
     MI_SECRET_NUEVO: /shared/prod/secrets/MI_SECRET_NUEVO
   ```

3. **Deploy.** The next `copilot deploy` brings up the container with the
   variable available in `process.env.MI_SECRET_NUEVO`.

## Rotating a secret

For credentials without automatic rotation (everything we have today):

1. Generate the new value in the provider (e.g.: new `STYTCH_SECRET`
   in the Stytch dashboard).
2. `aws ssm put-parameter --overwrite --type SecureString ...` (the
   helper script already does this).
3. Force-redeploy the service so it picks up the new value:
   `copilot svc deploy --name woku-server`.
4. Revoke the previous value in the provider.

> The container does **not** detect changes in SSM live. The values are
> read only once at startup. That is why you must always
> redeploy after rotating.

## Reporting a leak

If you discover an exposed secret (in a public repo, in logs, in a
PR, in a screenshot), send the details to `team@woku.app`. The
response process:

1. **Immediate rotation** of the compromised value.
2. Investigation of the previous value's usage in CloudTrail.
3. Customer notification if the rotation affects their integrations.

The default SLA is 4 business hours, or whatever your Corporate
contract stipulates if it requires a shorter time.
