Skip to main content
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

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

  1. Upload the value to SSM. Export the value in your shell and use the helper that lives in the repo:
    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:
  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.