Environments & Secrets
Environments
The platform runs in four environments:development, staging, production, and test.
The active environment is set via the pixwel_environment env var.
How secrets are managed
Secrets are stored in AWS SSM Parameter Store and fetched at runtime. Nothing sensitive is committed to git or baked into Docker images.SSM layout
| Path | What |
|---|---|
/platform/development/env | Full env file for local dev |
/platform/staging/env | Full env file for staging ECS containers |
/platform/production/env | Full env file for production ECS containers |
/platform/development/cloudfront_pem | CloudFront signing key (development) |
/platform/staging/cloudfront_pem | CloudFront signing key (staging) |
/platform/production/cloudfront_pem | CloudFront signing key (production) |
/platform/aspera_client_pem | Aspera JWT signing key (shared across envs) |
/platform/{environment}/{key} and injected into Lambda functions via api/serverless.yml.
How it works per deployment mode
Local dev (Codespace or laptop with docker-compose)
/platform/development/env→docker/development.env/platform/development/cloudfront_pem→docker/pems/cloudfront.pem/platform/aspera_client_pem→docker/pems/aspera.pem
install.sh calls this then copies docker/development.env to .env for docker-compose.
docker-compose.yml mounts docker/pems/ into every container at /opt/pixwel/pems. The PHP bootstrap uses those files directly — no SSM calls at container startup, so offline development works after the initial fetch-secrets.sh run.
Requires: AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY with SSM read access (one-time, for fetch-secrets.sh).
GitHub Actions (CI tests)
test.yml runs ./install/fetch-secrets.sh development before building the API test container.
GitHub Actions (deploy)
deploy.yml runs ./install/fetch-secrets.sh staging or ./install/fetch-secrets.sh production in the deploy jobs. The env file is copied to .env and passed to the ECS task via --env-file.
ECS containers (staging/production)
The container starts with the env file injected at runtime (--env-file). The PHP bootstrap (api/config/bootstrap/environments.php) fetches the signing keys from SSM once per container and caches them to a stable path ($TMPDIR/pixwel-pems); every request after the first reads the cached file. No keys are baked into the image, and there are no SSM calls in the request/response cycle — only the first request (or worker process boot) per container touches SSM.
Lambda (serverless API functions)
Secrets are injected at deploy time from SSM byserverless.yml via ${ssm:/platform/{env}/{key}} references. The PHP bootstrap caches cloudfront_pem / aspera_client_pem the same way as ECS — no runtime SSM calls.
AWS identities & access
Secrets access is controlled by IAM. There are a few distinct identities, by layer:| Where | Identity | Used for |
|---|---|---|
CI — fetch-secrets.sh step | engineering-test-user (member of the pixwel-platform-engineering group) via secrets.AWS_ACCESS_KEY_ID | reading SSM on the runner to write the env file + PEMs |
| CI — serverless deploys | a dedicated serverless-deploy user (hardcoded key id in deploy.yml) + secrets.ENG_AWS_SECRET_ACCESS_KEY | serverless deploy |
| App inside containers (ECS + CI test containers) | platform-staging / platform-production (the aws_key/aws_secret in the env file) | the app’s own AWS calls + the bootstrap’s per-container SSM fetch |
| Codespaces / local | pass-through AWS_ACCESS_KEY_ID from Codespace/host env (typically a pixwel-platform-engineering group member) | fetch-secrets.sh |
pixwel-platform-engineering group policy (for engineers + CI’s test user) and on the per-environment app users (platform-staging, platform-production). When adding a new SSM parameter, make sure the relevant identity is granted ssm:GetParameter on its path — e.g. the group policy currently allows /platform/development/* plus the shared /platform/aspera_client_pem.
Hardening backlog:engineering-test-useris a shared user with a long-lived static key, and CI stores static keys insecrets.AWS_ACCESS_KEY_ID. The modern pattern is GitHub Actions OIDC →sts:AssumeRole(no stored keys) and per-person credentials. Tracked separately.
Updating secrets
To update an env file:Adding a new secret
- Add to the SSM env file for each environment (
/platform/{env}/env) - For Lambda, also add an
${ssm:...}reference inapi/serverless.yml - No Dockerfile or git changes needed