Browser-local boundary planner
Turn three observed states into one next action
No .env value, SMTP password, recipient, reset URL, token, message, provider ID or credential is requested or transmitted.
- Boundary
- Next action
- Exit condition
- Safety
The five boundaries
- 1. Interpolation
- Compose can read values from a project
.envfile while resolving${VARIABLE}expressions. - 2. Service export
- Only variables declared through the service
environmentorenv_fileconfiguration are placed in that container. - 3. Running process
- The created app container must actually contain the key names expected by the deployed application revision.
- 4. Send path
- The password-reset handler must call the mailer and receive either a bounded failure or provider acceptance.
- 5. Completion
- Receiver visibility and successful reset-link consumption remain separate from SMTP configuration.
Do not paste .env output, SMTP values, a recipient address, reset URL, token, provider message identifier, private key, seed phrase or wallet signature into a public issue or diagnostic.
What the source proves in Riffado
At Riffado revision 731c728314a685a488d0f03ab21828f244f60028, .env.example declares optional SMTP_* variables and the mail code reads those names from process.env. The official docker-compose.yml app service enumerates its environment but does not include the SMTP keys. That is enough to test the service-export boundary before changing DNS, the receiver mailbox, or the reset page.
The same issue also reports a reset-page failure. Keep that as a later client/server and token-consumption investigation: a missing email and a crashing link page are two independent defects.
This is source evidence for that revision, not a claim about every deployment. A local override file, another Compose file, a manually supplied environment, or a later revision can change the effective model.
Prove key presence without printing values
Run each check from the directory and Compose-file set used for the actual deployment. The first command lists interpolation key names only; the second lists key names exported by the resolved app service.
docker compose config --environment | cut -d= -f1 | sort
docker compose config --format json \
| jq -r '.services.app.environment | keys[]' \
| sort
Then check boolean presence inside the running application process. This command does not print any value:
docker compose exec app node -e \
"console.log(Object.fromEntries(
['SMTP_HOST','SMTP_PORT','SMTP_SECURE','SMTP_USER','SMTP_PASSWORD','SMTP_FROM']
.map(k => [k, Boolean(process.env[k])])
))"
docker compose config --environment without the key-only filter can expose interpolation values. Keep raw output private and never attach it to a public report.
Pass the variables into the app service
When the resolved service omits the keys, add explicit mappings to the existing app.environment block. Keep the actual values in a protected, untracked environment source:
services:
app:
environment:
SMTP_HOST: ${SMTP_HOST:?set SMTP_HOST}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_SECURE: ${SMTP_SECURE:-false}
SMTP_USER: ${SMTP_USER:?set SMTP_USER}
SMTP_PASSWORD: ${SMTP_PASSWORD:?set SMTP_PASSWORD}
SMTP_FROM: ${SMTP_FROM:?set SMTP_FROM}
SMTP_MARKETING_FROM: ${SMTP_MARKETING_FROM:-}
SMTP_REPLY_TO: ${SMTP_REPLY_TO:-}
EMAIL_SEND_RATE_PER_SECOND: ${EMAIL_SEND_RATE_PER_SECOND:-1}
An env_file can also populate a container, but an explicit list limits accidental export. Docker recommends secrets rather than environment variables for sensitive values; adopting Compose secrets also requires the application to read file-backed values. Do not assume that support exists without changing and testing the application.
Recreate, then send once
docker compose up -d --force-recreate app
A plain docker compose restart does not apply changed environment configuration. After recreation, repeat the presence-only check, trigger one password reset for a controlled account, and correlate its UTC timestamp to one application result and one provider event.
- If the app still reports SMTP not configured, verify non-empty required values and the exact names read by the deployed revision.
- If the transport fails, retain only the bounded status or exception and fix that named boundary.
- If the provider accepts the message, follow that exact event through delivery, delay, bounce, suppression, or receiver acceptance.
- If the message is visible, test the newest reset link once as a separate route and token-use proof.
Read the three-section sample first. The complete 13-section Web3 Email Authentication Operations Kit costs exactly 1 native USDC on Base only when it is useful to you.
The production-ready proof
Call the mail path ready only when the resolved service includes the expected keys, the current app process confirms presence without revealing values, the application records one reset-send attempt, and the provider or receiver records the same message instance. Record reset-link completion separately.
This independent guide is not affiliated with Riffado or Docker. The effective Compose model, running container, application logs, provider events, and controlled receiver remain the sources of truth for their respective boundaries.
Primary references
- Riffado issue 241: self-hosted Docker deployment report
- Riffado revision: SMTP keys in .env.example
- Riffado revision: app service environment in docker-compose.yml
- Riffado revision: SMTP environment reader
- Riffado revision: SMTP readiness and transport
- Docker: Compose variable interpolation
- Docker: Set environment variables in a container
- Docker: compose up and force recreation
- Docker: compose restart configuration boundary