Skip to content

feat: capture devcontainer email to local file#3097

Open
Ben-Heerema wants to merge 5 commits into
developfrom
feat/devcontainer-mail-capture
Open

feat: capture devcontainer email to local file#3097
Ben-Heerema wants to merge 5 commits into
developfrom
feat/devcontainer-mail-capture

Conversation

@Ben-Heerema

@Ben-Heerema Ben-Heerema commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Route devcontainer Postfix mail through a local capture transport and write full raw emails to /var/log/carlos-mail-capture.eml instead of discarding them or attempting outbound SMTP delivery.
  • Capture-everything is the default — by default no test email can leave the container.
  • Add mail capture, mail show, mail clear, and mail test helper commands.
  • Add an opt-in, allowlisted real-delivery escape hatch for when a developer deliberately needs a test message sent for real.
  • Update Postfix email testing docs to describe capture behavior (and the opt-in path) instead of the old "discard" claim.

Confined to .devcontainer/ and docs/; no production or CARLOS application email code changes.

Why

The dev docs claimed local Postfix discarded all mail, but the config never enforced discard or prevented normal outbound SMTP delivery. Capturing mail locally by default gives a reliable, safe way to inspect generated provider-to-patient emails (headers, body, base64 attachments) without anything leaving the container.

Behavior

Default (no env flag): CARLOS sends to localhost:25 → Postfix accepts localhost-only → a transport_maps regexp (/.*/ → devcapture) plus default/relay/local_transport=devcapture overrides route every recipient to the devcapture pipe → the pipe writes the raw MIME (atomically, under flock) to the capture file. inet_interfaces=loopback-only keeps port 25 container-only. Nothing is delivered externally.

Opt-in real delivery (advanced): real send requires both CARLOS_MAIL_ALLOW_SEND=1 at mail start and an entry in /etc/postfix/carlos-send-allowlist (which ships empty). When enabled, the allowlist is consulted ahead of the capture map, so only allowlisted recipients go to smtp: while everything else is still captured. Optional CARLOS_MAIL_RELAYHOST sets an SMTP relay. mail status reports the active mode; mail start/restart warn loudly when send mode is on. The empty default allowlist means the env flag alone sends nothing.

Helper commands

mail start|stop|restart|status|log|capture|show|clear|test — capture file location and delivery mode are surfaced by status.

Acceptance criteria

  • By default, every accepted devcontainer message is captured locally to /var/log/carlos-mail-capture.eml; no accepted mail can use normal outbound SMTP.
  • Capture is atomic (no interleaving) and preserves the raw MIME payload (no injected From line).
  • Real outbound delivery is possible only via the explicit, allowlisted opt-in (env flag + non-empty allowlist), and is off by default.
  • The capture file is owned nobody:nogroup 0660 (not world-writable); port 25 stays loopback-only.
  • Docs accurately describe capture and the opt-in path.

Testing

  • Rebuilt devcontainer image; verified postconf -n routes mail to devcapture by default.
  • Verified atomic capture with a 5-way concurrent append test (no interleaving).
  • Simulated configure_send_mode for: flag unset (capture-only), flag set + empty allowlist (still captured, warns), flag set + entry + relayhost (allowlisted → smtp, others captured).
  • mail and the embedded capture script pass bash -n / sh -n.

Privacy / PHI

The capture file holds full raw email content (possible PHI-like test data, attachments). Do not commit it; clear with mail clear. Never add a real patient address to the send allowlist or enable send mode with real patient data — opt-in delivery is for synthetic test data and your own test inbox only.

Relates to #3096 (provider-to-patient email investigation). This does not fix #3096; it makes the local email path safe to test.

🤖 Generated with Claude Code

Route devcontainer Postfix mail through a local capture transport and
write full raw emails to /var/log/carlos-mail-capture.eml instead of
discarding them or attempting outbound SMTP delivery.

The dev docs claimed local Postfix discarded mail, but the config never
enforced discard or prevented normal outbound smtp delivery. This makes
provider-to-patient email testing safe and observable: every accepted
message is written locally and nothing leaves the container.

- Add postfix-capture-mail pipe script (runs as nobody) that appends the
  full MIME message plus envelope metadata to the capture file
- Register a devcapture pipe transport in master.cf and route all mail
  to it via transport_maps regexp plus default/relay/local_transport
  overrides (defense in depth); keep inet_interfaces=loopback-only
- Add mail capture/show/clear/test helper subcommands and surface the
  capture-file location in start/restart/status
- Rewrite docs/postfix-mail-server.md to describe capture behavior
  (not discard), with a privacy warning and routing-verification steps
- Add a devcontainer README pointer and list the new mail subcommands
  in help-dev

Relates to #3096

Signed-off-by: Ben Heerema <ben@maplecreekmedical.ca>
@Ben-Heerema Ben-Heerema self-assigned this Jun 30, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Ben-Heerema, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Ben-Heerema, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 31368c7e-ef93-414e-a5b0-7c692b215a83

📥 Commits

Reviewing files that changed from the base of the PR and between dae69a3 and 67f0404.

📒 Files selected for processing (2)
  • .devcontainer/development/Dockerfile
  • .devcontainer/development/scripts/mail
📝 Walkthrough

Walkthrough

Replaces the prior Postfix discard-transport dev setup with a pipe-based mail capture pipeline. The Dockerfile installs a capture script and configures all Postfix transports to route through devcapture, writing raw MIME to /var/log/carlos-mail-capture.eml. The mail script gains capture, show, clear, and test subcommands; help text and docs are updated accordingly.

Changes

Postfix dev mail capture pipeline

Layer / File(s) Summary
Dockerfile: devcapture transport and routing
.devcontainer/development/Dockerfile
Adds capture script (/usr/local/bin/postfix-capture-mail), registers devcapture pipe transport in master.cf, writes a regexp transport_maps file, and sets default_transport, relay_transport, and local_transport to devcapture: in main.cf.
mail script: new subcommands and capture helpers
.devcontainer/development/scripts/mail, .devcontainer/development/scripts/help-dev
Adds strict shell settings, capture-file setup, environment-driven delivery mode helpers, status reporting, and the new capture/show/clear/test subcommands; updates help text.
Documentation and README updates
docs/postfix-mail-server.md, .devcontainer/README.md
Rewrites the postfix mail-server guide around capture-based routing and adds the devcontainer Email Testing subsection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 A mailbox now stays close and neat,
With captured mail from every street.
mail show peeks at what arrived,
mail clear keeps the logs alive.
Hop hop—your test emails don’t roam,
They snuggle safely in your home.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR updates devcontainer mail capture, but it does not trace provider workflows, permissions, consent, or chart/eForm email behavior required by #3096. Add the investigation/documentation for chart and eForm email flows, backend routing, permission/consent checks, failure modes, and send-status review.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: capturing devcontainer email to a local file.
Description check ✅ Passed The description accurately summarizes the devcontainer email-capture and docs changes.
Out of Scope Changes check ✅ Passed All changed files stay within devcontainer email handling and docs, matching the stated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/devcontainer-mail-capture
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/devcontainer-mail-capture

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review
@cubic-dev-ai review

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 67f0404.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review
@cubic-dev-ai review

@Ben-Heerema I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@Ben-Heerema I'll review the changes.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a local email capture system for CARLOS EMR development within the devcontainer, routing all outbound emails to a local capture file (/var/log/carlos-mail-capture.eml) instead of sending them externally. It also adds new helper commands to the mail script and updates the documentation. The review feedback highlights three permission-related issues where non-root devcontainer users (such as vscode) would encounter 'Permission denied' errors when trying to read, tail, or touch the capture file and Postfix logs. The reviewer suggests changing the capture file permissions to 0666 and handling file existence checks more gracefully in the mail script to prevent crashes under set -e.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread .devcontainer/development/Dockerfile
Comment thread .devcontainer/development/scripts/mail
Comment thread .devcontainer/development/scripts/mail

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Confidence score: 4/5

  • In .devcontainer/development/scripts/mail, setting the capture file to chmod 0666 makes it world-writable instead of the documented 0660, which can let unrelated processes/users tamper with captured mail in the dev environment and make debugging output untrustworthy—switch to chown nobody:nogroup followed by chmod 0660 before merging.
Architecture diagram
sequenceDiagram
    participant App as CARLOS Application
    participant Port25 as localhost:25
    participant Postfix as Postfix SMTP
    participant DevCapture as devcapture Pipe Transport
    participant CaptureScript as /usr/local/bin/postfix-capture-mail
    participant CaptureFile as /var/log/carlos-mail-capture.eml
    participant MailLog as /var/log/mail.log
    participant MailHelper as mail CLI Helper

    Note over App,MailHelper: Devcontainer Local Email Capture Flow

    App->>Port25: Send email to localhost:25 (SMTP)
    Port25->>Postfix: Accept connection (loopback-only)
    Postfix->>Postfix: Check inet_interfaces=loopback-only

    alt Connection from localhost
        Postfix->>Postfix: Accept message
        Postfix->>Postfix: Lookup transport_maps regexp:/etc/postfix/carlos-transport-regexp
        Postfix->>Postfix: Match /.*/ to devcapture: transport
        Note over Postfix: Defense in depth: default_transport/devcapture:, relay_transport/devcapture:, local_transport/devcapture: catch any misses

        Postfix->>DevCapture: Route message via devcapture pipe
        DevCapture->>CaptureScript: Execute as user nobody
        Note over CaptureScript: Arguments: ${sender} ${recipient}

        CaptureScript->>CaptureFile: Append envelope metadata + raw MIME message
        CaptureScript-->>DevCapture: exit 0
        DevCapture-->>Postfix: Delivery status sent
        Postfix->>MailLog: Log delivery event

        Note over CaptureFile: Writes: Captured-At, Envelope-From, Envelope-To, BEGIN RAW MESSAGE, stdin (MIME), END RAW MESSAGE

        MailHelper->>CaptureFile: mail capture (tail -f)
        MailHelper->>CaptureFile: mail show (cat)
        MailHelper->>CaptureFile: mail clear (: > file)
        MailHelper->>Postfix: mail test (sendmail test message)

    else Connection from external host
        Postfix->>Postfix: Reject connection (inet_interfaces=loopback-only)
        Postfix->>MailLog: Log rejected connection
    end

    Note over App,CaptureFile: Captured emails contain full raw MIME including PHI - sensitive content warning
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .devcontainer/development/scripts/mail Outdated
The mail helper's ensure_capture_file widened the capture file to 0666,
diverging from the 0660 the image sets and documents. Restore the image's
nobody:nogroup ownership and 0660 mode when recreating the file so the
Postfix pipe user can still append without making it world-writable. Guard
the touch so a missing file never aborts the script under set -e.

Addresses cubic-dev-ai review finding on the PR.

Signed-off-by: Ben Heerema <ben@maplecreekmedical.ca>
@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review
@cubic-dev-ai review

@Ben-Heerema I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@Ben-Heerema I'll review the changes.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant CARLOS as CARLOS Application
    participant SMTP as Postfix smtpd
    participant QMGR as Postfix Queue Manager
    participant Transport as transport_maps (regexp)
    participant Pipe as devcapture pipe transport
    participant CaptureScript as postfix-capture-mail (nobody)
    participant CaptureFile as /var/log/carlos-mail-capture.eml
    participant Developer as Developer (mail helper)

    Note over CARLOS,CaptureFile: Local email capture flow (devcontainer only)

    CARLOS->>SMTP: SMTP connect localhost:25 (MAIL FROM, RCPT TO, DATA)
    SMTP->>SMTP: inet_interfaces=loopback-only (container only)
    SMTP->>QMGR: Accept and queue message

    QMGR->>Transport: Lookup recipient
    Transport-->>QMGR: /.*/ devcapture:  (regexp match)

    alt defense in depth (fallbacks)
        QMGR->>QMGR: default_transport=devcapture:
        QMGR->>QMGR: relay_transport=devcapture:
        QMGR->>QMGR: local_transport=devcapture:
    end

    QMGR->>Pipe: trigger devcapture unix pipe
    Pipe->>CaptureScript: argv=postfix-capture-mail ${sender} ${recipient}
    Note over Pipe,CaptureScript: stdin carries full raw MIME message

    CaptureScript->>CaptureScript: printf -- '...' (fixed bug: use printf --)
    CaptureScript->>CaptureFile: Append envelope metadata + raw MIME + separators
    CaptureScript-->>Pipe: exit 0

    Pipe-->>QMGR: status=sent (no external delivery)

    Developer->>CaptureScript: mail capture (tail -f)
    CaptureScript->>CaptureScript: ensure_capture_file (chown nobody:nogroup 0660)
    CaptureScript->>CaptureFile: tail -f updates (if CARLOS sends again)
Loading

Re-trigger cubic

@Ben-Heerema Ben-Heerema marked this pull request as ready for review June 30, 2026 16:44
@Ben-Heerema Ben-Heerema requested a review from yingbull as a code owner June 30, 2026 16:45
@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Ben-Heerema, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.devcontainer/development/Dockerfile:
- Around line 135-143: The mail-capture append block is not safe under
concurrent deliveries, so multiple `devcapture` processes can interleave writes
and corrupt the captured MIME. Update the capture logic in the devcontainer
Dockerfile’s `devcapture` handler to take an exclusive lock around the entire
`printf`/`cat` append sequence to `$CAPTURE_FILE`, using a single locked
critical section so each message is written atomically.
- Around line 156-159: The postfix capture service definition in the Dockerfile
is using pipe flags that alter the message stream before it reaches
postfix-capture-mail. Update the RUN that appends the devcapture entry to
master.cf so the pipe flags no longer include F, keeping the stdin payload raw
for /usr/local/bin/postfix-capture-mail while preserving the rest of the service
configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f05f12c-7d22-44a2-829b-58dd04fb32aa

📥 Commits

Reviewing files that changed from the base of the PR and between b72f9f2 and fe5d6bf.

📒 Files selected for processing (5)
  • .devcontainer/README.md
  • .devcontainer/development/Dockerfile
  • .devcontainer/development/scripts/help-dev
  • .devcontainer/development/scripts/mail
  • docs/postfix-mail-server.md
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: cubic · AI code reviewer
⚠️ CI failures not shown inline (8)

GitHub Actions: DCO / DCO Sign-Off: feat: capture devcontainer email to local file

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1m�[0m
 �[36;1mpr_json=$(curl -fsSL \�[0m
 �[36;1m  -H "Authorization: ***" \�[0m
 �[36;1m  -H "Accept: application/vnd.github+json" \�[0m
 �[36;1m  "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER")�[0m
 �[36;1mpr_commit_count=$(jq -r '.commits' <<< "$pr_json")�[0m
 �[36;1mbase_ref=$(jq -r '.base.ref' <<< "$pr_json")�[0m
 �[36;1m�[0m
 �[36;1mif [ "$pr_commit_count" -gt 250 ]; then�[0m
 �[36;1m  echo "::error::CARLOS EMR DCO check cannot automatically verify $pr_commit_count commits because GitHub's pull request commits API is capped at 250 commits."�[0m

GitHub Actions: DCO / 0_DCO Sign-Off.txt: feat: capture devcontainer email to local file

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1m�[0m
 �[36;1mpr_json=$(curl -fsSL \�[0m
 �[36;1m  -H "Authorization: ***" \�[0m
 �[36;1m  -H "Accept: application/vnd.github+json" \�[0m
 �[36;1m  "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER")�[0m
 �[36;1mpr_commit_count=$(jq -r '.commits' <<< "$pr_json")�[0m
 �[36;1mbase_ref=$(jq -r '.base.ref' <<< "$pr_json")�[0m
 �[36;1m�[0m
 �[36;1mif [ "$pr_commit_count" -gt 250 ]; then�[0m
 �[36;1m  echo "::error::CARLOS EMR DCO check cannot automatically verify $pr_commit_count commits because GitHub's pull request commits API is capped at 250 commits."�[0m

GitHub Actions: Automatic Dependency Submission (Maven) / 0_submit-maven.txt: Automatic Dependency Submission (Maven)

Conclusion: failure

View job details

##[group]Run if command -v mvn &> /dev/null; then
 �[36;1mif command -v mvn &> /dev/null; then�[0m
 �[36;1m  version=$(mvn -q -v)�[0m
 �[36;1m  echo "ignore-wrapper=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1melif [[ -x ./mvnw ]]; then�[0m
 �[36;1m  version=$(./mvnw -q -v)�[0m
 �[36;1m  echo "ignore-wrapper=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "::error::No Maven executable found. Please ensure Maven is installed or a Maven wrapper is present."�[0m

GitHub Actions: Automatic Dependency Submission (Maven) / submit-maven: Automatic Dependency Submission (Maven)

Conclusion: failure

View job details

##[group]Run if command -v mvn &> /dev/null; then
 �[36;1mif command -v mvn &> /dev/null; then�[0m
 �[36;1m  version=$(mvn -q -v)�[0m
 �[36;1m  echo "ignore-wrapper=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1melif [[ -x ./mvnw ]]; then�[0m
 �[36;1m  version=$(./mvnw -q -v)�[0m
 �[36;1m  echo "ignore-wrapper=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "::error::No Maven executable found. Please ensure Maven is installed or a Maven wrapper is present."�[0m

GitHub Actions: SonarCloud Analysis / Build and analyze: feat: capture devcontainer email to local file

Conclusion: failure

View job details

##[group]Run if [ -z "$SONAR_TOKEN" ]; then
 �[36;1mif [ -z "$SONAR_TOKEN" ]; then�[0m
 �[36;1m  echo "::error::SONAR_TOKEN is not available in this workflow run."�[0m

GitHub Actions: SonarCloud Analysis / Build and analyze: feat: capture devcontainer email to local file

Conclusion: failure

View job details

Node 20 is being deprecated. This workflow is running with Node 24 by default. If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
 Post job cleanup.
 (node:3894) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
 (Use `node --trace-deprecation ...` to show where the warning was created)
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/carlos/carlos --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: .sonar-cache/06f6abcba0e155ab188fa8a3d1d759b8/sonar-java-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/95f6c184575e3be4647b786069d6d682/sonar-securityjavafrontend-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/be77970dffb3af77b743873d2acfb1b2/sonar-dbdjavafrontend-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/c6ef680a93f0d72a08c373315db8eed6/sonar-xml-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/1d9ea99bf4667c88719b48067a250a82/sonar-architecturejavafrontend-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/0d405f7b8a964f21eae0e37f5ed11150/sonar-iac-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/c5530adf25b595550fc6dda8e33d4291/sonar-jasmin-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/b577e400c3b63c02eafc2c98c9f0f197/sonar-textenterprise-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/bc69b468a9274275cc4e49a3f8135d5d/sonar-architecture-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/38784d6093b56436b7506d76adff42dd/sonar-javaa3scontextcollector-plugin.jar: Cannot open: Permission denied
 /usr/bin/tar: .sonar-cache/a15aa695be750f8207145691ef64d6fe/sona...

GitHub Actions: SonarCloud Analysis / Build and analyze: feat: capture devcontainer email to local file

Conclusion: failure

View job details

884 Invalid character encountered in file /workspace/src/main/resources/oscar/form/prop/mentalHealthForm42.pdf at line 2 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.886 Invalid character encountered in file /workspace/src/main/webapp/webfonts/fa-solid-900.woff2 at line 2 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.900 Invalid character encountered in file /workspace/src/main/webapp/mcedt/mailbox/img/spinner.gif at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.900 Invalid character encountered in file /workspace/src/main/webapp/images/Logo_old.png at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.904 Invalid character encountered in file /workspace/src/main/webapp/form/graphics/SocialHistory.gif at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.904 Invalid character encountered in file /workspace/src/main/webapp/oscarReport/img/plusbottom.gif at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.907 Invalid character encountered in file /workspace/src/main/resources/oscar/form/prop/GrowthChartRourke2009Girlspg2.pdf at line 2 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARNING] 16:22:42.925 Invalid character encountered in file /workspace/src/main/webapp/images/icons/valid.png at line 1 for encoding UTF-8. Please fix file content or configure the encoding to be used using property 'sonar.sourceEncoding'.
 [WARN...

GitHub Actions: SonarCloud Analysis / 0_Build and analyze.txt: feat: capture devcontainer email to local file

Conclusion: failure

View job details

##[group]Run if [ -z "$SONAR_TOKEN" ]; then
 �[36;1mif [ -z "$SONAR_TOKEN" ]; then�[0m
 �[36;1m  echo "::error::SONAR_TOKEN is not available in this workflow run."�[0m
🧰 Additional context used
📓 Path-based instructions (2)
{README.md,docs/**/*.md,src/main/webapp/**/*.jsp,src/main/webapp/**/*.html}

📄 CodeRabbit inference engine (GEMINI.md)

Refer to this system as 'CARLOS EMR' or 'CARLOS' in all user-facing content (UI, documentation, README files, issue templates, workflows)

Files:

  • docs/postfix-mail-server.md
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Preserve all existing copyright headers in modified files; do not remove or change GPL version statements

Files:

  • docs/postfix-mail-server.md
🪛 Hadolint (2.14.0)
.devcontainer/development/Dockerfile

[info] 127-127: Multiple consecutive RUN instructions. Consider consolidation.

(DL3059)


[info] 156-156: Expressions don't expand in single quotes, use double quotes for that.

(SC2016)


[info] 162-162: Multiple consecutive RUN instructions. Consider consolidation.

(DL3059)

Comment thread .devcontainer/development/Dockerfile Outdated
Comment thread .devcontainer/development/Dockerfile

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant CARLOS as CARLOS Application
    participant SMTP as Postfix smtpd
    participant QMGR as Postfix Queue Manager
    participant Transport as transport_maps (regexp)
    participant Pipe as devcapture pipe transport
    participant CaptureScript as postfix-capture-mail (nobody)
    participant CaptureFile as /var/log/carlos-mail-capture.eml
    participant Developer as Developer (mail helper)

    Note over CARLOS,CaptureFile: Local email capture flow (devcontainer only)

    CARLOS->>SMTP: SMTP connect localhost:25 (MAIL FROM, RCPT TO, DATA)
    SMTP->>SMTP: inet_interfaces=loopback-only (container only)
    SMTP->>QMGR: Accept and queue message

    QMGR->>Transport: Lookup recipient
    Transport-->>QMGR: /.*/ devcapture:  (regexp match)

    alt defense in depth (fallbacks)
        QMGR->>QMGR: default_transport=devcapture:
        QMGR->>QMGR: relay_transport=devcapture:
        QMGR->>QMGR: local_transport=devcapture:
    end

    QMGR->>Pipe: trigger devcapture unix pipe
    Pipe->>CaptureScript: argv=postfix-capture-mail ${sender} ${recipient}
    Note over Pipe,CaptureScript: stdin carries full raw MIME message

    CaptureScript->>CaptureScript: printf -- '...' (fixed bug: use printf --)
    CaptureScript->>CaptureFile: Append envelope metadata + raw MIME + separators
    CaptureScript-->>Pipe: exit 0

    Pipe-->>QMGR: status=sent (no external delivery)

    Developer->>CaptureScript: mail capture (tail -f)
    CaptureScript->>CaptureScript: ensure_capture_file (chown nobody:nogroup 0660)
    CaptureScript->>CaptureFile: tail -f updates (if CARLOS sends again)
Loading

Re-trigger cubic

@Ben-Heerema Ben-Heerema marked this pull request as draft June 30, 2026 17:25
Address CodeRabbit review findings on the devcapture pipe:

- Serialize the capture append under an exclusive flock so concurrent
  devcapture deliveries can no longer interleave and corrupt the captured
  MIME. The capture file's own append-mode fd is the lock object, so no
  separate lock file (and no extra permission setup) is needed; falls back
  to a plain append if flock is absent.
- Drop the 'F' pipe flag (keep 'q'). 'F' prepended an mbox-style "From "
  envelope line before the capture script read stdin, polluting the raw
  MIME payload the capture file is meant to preserve.

Signed-off-by: Ben Heerema <ben@maplecreekmedical.ca>
@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review
@cubic-dev-ai review

@Ben-Heerema I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@Ben-Heerema I'll review the changes.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Dev as Developer
    participant Mail as mail CLI Helper
    participant Carlos as CARLOS App
    participant Postfix as Postfix SMTP
    participant Capture as capture script (nobody)
    participant CaptureFile as /var/log/carlos-mail-capture.eml

    Note over Dev,CaptureFile: NEW: All devcontainer mail is captured locally

    Dev->>Mail: mail start
    Mail->>CaptureFile: ensure exists, chown nobody:nogroup 0660
    Mail->>Postfix: service postfix start
    Mail-->>Dev: "Capture file: ..."

    Dev->>Mail: mail capture (or mail show / clear)

    Carlos->>Postfix: SMTP to localhost:25 (seeded emailConfig)
    Postfix->>Postfix: accept from loopback-only
    Note over Postfix: transport_maps=regexp:/etc/postfix/carlos-transport-regexp<br/>default/relay/local=devcapture:
    Postfix->>Capture: pipe: argv=postfix-capture-mail ${sender} ${recipient}
    Capture->>CaptureFile: flock(9) 9>> capture.eml
    Note over Capture: append envelope + raw MIME from stdin
    Capture-->>Postfix: exit 0
    Postfix-->>Carlos: delivered via pipe

    alt Developer sends test message
        Dev->>Mail: mail test
        Mail->>Postfix: sendmail -f noreply@openo-dev.local patient@example.test
        Postfix->>Capture: same pipe flow
        Capture->>CaptureFile: append test message
        Mail-->>Dev: "Run: mail capture"
        Dev->>Mail: mail capture
        Mail->>CaptureFile: tail -f
    end

    Dev->>Mail: mail clear
    Mail->>CaptureFile: truncate to 0 bytes
    Mail-->>Dev: "Cleared"
Loading

Re-trigger cubic

Keep capture-everything as the default (nothing leaves the container), but
allow a developer to deliver a specific test message for real when they
explicitly choose to. Real delivery requires BOTH a runtime env flag and an
allowlisted recipient, so it cannot happen by accident.

- Ship an empty recipient allowlist (/etc/postfix/carlos-send-allowlist); with
  CARLOS_MAIL_ALLOW_SEND=1 the 'mail' helper wires it into transport_maps ahead
  of the capture map, so only allowlisted recipients route to smtp while every
  other recipient is still captured. Empty allowlist => nothing sends.
- Optional CARLOS_MAIL_RELAYHOST sets an SMTP relay for real delivery.
- 'mail status' reports the active delivery mode and the current allowlist;
  'mail start'/'restart' print a warning banner when send mode is active.
- Document the opt-in flow with a PHI warning; update help-dev.

Default behavior is unchanged: without the flag, transport routing is the same
capture-only configuration as before.

Signed-off-by: Ben Heerema <ben@maplecreekmedical.ca>
@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review
@cubic-dev-ai review

@Ben-Heerema I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@Ben-Heerema I'll review the changes.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 5 files

Confidence score: 4/5

  • In .devcontainer/development/scripts/mail, leaving relayhost unchanged when CARLOS_MAIL_RELAYHOST is unset can make real-send mode silently reuse a stale relay from a prior devcontainer session, which could route mail through the wrong host — clear relayhost explicitly when the env var is empty before merging.
  • In .devcontainer/development/Dockerfile, the capture metadata path currently keeps only the first envelope recipient when Postfix sends multiple recipients, so devcapture records can be incomplete and mislead debugging — either store all recipient arguments or enforce single-recipient delivery for devcapture before merging.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".devcontainer/development/Dockerfile">

<violation number="1" location=".devcontainer/development/Dockerfile:133">
P3: Capture metadata drops all but the first envelope recipient when Postfix batches multiple recipients. Record all recipient arguments or force single-recipient delivery for `devcapture`.</violation>
</file>

<file name=".devcontainer/development/scripts/mail">

<violation number="1" location=".devcontainer/development/scripts/mail:35">
P2: Clear `relayhost` when `CARLOS_MAIL_RELAYHOST` is unset. Otherwise real-send mode can silently reuse a stale relay from an earlier devcontainer session.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Dev as Developer
    participant Mail as Mail Helper Script
    participant Postfix as Postfix Service
    participant CaptureScript as Capture Script (postfix-capture-mail)
    participant CapFile as Capture File (/var/log/carlos-mail-capture.eml)
    participant Allowlist as Allowlist File
    participant Carlos as CARLOS Application
    participant ExtSMTP as External SMTP (relay)

    Note over Dev,Carlos: Default Capture-Only Flow

    Dev->>Mail: mail start
    Mail->>Mail: configure_send_mode()<br/>CARLOS_MAIL_ALLOW_SEND unset →<br/>transport_maps = capture-only regexp
    Mail->>Postfix: service postfix start
    Postfix-->>Mail: started
    Mail-->>Dev: Capture file location

    Carlos->>Postfix: send email to localhost:25
    Postfix->>Postfix: smtpd accepts (loopback-only)<br/>transport_maps: /.*/ devcapture:
    Postfix->>CaptureScript: pipe message (flags=um, user=nobody)<br/>with envelope sender/recipient
    CaptureScript->>CaptureScript: flock exclusive lock<br/>on capture file
    CaptureScript->>CapFile: append raw MIME + envelope metadata<br/>(atomically)
    CaptureScript-->>Postfix: exit 0
    Postfix-->>Carlos: delivered to transport

    Note over Dev,Carlos: Opt-in Real Delivery Flow

    Dev->>Mail: CARLOS_MAIL_ALLOW_SEND=1 mail start
    Mail->>Mail: configure_send_mode()<br/>if flag=1 and allowlist has entries →<br/>transport_maps = allowlist regexp,<br/>then capture regexp
    alt Allowlist non-empty
        Mail->>Allowlist: read entries
        Allowlist-->>Mail: recipient patterns
        Mail->>Postfix: service postfix start<br/>with relayhost if set
        Postfix-->>Mail: started
        Mail-->>Dev: warning: real send enabled
    else Allowlist empty
        Mail->>Postfix: service postfix start<br/>(transport still capture-only)
        Postfix-->>Mail: started
        Mail-->>Dev: warning: allowlist empty, nothing will send
    end

    Carlos->>Postfix: send email to localhost:25
    Postfix->>Postfix: smtpd accepts<br/>transport_maps: check allowlist first
    alt Recipient matches allowlist entry
        Postfix->>ExtSMTP: smtp: deliver externally
        ExtSMTP-->>Postfix: delivered
    else No match
        Postfix->>CaptureScript: pipe to devcapture
        CaptureScript->>CapFile: append captured email
    end
    Postfix-->>Carlos: delivered (mode noted)

    Note over Dev: Developer Commands (test)
    Dev->>Mail: mail test
    Mail->>Mail: generate test message via sendmail
    Mail->>Postfix: send test message
    Postfix->>CaptureScript: pipe to devcapture (default)
    CaptureScript->>CapFile: append test message
    Mail-->>Dev: "Sent. Run: mail capture"
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .devcontainer/development/scripts/mail Outdated
Comment thread .devcontainer/development/Dockerfile
…capture)

Address cubic review findings on the opt-in real-delivery path:

- Set relayhost from CARLOS_MAIL_RELAYHOST on every `mail start`/`restart`,
  clearing it when the variable is unset, so a stale relay from an earlier
  devcontainer session can't be silently reused in real-send mode.
- Set devcapture_destination_recipient_limit=1 so Postfix invokes the capture
  pipe once per recipient; the script's ${recipient} (and the Envelope-To
  metadata line) is then always the single correct address even when Postfix
  batches multiple recipients for a message.

Signed-off-by: Ben Heerema <ben@maplecreekmedical.ca>
@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review
@cubic-dev-ai review

@Ben-Heerema I have started the AI code review. It will take a few minutes to complete.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@Ben-Heerema I'll review the changes.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Dev as Developer (shell)
    participant Mail as mail helper script
    participant Postfix as Postfix MTA
    participant CapturePipe as capture pipe (postfix-capture-mail)
    participant CaptureFile as /var/log/carlos-mail-capture.eml
    participant Allowlist as /etc/postfix/carlos-send-allowlist
    participant ExtSMTP as External SMTP

    Note over Dev,ExtSMTP: Devcontainer mail capture system (PR #3096)

    rect rgb(240, 240, 240)
        Note over Dev,Postfix: Initial setup (mail start / restart)
        Dev->>Mail: mail start / mail restart
        Mail->>Mail: configure_send_mode()
        alt CARLOS_MAIL_ALLOW_SEND=1
            Mail->>Postfix: postconf transport_maps=regexp:allowlist, regexp:carlos-transport-regexp
            Mail->>Postfix: postconf relayhost=$CARLOS_MAIL_RELAYHOST
            Note over Mail: Warns that real send is enabled
        else default (flag unset)
            Mail->>Postfix: postconf transport_maps=regexp:carlos-transport-regexp
        end
        Mail->>Postfix: service postfix start/restart
        Postfix-->>Mail: started
        Mail-->>Dev: status + capture file location
    end

    Note over Dev,ExtSMTP: Per-email flow (CARLOS app sends via localhost:25)

    CarApp->>Postfix: SMTP (localhost:25) | to: patient@example.test
    Postfix->>Postfix: Accept, inet_interfaces=loopback-only
    Postfix->>Postfix: Lookup transport_maps

    alt Default (capture-only mode)
        Postfix->>Postfix: Match /.*/ –> devcapture:
        Postfix->>CapturePipe: Pipe delivery (flags=q user=nobody)
        Note over CapturePipe: Arguments: ${sender} ${recipient}
        CapturePipe->>CapturePipe: flock on capture file
        CapturePipe->>CaptureFile: Write envelope metadata + raw MIME
        CaptureFile-->>CapturePipe: success (exit 0)
        CapturePipe-->>Postfix: delivery complete
        Postfix-->>CarApp: 250 OK (queued as captured)

    else Opt-in send mode (CARLOS_MAIL_ALLOW_SEND=1)
        Postfix->>Postfix: Match against allowlist first
        alt Recipient matches allowlist (e.g., /^me@test\.com$/ smtp:)
            Postfix->>ExtSMTP: smtp delivery to external MTA
            ExtSMTP-->>Postfix: 250 OK
            Note over Postfix: Recipient was delivered externally
        else Recipient does NOT match allowlist
            Postfix->>Postfix: Fallback to carlos-transport-regexp (capture)
            Postfix->>CapturePipe: Pipe delivery
            CapturePipe->>CaptureFile: Write captured email
            CaptureFile-->>CapturePipe: success
            CapturePipe-->>Postfix: done
            Note over Postfix: Captured locally even in send mode
        end
        Postfix-->>CarApp: 250 OK
    end

    rect rgb(245, 245, 245)
        Note over Dev,CaptureFile: Inspection commands
        Dev->>Mail: mail capture / mail show
        Mail->>CaptureFile: tail / cat (via ensure_capture_file)
        CaptureFile-->>Dev: raw MIME content
    end
Loading

Re-trigger cubic

@sonarqubecloud

Copy link
Copy Markdown

@Ben-Heerema Ben-Heerema marked this pull request as ready for review June 30, 2026 19:02

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Ben-Heerema, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@Ben-Heerema Ben-Heerema added this to the july-2026-release milestone Jul 2, 2026
@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@yingbull I think this is working but I tried to restart the container off this branch and was confused. Can you help in how I would go about testing this?

@yingbull

yingbull commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

you should be able to script a test to the smtp port?

@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

To replicate the manual mail-capture test for this PR:

  1. Rebuild/start the devcontainer, then make sure CARLOS is running at:
http://localhost:8080/carlos/
  1. Start local mail capture inside the Tomcat container:
docker exec -it carlos-tomcat-dev mail start
  1. In a second terminal, tail the captured email output:
docker exec -it carlos-tomcat-dev mail capture
  1. In CARLOS, log in and trigger an email flow, for example sending an eForm/email to a test recipient address.

  2. Confirm the terminal from step 3 shows a captured raw MIME message like:

===== CARLOS DEV EMAIL CAPTURE =====
Captured-At: ...
Envelope-From: noreply@openo-dev.local
Envelope-To: test@example.com
Subject: ...
Content-Type: multipart/mixed
Content-Disposition: attachment; filename=message.pdf

The captured message is written to:

/var/log/carlos-mail-capture.eml

This verifies that CARLOS sends to local Postfix and the devcontainer captures the message locally, including headers, body, and attachments, instead of delivering it externally.

@Ben-Heerema

Copy link
Copy Markdown
Collaborator Author

@yingbull I am happy with the testing now! It should be working as currently intended now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants