# Pi Stage 4 SMTP transport deployment evidence

Status: accepted
Type: evidence
Updated: 2026-08-02
Workpackage: work/wp/active/wp-build-standalone-pi-sd-qualification-operator-application.md

## Scope

This records deployment, authentication, and one recipient-confirmed delivery
through the fixture-only Stage 4 SMTP transport on `srv-rpi00.local`. No
controller command, firmware change, media operation, power operation, or live
qualification admission occurred.

## Integrated identity

- Bounded notification egress: `PC-Redemption/sd-qualification#9`, merged as
  `3401766754236075db774398891d9b47bb0c3df9`.
- SMTP credential confinement: `PC-Redemption/sd-qualification#10`, merged as
  `47529dfcaef088d008003a9196a06a8e03b91ff1`.
- Reminder/escalation regression: `PC-Redemption/sd-qualification#11`, merged
  as `4523e69ea65842032b927e520370e4719fd2dc77`.
- Final deployed release: `notification-stage4-complete-4523e69`.
- Final image ID:
  `sha256:7d71001171edcc94dad358a83ddc750ebbed656d5cc91af220d53fdd65066ca0`.
- Retained final release:
  `/var/tmp/sdq-release-notification-stage4-4523e69`.
- Retained immediate rollback: Stage 4 authenticated-transport release
  `47529df`; Stage 3 `6809dce` and Stage 2 `9327e28` images/packages also
  remain.

## Transport and security gates

- Host DNS and TLS to `smtp.gmail.com:587` passed with certificate verification
  and TLS 1.3 before authentication.
- The owner installed the 16-character app password directly into the
  owner-only (`0600`) ignored `.env`; the value was not printed, copied into
  Git, or placed in an artifact.
- An initial authentication attempt correctly exposed that all application
  services were confined to an `internal: true` Docker network. No message was
  sent and the credential was not blamed for that DNS failure.
- PR #9 added a Compose-managed, unexposed bridge used only by
  `notification-worker`, while retaining its private PostgreSQL network.
- Review before deployment found that the shared `env_file` could expose the
  SMTP password to other application services. PR #10 explicitly blanks that
  variable in the shared environment and restores the protected value only for
  `notification-worker`.
- Rendered Compose assertions prove web, command-worker, and migration receive
  a blank SMTP password; the notification worker receives the protected value.
  Runtime inspection after deployment confirmed the same boundary without
  printing the value.
- Runtime networks are: web=`internal,frontdoor`,
  command-worker=`internal`, and
  notification-worker=`internal,notification-egress`. No SMTP service port is
  published.
- Authenticated Django SMTP connection to Gmail passed after final deployment.
- The application created fixture-only campaign
  `dd8c73e4-fc41-44a2-aae4-6fc668eea438` in `DRAFT` and durable notification
  `1` with intervention type `FIXTURE_REQUEST`, no controller assignment, and
  safe state `FIXTURE_SAFE_NO_PHYSICAL_ACTION`.
- The notification worker delivered the message to the owner-approved recipient
  in one attempt at `2026-08-02T19:57:26.129785Z`; the outbox recorded
  `SENT` and provider response `delivery accepted`.
- The recipient confirmed that the Gmail inbox received the expected subject
  and bounded body. The application then recorded append-only acknowledgement
  at `2026-08-02T19:58:31.962705Z`. Final notification state is
  `ACKNOWLEDGED`, with one delivery attempt and zero reminders or escalations;
  campaign state remains fixture-only `DRAFT`.
- With explicit owner authorization, durable notification `2` exercised a
  controlled transport outage against closed local port `127.0.0.1:1`. The
  first attempt recorded `FAILED`, delivered no message, and scheduled the
  normal 118-second retry while the normal notification worker remained
  stopped.
- The recorded backoff elapsed without database timestamp manipulation. One
  worker cycle then used restored Gmail configuration and transitioned the
  same notification through preserved attempt history `FAILED -> SENT` at
  `2026-08-02T20:03:47.702302Z`. The recipient confirmed receipt, and the
  application recorded acknowledgement at `2026-08-02T20:04:47.275551Z`.
  Final state is `ACKNOWLEDGED`, attempts=2, reminders=1, escalations=0, and
  the campaign remains fixture-only `DRAFT`.
- PR #11 adds deterministic in-memory coverage for initial action, reminder,
  escalation, auditable attempt counters, and unchanged campaign state. It
  sends no external mail.

## Verification and recovery

- Final exact image: 63 isolated-image Django tests passed.
- Repository contract/package/network suite: 15 tests passed.
- Release package hashes:
  - `image.tar`:
    `0eab4d6aa08e08d23a605b1d35897c4044250448e9692634c8aa06626c6df0f8`;
  - `manifest.json`:
    `1423c9aaa6e4b8dc91bf7d6d106836713bd79f6a2a3ca3eae6ea951537a86a4e`;
  - `sbom.cdx.json`:
    `bbaef9c72b22544fe342edefd8ef4e956041e832935c7972e8bbddee65fd33d9`;
  - `SHA256SUMS`:
    `af577cbb6892b3df5ec2ea32639a9d2eec8e25aed6c06ff27b6f83926231d84c`.
- Final pre-deploy database backup and disposable restore-check passed at
  `/backups/sd-qualifier-20260802T200716Z.dump`, SHA-256
  `a5fb5a70e73f2552b021a7dc83686c82656e7d39c055217c465999136b25ce73`.
- Final post-deploy backup
  `/backups/sd-qualifier-20260802T200943Z.dump` has SHA-256
  `791bab5502e249c05b7815cfa39bee9821c5f253e5dcaaab179acc53dc687e14`;
  disposable restore-check passed.
- Web, command-worker, and notification-worker all run the final exact image
  with zero restarts. Django system check and application health pass with
  `fixture_only=true` and `live_admitted=false`.
- The undeployed pre-confinement Stage 4 image/package and merged temporary
  branches/worktree were removed after final verification.

## Remaining gate

Stage 4 notification qualification is complete. Exact certificate-provider
HTTPS hostnames remain required separately before the protected
certificate-fetch policy can be admitted. Controller execution remains
disabled pending the pinned-validator and separately authorized read-only A/B
adapter gates.
