From e741619ca142d9f3648e760a974c8a69c1430ab3 Mon Sep 17 00:00:00 2001 From: Md Nurnobi Date: Mon, 17 Aug 2026 11:05:30 -0700 Subject: [PATCH 1/3] fix(deploy): harden installation recovery and inbound delivery --- .github/ISSUE_TEMPLATE/question.yml | 2 +- .github/workflows/ci.yml | 4 +- CHANGELOG.md | 18 ++ CITATION.cff | 4 +- PATCH_BASELINE.txt | 11 + PATCH_FILE_LIST.txt | 51 ++++ PATCH_MANIFEST.sha256 | 54 +++++ PATCH_RELEASE_NOTES.md | 26 ++ README.md | 6 +- ROADMAP.md | 5 + SECURITY.md | 3 +- VERSION | 2 +- .../templates/dovecot/99-vibmail.conf.tpl | 2 +- design/DESIGN_MANIFEST.json | 2 +- docs/BUILD.md | 4 +- docs/FORENSIC_AUDIT_REPORT.md | 27 ++- docs/FORENSIC_FILE_INVENTORY.json | 228 ++++++++++-------- docs/INSTALLATION.md | 18 +- docs/OPERATIONS.md | 15 +- docs/PUBLISHING.md | 16 +- docs/QUICKSTART.md | 10 +- docs/RELEASE_NOTES_1.3.0.md | 95 +++++--- docs/RELEASE_PROCESS.md | 2 +- docs/TEST_REPORT.md | 24 +- docs/TROUBLESHOOTING.md | 15 +- documents/ADMIN_GUIDE.md | 11 +- documents/BASELINE.md | 4 +- documents/DOCUMENTATION_MANIFEST.json | 110 +++++---- documents/DOCUMENTATION_POLICY.md | 4 +- documents/HOW_TO_USE.md | 4 +- documents/README.md | 33 +-- documents/USER_MANUAL.md | 4 +- .../design/ACCESSIBILITY_SPECIFICATION.md | 4 +- documents/design/COMPONENT_MATRIX.md | 4 +- documents/design/FUTURE_UI_ROADMAP.md | 4 +- documents/design/IMPLEMENTATION_STATUS.md | 4 +- documents/design/RESPONSIVE_SPECIFICATION.md | 4 +- documents/design/SCREEN_CATALOG.md | 4 +- documents/design/UI_FOUNDATION.md | 4 +- documents/phases/PHASE-000-BASELINE.md | 4 +- .../PHASE-001-UI-DESIGN-INTAKE-BASELINE.md | 4 +- ...RED-UI-FOUNDATION-AND-APPLICATION-SHELL.md | 4 +- ...COVERY-AND-INBOUND-DELIVERY-RELIABILITY.md | 86 +++++++ install.sh | 104 +++++--- mailbox-app/CHANGELOG.md | 6 + .../commands/create_initial_admin.py | 33 ++- .../management/commands/ingest_maildir.py | 18 +- mailbox-app/apps/ingestion/service.py | 34 +-- .../commands/create_system_mailbox.py | 44 +++- mailbox-app/config/settings/production.py | 6 + mailbox-app/pyproject.toml | 2 +- .../test_commands_health_postfix.py | 20 ++ .../tests/security/test_deployment_assets.py | 12 + scripts/test_installer.py | 25 ++ scripts/test_operations.py | 8 + 55 files changed, 902 insertions(+), 350 deletions(-) create mode 100644 PATCH_BASELINE.txt create mode 100644 PATCH_FILE_LIST.txt create mode 100644 PATCH_MANIFEST.sha256 create mode 100644 PATCH_RELEASE_NOTES.md create mode 100644 documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml index c6f251e..5e9150b 100644 --- a/.github/ISSUE_TEMPLATE/question.yml +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -10,7 +10,7 @@ body: id: version attributes: label: MailStack version - placeholder: "1.3.0-rc.1" + placeholder: "1.3.0-rc.2" validations: required: true - type: dropdown diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2065212..a79cca7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,5 +125,5 @@ jobs: - name: Release verification run: | python scripts/verify_release.py \ - dist/mailstack-1.3.0-rc.1-source.zip \ - --checksum dist/mailstack-1.3.0-rc.1-source.zip.sha256 + dist/mailstack-1.3.0-rc.2-source.zip \ + --checksum dist/mailstack-1.3.0-rc.2-source.zip.sha256 diff --git a/CHANGELOG.md b/CHANGELOG.md index 529eeb9..b959891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,24 @@ All notable repository-level changes are recorded here. Application history befo - Preserved `VIBMAIL_*` environment variables, `vibmail-*` service names, `/etc/vibmail` paths, database identifiers, source directories, and legacy deployment contracts. - No application behavior, database migration operations, mail-flow, authentication, authorization, or deployment contract was intentionally changed. Legacy protocol headers such as `X-VibMail-CSRF` remain unchanged. +## 1.3.0-rc.2 — 2026-08-17 + +### Fixed + +- Prevented the installer from changing the host-wide `/var/log` mode and added explicit least-privilege log-path checks. +- Isolated installer-launched Django management commands from stale parent-shell database and Django environment variables. +- Prepared the mailbox provisioning runtime lock directory before clean/repair bootstrap commands. +- Made reviewed repair resumable with explicit idempotent initial-administrator and system-mailbox preservation while retaining strict duplicate rejection by default. +- Persisted root-only initial administrator credentials immediately after administrator creation so later installer failures do not lose the generated password. +- Fixed Postfix-to-Dovecot LMTP delivery by configuring the static userdb with `allow_all_users=yes` while preserving Postfix SQL recipient validation. +- Allowed official one-shot dry-run ingestion verification to run beside the live ingestion worker without taking the exclusive worker lock or mutating ServiceHeartbeat state. +- Qualified the two conservative MariaDB uniqueness warnings against the existing `utf8mb4_unicode_ci` and unique-column deployment contract without changing schema or migrations. + +### Operations + +- Added SSH session-resilience guidance and a non-blocking installer warning when a mutating run starts outside `tmux`/`screen`. +- Synchronized the live-staging fixes back into the canonical source so fresh deployments require no manual copies of the acceptance hotfixes. + ## 1.3.0-rc.1 — 2026-06-30 ### Preserved diff --git a/CITATION.cff b/CITATION.cff index b3c3021..3b30a30 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ cff-version: 1.2.0 message: "If you use MailStack, cite the software release and link to the canonical source repository." title: "MailStack" type: software -version: "1.3.0-rc.1" -date-released: 2026-06-30 +version: "1.3.0-rc.2" +date-released: 2026-08-17 authors: - name: "Vib Tools" repository-code: "https://github.com/vibtools/MailStack" diff --git a/PATCH_BASELINE.txt b/PATCH_BASELINE.txt new file mode 100644 index 0000000..3506bf3 --- /dev/null +++ b/PATCH_BASELINE.txt @@ -0,0 +1,11 @@ +MailStack Replace-Ready Delta Patch Baseline +=========================================== +Baseline archive: Baseline_MailStack(1).zip +Baseline archive SHA256: 78aebfa70fae8e1f70d1c7f32876d35171d1e0030445e7b7e9ff7228ac51cfce +Baseline Git commit: edb11c198b7b21a6765a2f3f8fbd0f997d6b07b1 +Baseline version: 1.3.0-rc.1 +Target version: 1.3.0-rc.2 +Phase: PHASE-003 — Installation, Recovery & Inbound Delivery Reliability Hardening +Candidate local commit: ff2ab30a7a95e65af87b36bbf48a64210e211b4d +Deleted source files: none +Changed/new source files: 51 diff --git a/PATCH_FILE_LIST.txt b/PATCH_FILE_LIST.txt new file mode 100644 index 0000000..468b656 --- /dev/null +++ b/PATCH_FILE_LIST.txt @@ -0,0 +1,51 @@ +.github/ISSUE_TEMPLATE/question.yml +.github/workflows/ci.yml +CHANGELOG.md +CITATION.cff +README.md +ROADMAP.md +SECURITY.md +VERSION +deployment/templates/dovecot/99-vibmail.conf.tpl +design/DESIGN_MANIFEST.json +docs/BUILD.md +docs/FORENSIC_AUDIT_REPORT.md +docs/FORENSIC_FILE_INVENTORY.json +docs/INSTALLATION.md +docs/OPERATIONS.md +docs/PUBLISHING.md +docs/QUICKSTART.md +docs/RELEASE_NOTES_1.3.0.md +docs/RELEASE_PROCESS.md +docs/TEST_REPORT.md +docs/TROUBLESHOOTING.md +documents/ADMIN_GUIDE.md +documents/BASELINE.md +documents/DOCUMENTATION_MANIFEST.json +documents/DOCUMENTATION_POLICY.md +documents/HOW_TO_USE.md +documents/README.md +documents/USER_MANUAL.md +documents/design/ACCESSIBILITY_SPECIFICATION.md +documents/design/COMPONENT_MATRIX.md +documents/design/FUTURE_UI_ROADMAP.md +documents/design/IMPLEMENTATION_STATUS.md +documents/design/RESPONSIVE_SPECIFICATION.md +documents/design/SCREEN_CATALOG.md +documents/design/UI_FOUNDATION.md +documents/phases/PHASE-000-BASELINE.md +documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md +documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md +documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md +install.sh +mailbox-app/CHANGELOG.md +mailbox-app/apps/accounts/management/commands/create_initial_admin.py +mailbox-app/apps/ingestion/management/commands/ingest_maildir.py +mailbox-app/apps/ingestion/service.py +mailbox-app/apps/mailboxes/management/commands/create_system_mailbox.py +mailbox-app/config/settings/production.py +mailbox-app/pyproject.toml +mailbox-app/tests/integration/test_commands_health_postfix.py +mailbox-app/tests/security/test_deployment_assets.py +scripts/test_installer.py +scripts/test_operations.py diff --git a/PATCH_MANIFEST.sha256 b/PATCH_MANIFEST.sha256 new file mode 100644 index 0000000..b209db6 --- /dev/null +++ b/PATCH_MANIFEST.sha256 @@ -0,0 +1,54 @@ +ef4c844680601fd26b1c13550ed9e7b64634f00a0c997a05f31e1bfced5c3b1c .github/ISSUE_TEMPLATE/question.yml +8938f7c5100d9aa2cdb022c93cff2e3c9cd2521f98ca1e36ba15013529955969 .github/workflows/ci.yml +28b4d4f3b535a5805deea800e85c07c811e618e1d936e61b39aaf9386a777f21 CHANGELOG.md +04c310da50d9e7f90dd773a7e20868131c04a32e49adbd667ea8987eb8c430f5 CITATION.cff +8ae650c2d48f3d9cf5df8f1eafa3e5bdf2869c628c11406f25858058b0dd9ead PATCH_BASELINE.txt +1dd8af3b422dd3fe4f1a93584631d2765b166545164601a9c74e26f6b9b4c2a4 PATCH_FILE_LIST.txt +0bf98dec2c174cc06317c70e56c9abcfee28ad4e889a3e011c62d1543465cb0e PATCH_RELEASE_NOTES.md +f98884d98deb7c56e6453663ef9e38cd1d7bf32016692cc47245a2a976622237 README.md +d34a6d69c0934af9111861c4a5eb1ec617e7145561c12860dc92359d9fe28837 ROADMAP.md +07cf4bce801bfd01d172269c78c6dc9a65bf183274c9176a3966fbe841f0880d SECURITY.md +e0019333d1ddc7558aa3803b957eed077855274ca7b112a1028172c473584db2 VERSION +9a3386e2fd3a7b066596760005d55dfa4d0f0ab25aaee1548584191d6b91dc28 deployment/templates/dovecot/99-vibmail.conf.tpl +2b3aa59b5279a4cdd2944a174ade9cde00d0ebb7f722fedabc950d1ed6980647 design/DESIGN_MANIFEST.json +3fd43e51482f4bea9f0cde4f9fb360915a7228116c0b4c6bbb299c0d3c221241 docs/BUILD.md +93a748cde90dc3a0c0b08d0f31c20207baafc4235665071e255fb017af90ba19 docs/FORENSIC_AUDIT_REPORT.md +b62bc3941ce9f444ee8cb36dacb8cade0bfeec44969564ff8d9e3b34235ae766 docs/FORENSIC_FILE_INVENTORY.json +c861c9464e076bb9cac0711be70099a7cd03e193e519d03e9f96898100f839f3 docs/INSTALLATION.md +25d347928a3b5ad7884632b19f3221160d4b1dffe867361831e126cd88eae660 docs/OPERATIONS.md +2ba14ec0d8842e8a2551d7e962c8e82445b1737c3773ce4033f3ee2441c70c83 docs/PUBLISHING.md +7fe067162d4b6db47e5e35ff841fa6acc577e97a51abfe9dc1e49f535e286d55 docs/QUICKSTART.md +e535f31438a58973f619166d79c94b2f1fc9ceb2f07471a99ce469716d2da8ee docs/RELEASE_NOTES_1.3.0.md +174f022988e4c49dca9c56df58aeb9cd65109966d53467954347ba332b1d9145 docs/RELEASE_PROCESS.md +b6614e4f8edccefdd2a3f7520bd2120822b780a35ac3bd95cf2a6c290f41ce2e docs/TEST_REPORT.md +7ff9f0a8afcd5348b72b7a37b7af51f0859f5ec8c920cc5721c8f5b3f24b21dc docs/TROUBLESHOOTING.md +ccc958123954f49d630b65022b66e54e915450a8d23e3d7374688a2a684b5dbb documents/ADMIN_GUIDE.md +ac3b47c424c89d0362ec9f241c81ad2307be9f5049312b8c9e79c5f8948bbee8 documents/BASELINE.md +75b5a206ca0cf3b76eed2351b553984e076d33549c0fdbe98e41307d69764f44 documents/DOCUMENTATION_MANIFEST.json +bf7d3a50a4908143bf6b79d158872e364dab1966c25a171b2dc463cd94e0ee71 documents/DOCUMENTATION_POLICY.md +74e1ba6d9794773d53fdc101e249f1cf6358571be037a53283c62cde95a23522 documents/HOW_TO_USE.md +277026a111f098ea780a005d767dd8427953bc90a6ca887a87314f576f4c0ede documents/README.md +629af8a82133856a9d100856992b43be4ce2d185c387b06c7f300e0ad008aa0c documents/USER_MANUAL.md +a09c0a02ea09c5670479d6f8435462cc40a39e6151a21e4458874831d9934bd0 documents/design/ACCESSIBILITY_SPECIFICATION.md +403c550dea587811ccd333bf5991adde6e8d1578811d95a7db56184816463672 documents/design/COMPONENT_MATRIX.md +562e0c4982f598776a353a34b9e6f2108fe428a97946b882a0cce0e647310a5e documents/design/FUTURE_UI_ROADMAP.md +928640f9b01083968f0db0d03c0f2d8d4e734b43c7ab6357509a3c92a5ae4a41 documents/design/IMPLEMENTATION_STATUS.md +df937d87f53f2ea9fb3a8927e35263c4ecc3dbfd4de9e43a4f5dc61c984d6e15 documents/design/RESPONSIVE_SPECIFICATION.md +895a17622c828fe240b8c8cdbd318a306563251c40fd9b031ecc8902d6eb4c82 documents/design/SCREEN_CATALOG.md +b8bab951b3d5ae666d35ef86653e32682c1e8dc7450b49f0f95e05e0f72766c9 documents/design/UI_FOUNDATION.md +1ee7772711cd7c2d7ba9c5f4188a31f093c800e6c25d869e9f0f544368e0b50e documents/phases/PHASE-000-BASELINE.md +7e0e2a0539cfc3215cae7aeb9bb93fe1b249bcd16155ef2260dc85e4058c4c6e documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md +86f152eed2a86867667ee25ed82ea1293c36ae91af11a7c49b562576339ff4ec documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md +a294f0cc03875fa95ba88feac9f460dd41e714fe2f06503f18365edef0e637ca documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md +f7db90e856ba9ef5598cdc248e24011a57f02c94d4bdb78ef2a42eadb6e71c31 install.sh +30b80318b9c7df92a597d56e4699816d5c974768c06b1443613dcd1dd20c85b5 mailbox-app/CHANGELOG.md +cd9e62ad4a57e567135dc22820fd6346b2908b7d4bc4d569c9ca567b6fc2063c mailbox-app/apps/accounts/management/commands/create_initial_admin.py +18f76e00e8a2f99810387030ad5af39f4214350fe7af2164da28f6a905816ae6 mailbox-app/apps/ingestion/management/commands/ingest_maildir.py +950050c62467b905dec31d0c303da7d89abd3179032a72d947da4a8ab2ca8250 mailbox-app/apps/ingestion/service.py +703988125d67eab971a5c4e5a6ef966f0e003d8f5af22e47e5ff26ac1cc1981d mailbox-app/apps/mailboxes/management/commands/create_system_mailbox.py +320db55b2db817c770cea4fcce2710b4f7820af89266bea3bdcb37d9d46f06f6 mailbox-app/config/settings/production.py +f08792aae7eb9efabe59112ea8707353f781cf691aefba3a5b52b46d72df4353 mailbox-app/pyproject.toml +ac21742565d672b152e883bf64f14119569470a5a06abd7ead55d81e99825fc9 mailbox-app/tests/integration/test_commands_health_postfix.py +79f339a39ff17d98f388430bdb2932393876ff999b86e511d53f647fa5dd7ed0 mailbox-app/tests/security/test_deployment_assets.py +a1aa7a67b450f99828cef66847a7d73769b77aea8993a6995418b80c2433c2d6 scripts/test_installer.py +10185ee3d8fb2a805c9b04787f7326dcd5c65db86a9965ed7fcbb3aa3d23ae23 scripts/test_operations.py diff --git a/PATCH_RELEASE_NOTES.md b/PATCH_RELEASE_NOTES.md new file mode 100644 index 0000000..1746534 --- /dev/null +++ b/PATCH_RELEASE_NOTES.md @@ -0,0 +1,26 @@ +# MailStack 1.3.0-rc.2 PHASE-003 Delta Patch + +This patch is valid only against the frozen `1.3.0-rc.1` baseline at commit +`edb11c198b7b21a6765a2f3f8fbd0f997d6b07b1` / baseline ZIP SHA-256 +`78aebfa70fae8e1f70d1c7f32876d35171d1e0030445e7b7e9ff7228ac51cfce`. + +## In-scope fixes + +1. Preserve host-wide `/var/log` permissions and validate the dedicated MailStack log path. +2. Isolate installer-launched Django commands from stale parent-shell environment variables. +3. Prepare the mailbox provisioning runtime lock directory before bootstrap. +4. Make reviewed partial-install repair idempotent for valid bootstrap objects. +5. Persist newly created initial administrator credentials before later installer phases. +6. Fix Dovecot static-userdb LMTP delivery with `allow_all_users=yes` while retaining Postfix recipient validation. +7. Make one-shot dry-run ingestion verification live-worker compatible and non-mutating. +8. Add SSH/PuTTY resilient-session guidance and installer warning. +9. Qualify the known MariaDB/Django uniqueness warnings without a schema migration. +10. Synchronize the staging hotfixes into the canonical source and bump to `1.3.0-rc.2`. + +## Scope preservation + +No UI page, route, authorization model, application feature, outbound mail function, database migration, dependency, service name, or legacy runtime identifier is intentionally changed. No source file is deleted. + +## Apply + +Verify the baseline first, then overlay the source paths from this ZIP onto the repository root. The four `PATCH_*` files are package metadata, not canonical source files; remove them from the repository working tree before committing if the ZIP was extracted directly into the repository root. Run the documented verification gates before deployment. diff --git a/README.md b/README.md index e02d27c..f9f497e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ It combines **Postfix**, **Dovecot LMTP**, **Django**, **MariaDB**, **Maildir**, **Gunicorn**, and **Nginx** in a hardened single-node reference deployment. MailStack provides a private browser-based inbox, administrator-managed users, object-level mailbox access, safe email rendering, protected attachments, live inbox updates, operational audit logs, backup and restore tooling, a public website, and an isolated contact service. -> **Release status:** `v1.3.0-rc.1` remains a release candidate. Source, test, security, template, and release tooling are present, but clean Ubuntu 24.04 VPS acceptance and real external SMTP/LMTP verification are required before stable promotion. +> **Release status:** `v1.3.0-rc.2` remains a release candidate. Clean Ubuntu 24.04 installation and real external SMTP/LMTP delivery have been exercised during the PHASE-003 hardening campaign; backup/restore, restart-recovery, legal, and final release-owner acceptance still gate stable promotion. ## Why MailStack @@ -211,8 +211,8 @@ Build and verify a deterministic source release: ```bash python scripts/build_release.py --root . python scripts/verify_release.py \ - dist/mailstack-1.3.0-rc.1-source.zip \ - --checksum dist/mailstack-1.3.0-rc.1-source.zip.sha256 + dist/mailstack-1.3.0-rc.2-source.zip \ + --checksum dist/mailstack-1.3.0-rc.2-source.zip.sha256 ``` ## Security diff --git a/ROADMAP.md b/ROADMAP.md index 5e75c2d..5c4dcb5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -26,6 +26,11 @@ This roadmap is directional. Security, data integrity and backward compatibility - Keep planned and future-review screens inactive until architecture and security phases approve them - Require responsive, accessibility, security, regression, documentation and CI evidence for each page +## 1.3 RC hardening + +- PHASE-003 qualifies clean-install, reviewed partial-install repair, live-safe verification, and real external SMTP-to-LMTP delivery fixes discovered during Ubuntu 24.04 staging acceptance. +- Keep 1.3.0-rc.2 as a release candidate until backup/restore, restart-recovery, final security/legal, and release-owner acceptance are complete. + ## 1.3 stable - Promote the verified release candidate without feature removal diff --git a/SECURITY.md b/SECURITY.md index 78d3f0e..4b85402 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,8 @@ Security fixes are applied to the latest tagged release candidate or stable rele | Version | Support status | |---|---| -| 1.3.0-rc.1 | Supported release candidate | +| 1.3.0-rc.2 | Supported release candidate | +| 1.3.0-rc.1 | Superseded release candidate | | 1.2.x | Legacy deployment assets retained; security support is best-effort | | Older versions | Unsupported | diff --git a/VERSION b/VERSION index bf16dde..1d074f4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0-rc.1 +1.3.0-rc.2 diff --git a/deployment/templates/dovecot/99-vibmail.conf.tpl b/deployment/templates/dovecot/99-vibmail.conf.tpl index 2b2c4f6..1945a7b 100644 --- a/deployment/templates/dovecot/99-vibmail.conf.tpl +++ b/deployment/templates/dovecot/99-vibmail.conf.tpl @@ -11,7 +11,7 @@ ssl_key = git push -u origin main @@ -36,14 +36,14 @@ git push -u origin main Create an annotated release-candidate tag only after CI passes: ```bash -git tag -a v1.3.0-rc.1 -m "MailStack 1.3.0 RC1" -git push origin v1.3.0-rc.1 +git tag -a v1.3.0-rc.2 -m "MailStack 1.3.0 RC2" +git push origin v1.3.0-rc.2 ``` Attach these files to the forge release: -- `mailstack-1.3.0-rc.1-source.zip` -- `mailstack-1.3.0-rc.1-source.zip.sha256` +- `mailstack-1.3.0-rc.2-source.zip` +- `mailstack-1.3.0-rc.2-source.zip.sha256` - `docs/RELEASE_NOTES_1.3.0.md` - `docs/FORENSIC_AUDIT_REPORT.md` diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 15fb371..e68e8fe 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -24,12 +24,20 @@ The automated installer targets Ubuntu Server 24.04 LTS. It deliberately refuses ## 4. Install +For an SSH/PuTTY session, create a resilient terminal first: + +```bash +tmux new -s mailstack-install +``` + +Run the installer inside it: + ```bash sudo ./install.sh --domain example.com --admin-email admin@example.com \ --server-ip 203.0.113.10 --non-interactive ``` -The initial administrator credential is written to a root-only file. Sign in, store the password in an approved password manager, change it immediately, then securely delete the file. +The initial administrator credential is written to a root-only file immediately after the account is created. Sign in, store the password in an approved password manager, change it immediately, then securely delete the file. A reviewed `--repair` preserves an already-valid administrator instead of resetting its password. ## 5. Verify external delivery diff --git a/docs/RELEASE_NOTES_1.3.0.md b/docs/RELEASE_NOTES_1.3.0.md index f49fd99..3cc85e0 100644 --- a/docs/RELEASE_NOTES_1.3.0.md +++ b/docs/RELEASE_NOTES_1.3.0.md @@ -1,51 +1,66 @@ -# MailStack 1.3.0 RC1 release notes +# MailStack 1.3.0 RC2 release notes ## Purpose -Version 1.3.0 RC1 converts the verified private deployment source into a configurable, publicly documented, reproducibly packaged open-source release candidate while preserving the approved v1.2.1 mailbox application behavior. - -## Added - -- Ubuntu 24.04 one-command installer -- configurable mail, application, mail-server, and public hostnames -- MariaDB/Postfix/Dovecot/Nginx/systemd/environment templates -- public-site rendering for the configured domain -- complete AGPL-3.0 license text, licensing rationale and public governance/security documents -- Django 5.2.16 LTS security maintenance pin with exact-version deployment and CI verification -- GitHub CI, release workflow, Dependabot, CODEOWNERS, issue and pull-request templates -- source secret scanning, documentation validation, forensic file/symbol inventory, template validation and installer/operations contract tests -- SEO-oriented repository metadata, reusable project logo, deterministic source ZIP, manifest, checksum and verifier -- custom-domain migration and tests -- consistent configurable backup/restore tooling - -## Hardened - -- strict production setting validation -- SQL identifier validation and least-privilege column grants -- Postfix invoker-view privileges -- LMTP-only Dovecot service identity -- hostname collision and installer argument validation -- HSTS policy consistency -- backup consistency, archive validation, and service-state restoration -- release scanner self-protection and generated-artifact rejection +Version 1.3.0 RC2 is a reliability-hardening release candidate built from the frozen 1.3.0 RC1 +baseline. It incorporates only the installation, partial-recovery, official-verification, MariaDB +qualification, and inbound SMTP/LMTP fixes reproduced during the first live Ubuntu 24.04 staging +acceptance campaign. Existing MailStack product features, UI/UX, routes, data model, receive-only +scope, and deployment identifiers remain preserved. + +## Fixed in RC2 + +- The installer no longer changes the host-wide `/var/log` mode; it validates the dedicated MailStack + log path from the `vmail` runtime instead. +- Installer-launched Django commands now run from a clean least-privilege environment instead of + inheriting stale database/Django variables from the parent shell. +- `/run/vibmail/mailbox-provision-locks` is prepared before bootstrap mailbox creation. +- Reviewed `--repair` can preserve a valid existing initial administrator and valid system mailboxes + while creating only missing bootstrap objects; inconsistent partial state fails closed. +- Newly created initial-administrator credentials are persisted immediately to the root-only + credential file, before later TLS/mail-stack phases can fail. +- Dovecot's static LMTP userdb includes `allow_all_users=yes`; Postfix SQL virtual-mailbox lookup + remains the authoritative recipient gate, so unknown recipients are still rejected. +- One-shot `ingest_maildir --dry-run` verification no longer takes the live worker's exclusive lock + and no longer updates `ServiceHeartbeat`, allowing the official application verifier to run beside + the live ingestion service. +- Conservative Django MariaDB uniqueness warnings are qualified only in production against the + existing `utf8mb4_unicode_ci` database and unique-column deployment contract; no migration or + schema change is introduced. +- SSH/PuTTY installation guidance now recommends `tmux`/`screen`, with a non-blocking warning when a + mutating installer is launched over SSH outside a resilient terminal. ## Compatibility -- Existing `vibmail.my` defaults, application data model, URLs, templates, static assets, tests, migration history, and legacy maintenance assets are retained. -- No functional baseline source file was deleted. -- Existing v1.2 backup sets without contact-state archives remain restorable by the updated restore script. +- No database migration. +- No dependency upgrade or new dependency. +- No route, template, CSS, JavaScript, UI page, authorization, mailbox, message, public-site, or + contact-workflow redesign. +- No SMTP submission, IMAP, POP3, reply, forward, sent, draft, or public-registration feature. +- Existing `VIBMAIL_*` settings, `vibmail-*` services, `/etc/vibmail` paths, databases, Maildir + layout, and receive-only architecture remain unchanged. +- Strict duplicate rejection remains the default for bootstrap management commands; preservation is + available only through the explicit repair option. -## Release qualification +## Staging evidence that motivated RC2 -Repository CI, the online dependency audit, deterministic release build, release verification and clean-clone verification pass at commit `1e1737edea2e6c922265a15d8584b56671820c65`. The release remains an **RC** until a complete clean Ubuntu 24.04 live installation, inbound SMTP/LMTP, backup/restore and operational acceptance campaign passes. +The live campaign reproduced and isolated failures in `/var/log` traversal, inherited installer +credentials, mailbox provisioning runtime locks, partial bootstrap recovery, delayed initial +credential persistence, Dovecot static-userdb LMTP lookup, and live verification locking. After the +LMTP correction, deferred Gmail messages were accepted by Dovecot, saved to INBOX, removed from the +Postfix queue, ingested, and displayed by the application. + +## Release qualification -## Documentation baseline +RC2 must pass the full documentation, installer, operations, template, Ruff, Bandit, Django, +coverage, forensic, deterministic-release, and CI gates. Clean Ubuntu 24.04 installation and real +external SMTP/LMTP acceptance are required. Stable promotion remains blocked until backup/restore, +restart-recovery, final security/legal, and release-owner acceptance are complete. -MailStack also preserves the complete UI reference archive as `MAILSTACK-UI-DESIGN-INTAKE-001` and freezes -`MAILSTACK-UI-FOUNDATION-001`. The intake adds no runtime feature; it provides immutable design assets, -screen classification, future-scope boundaries, and automated integrity gates for later page-by-page redesign. +## RC1 foundation preserved -MailStack now includes a root `documents/` user-documentation hub with a complete user manual, -task-based how-to guide, administrator guide, protected baseline record, mandatory per-phase -records, deterministic index and manifest generation, documentation contract tests, and CI -change-policy enforcement. This framework does not change runtime application behavior. +RC1 established the configurable Ubuntu 24.04 installer, MariaDB/Postfix/Dovecot/Nginx/systemd +templates, reproducible source packaging, public governance/security documentation, Django 5.2.16 +security pin, protected user/documentation baseline, UI design intake, and shared application shell. +RC2 does not replace or redesign those foundations; it hardens the operational paths exercised by +staging acceptance. diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md index 41fb961..edd4079 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/RELEASE_PROCESS.md @@ -7,7 +7,7 @@ 5. Run the online dependency audit in CI. 6. Test a clean installation on an isolated Ubuntu Server 24.04 LTS VPS. 7. Verify external inbound SMTP, login, authorization boundaries, live updates, safe HTML, and attachment downloads. -8. Build with `python scripts/build_release.py --root . --version 1.3.0-rc.1`. +8. Build with `python scripts/build_release.py --root . --version 1.3.0-rc.2`. 9. Verify the ZIP and checksum with `python scripts/verify_release.py`. 10. Publish the source ZIP, checksum, release notes, user-document manifest, and exact Git commit/tag. diff --git a/docs/TEST_REPORT.md b/docs/TEST_REPORT.md index 15bf79e..e785991 100644 --- a/docs/TEST_REPORT.md +++ b/docs/TEST_REPORT.md @@ -1,6 +1,6 @@ -# Test report — MailStack 1.3.0 RC1 +# Test report — MailStack 1.3.0 RC2 -**Baseline verification date:** 2026-07-25 +**PHASE-003 verification date:** 2026-08-17 ## Application suite @@ -55,6 +55,26 @@ GitHub Actions run `30133728843` completed successfully on Ubuntu 24.04 with Pyt The network-enabled blocking `pip-audit` gate passed in GitHub Actions after Django was upgraded to 5.2.16. Dependency changes remain subject to `pip check`, the locked requirements contract and the blocking online advisory gate. +## PHASE-003 installation/recovery reliability qualification + +Dependency-free local qualification for 1.3.0-rc.2 passes the documentation index/manifest gate, +documentation contract tests, design manifest and PNG integrity tests, shared UI foundation +contracts, deployment-template rendering, installer contracts, operations contracts, Python compile, +shell syntax, forensic inventory generation/check, and the structural forensic audit. The installer +contract specifically protects global `/var/log`, sanitized `vmail` command execution, provisioning +runtime directories, early credential persistence, explicit repair idempotency, the Dovecot static +userdb LMTP setting, and the existing MariaDB collation qualification. + +The current artifact-building environment does not contain the repository's pinned Django/Ruff/Bandit +dependencies and cannot download them, so the dependency-backed Django/coverage/lint/full-forensic +gates are delegated to the mandatory Ubuntu 24.04 GitHub Actions run for the PHASE-003 commit. This +does not waive those gates. + +The live staging campaign that motivated PHASE-003 demonstrated real external Gmail delivery through +Postfix and Dovecot LMTP into Maildir, queue drain after the static-userdb correction, ingestion, and +web-inbox visibility. A final clean installation from the exact RC2 source remains a release-candidate +acceptance requirement before stable promotion. + ## Manual acceptance still required On an isolated Ubuntu 24.04 VPS, verify installation, TLS issuance/renewal, external SMTP reception, Postfix lookup rejection for unknown/disabled recipients, Dovecot LMTP delivery, Maildir ingestion, login and authorization isolation, live updates, safe HTML, attachment downloads, contact delivery, backup, restore, and restart recovery. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 31af754..f14c6b0 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -10,11 +10,13 @@ Confirm public DNS resolves to the server, ports 80/443 are reachable, no confli ## External mail is not received -Check provider port-25 policy, firewall rules, MX records, Postfix logs and lookup results. Verify the mailbox is active and unknown recipients are rejected. +Check provider port-25 policy, firewall rules, MX records, Postfix logs and lookup results. Verify the mailbox is active and unknown recipients are rejected. If Postfix accepts the recipient but defers LMTP with `451 4.3.0` and Dovecot reports that a static userdb cannot verify user existence, verify the deployed Dovecot static userdb contains `allow_all_users=yes`; Postfix SQL lookup remains the recipient-validation boundary. + +If a message is already deferred after correcting the LMTP configuration, validate Dovecot first and then retry the queue with `postqueue -f`; do not delete queued mail as a first response. ## Mail reaches Maildir but not the web inbox -Check `vibmail-ingestion`, the configured Maildir path, filesystem ownership, ingestion lock and application logs. Run the mailbox-storage and schema verification commands. +Check `vibmail-ingestion`, the configured Maildir path, filesystem ownership, ingestion lock and application logs. Run the mailbox-storage and schema verification commands. In 1.3.0-rc.2 the official one-shot dry-run verifier can run while the live ingestion worker remains active; a real second ingestion worker must still be rejected by the exclusive lock. ## Login works but mailbox is empty @@ -29,3 +31,12 @@ Check `vibmail-public-contact`, local Postfix, the protected contact environment Do not bypass archive or checksum validation. Confirm the backup set is complete, the target paths are safe and the documented service-state procedure was followed. See `mailbox-app/docs/TROUBLESHOOTING.md` for application-specific checks. + + +## Installer fails during Django startup with `/var/log/vibmail` permission denied + +MailStack 1.3.0-rc.2 does not change the host-wide `/var/log` mode. Confirm `/var/log` is a real traversable system directory and `/var/log/vibmail` is owned by the `vmail` runtime with its expected restricted mode. Do not make `/var/log` globally restrictive as an application-specific hardening step. + +## Repair reports inconsistent bootstrap state + +Do not delete an existing administrator or mail-server row to force repair through. `--repair` intentionally preserves complete objects but rejects mixed application/mail-server/filesystem state for system mailboxes and rejects a requested administrator name that does not match an existing valid administrator. Inspect the partial state and restore from the installer backup or a verified application backup as appropriate. diff --git a/documents/ADMIN_GUIDE.md b/documents/ADMIN_GUIDE.md index d4c26d8..e556f0c 100644 --- a/documents/ADMIN_GUIDE.md +++ b/documents/ADMIN_GUIDE.md @@ -4,8 +4,8 @@ title: MailStack Administrator Guide document_type: admin-guide audience: mailstack-administrators status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack administrator guide @@ -44,7 +44,8 @@ mailboxes. Message and mailbox deletion controls remain hidden unless policy all The administrator dashboard reports ingestion heartbeat, mail-storage accessibility, and database connectivity. Server-side verification should also use the maintained health and verification -scripts, systemd service status, and journal logs. After deployment, verify external delivery to +scripts, systemd service status, and journal logs. The one-shot dry-run verifier can run while the +live ingestion worker remains active and does not update its heartbeat. After deployment, verify external delivery to `postmaster@DOMAIN` and `abuse@DOMAIN`, Maildir creation, ingestion, browser visibility, and backup creation before onboarding users. @@ -62,4 +63,6 @@ attachments, database dumps, backups, certificates, private keys, or unsanitized Before production use, complete clean Ubuntu 24.04 VPS acceptance, TLS and DNS validation, real SMTP/LMTP delivery tests, backup/restore acceptance, and security review. Use the documented repair -or upgrade path rather than running the clean installer over an unreviewed existing mail stack. +or upgrade path rather than running the clean installer over an unreviewed existing mail stack. A +reviewed repair preserves an existing valid administrator and bootstrap mailboxes, creates only +missing bootstrap objects, and never silently resets an administrator password. diff --git a/documents/BASELINE.md b/documents/BASELINE.md index 9a6dd67..75e5ebe 100644 --- a/documents/BASELINE.md +++ b/documents/BASELINE.md @@ -4,8 +4,8 @@ title: MailStack Baseline document_type: baseline audience: maintainers-and-operators status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-24 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 baseline_id: MAILSTACK-1.3.0-RC1-DOCS-BASELINE-001 source_commit: 1e1737edea2e6c922265a15d8584b56671820c65 --- diff --git a/documents/DOCUMENTATION_MANIFEST.json b/documents/DOCUMENTATION_MANIFEST.json index b07fef3..40e87a3 100644 --- a/documents/DOCUMENTATION_MANIFEST.json +++ b/documents/DOCUMENTATION_MANIFEST.json @@ -6,192 +6,204 @@ "audience": "mailstack-administrators", "document_id": "admin-guide", "document_type": "admin-guide", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/ADMIN_GUIDE.md", "phase_id": null, - "sha256": "c14f1b2a52ceef69ce32b32ff56cbb71d003a71244d454c9d2e52f77b8e0c69a", + "sha256": "ccc958123954f49d630b65022b66e54e915450a8d23e3d7374688a2a684b5dbb", "status": "active", "title": "MailStack Administrator Guide", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "maintainers-and-operators", "document_id": "baseline", "document_type": "baseline", - "last_reviewed": "2026-07-24", + "last_reviewed": "2026-08-17", "path": "documents/BASELINE.md", "phase_id": null, - "sha256": "f17acc52e17a87a545bb38dbd171ee3462491506ceb6412b9a98e86257d7d1fe", + "sha256": "ac3b47c424c89d0362ec9f241c81ad2307be9f5049312b8c9e79c5f8948bbee8", "status": "active", "title": "MailStack Baseline", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "contributors-and-maintainers", "document_id": "documentation-policy", "document_type": "documentation-policy", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/DOCUMENTATION_POLICY.md", "phase_id": null, - "sha256": "1d8eacd99557546403054eb139b7fe89e53c59d90d07db018b8bce6db9d735ae", + "sha256": "bf7d3a50a4908143bf6b79d158872e364dab1966c25a171b2dc463cd94e0ee71", "status": "active", "title": "Documentation Policy", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "mailbox-users-and-administrators", "document_id": "how-to-use", "document_type": "how-to", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/HOW_TO_USE.md", "phase_id": null, - "sha256": "1e83c10dfd06d22dd269141a2c0dfd8b82644981719ec4471bf6c50a20f22686", + "sha256": "74e1ba6d9794773d53fdc101e249f1cf6358571be037a53283c62cde95a23522", "status": "active", "title": "How to Use MailStack", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "mailbox-users", "document_id": "user-manual", "document_type": "user-manual", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/USER_MANUAL.md", "phase_id": null, - "sha256": "c52aee69746a25bb47e8bb8f9b8d5a01cff2230c8c4877c4a807126078f75ba7", + "sha256": "629af8a82133856a9d100856992b43be4ce2d185c387b06c7f300e0ad008aa0c", "status": "active", "title": "MailStack User Manual", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "ui-accessibility-specification", "document_type": "design-reference", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/design/ACCESSIBILITY_SPECIFICATION.md", "phase_id": null, - "sha256": "fe3760b78db81e862744c46fe9dd42c8b323108224fa1bb7c35e2683f0aad38b", + "sha256": "a09c0a02ea09c5670479d6f8435462cc40a39e6151a21e4458874831d9934bd0", "status": "active", "title": "MailStack Accessibility Specification", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "ui-component-matrix", "document_type": "design-reference", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/design/COMPONENT_MATRIX.md", "phase_id": null, - "sha256": "9f0ce0a538eb632426ead8e2f306ea73725fe9a2bb4a5ba6b7e82c34a1265b2b", + "sha256": "403c550dea587811ccd333bf5991adde6e8d1578811d95a7db56184816463672", "status": "active", "title": "MailStack UI Component Matrix", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "future-ui-roadmap", "document_type": "design-reference", - "last_reviewed": "2026-07-24", + "last_reviewed": "2026-08-17", "path": "documents/design/FUTURE_UI_ROADMAP.md", "phase_id": null, - "sha256": "364119f5df14e66347699e7bda9b22b964a55f4c37659dd06c1c0db0d6fde04b", + "sha256": "562e0c4982f598776a353a34b9e6f2108fe428a97946b882a0cce0e647310a5e", "status": "active", "title": "MailStack Future UI Roadmap", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "ui-implementation-status", "document_type": "design-reference", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/design/IMPLEMENTATION_STATUS.md", "phase_id": null, - "sha256": "7095a9f650707c58641b6179e0837ff399edb66758addaa6ccbf1577f4d1d81a", + "sha256": "928640f9b01083968f0db0d03c0f2d8d4e734b43c7ab6357509a3c92a5ae4a41", "status": "active", "title": "MailStack UI Implementation Status", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "ui-responsive-specification", "document_type": "design-reference", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/design/RESPONSIVE_SPECIFICATION.md", "phase_id": null, - "sha256": "1bc228db2d45c2e609d9dd10da836f7214519176f415f8b0f3692dc0b5023171", + "sha256": "df937d87f53f2ea9fb3a8927e35263c4ecc3dbfd4de9e43a4f5dc61c984d6e15", "status": "active", "title": "MailStack Responsive Specification", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "ui-screen-catalog", "document_type": "design-reference", - "last_reviewed": "2026-07-24", + "last_reviewed": "2026-08-17", "path": "documents/design/SCREEN_CATALOG.md", "phase_id": null, - "sha256": "7822cd15dea8c3a64cdac2eaabe5c3ad4c5f9c9b9ca8cb42ddd24b2097c9748f", + "sha256": "895a17622c828fe240b8c8cdbd318a306563251c40fd9b031ecc8902d6eb4c82", "status": "active", "title": "MailStack UI Screen Catalog", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "designers-developers-and-maintainers", "document_id": "ui-foundation", "document_type": "design-reference", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/design/UI_FOUNDATION.md", "phase_id": null, - "sha256": "b36905a2fe8f995b053347e2f8b3ceed3bd0d085ffd2670a591e63e8e9c30487", + "sha256": "b8bab951b3d5ae666d35ef86653e32682c1e8dc7450b49f0f95e05e0f72766c9", "status": "active", "title": "MailStack UI Foundation", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "users-operators-and-maintainers", "document_id": "phase-000-baseline", "document_type": "phase", - "last_reviewed": "2026-07-24", + "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-000-BASELINE.md", "phase_id": "PHASE-000", - "sha256": "f6c1872bc32d81f11cf67a1f7bf619eebae7080603d3b7981211e2648dc064c0", + "sha256": "1ee7772711cd7c2d7ba9c5f4188a31f093c800e6c25d869e9f0f544368e0b50e", "status": "active", "title": "Documentation and Feature Baseline", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "users-operators-designers-and-maintainers", "document_id": "phase-001-ui-design-intake-baseline", "document_type": "phase", - "last_reviewed": "2026-07-24", + "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md", "phase_id": "PHASE-001", - "sha256": "f72355ea2266fff91bc4b053a1b11d308e23320664cdc3b2c604ad7e88695220", + "sha256": "7e0e2a0539cfc3215cae7aeb9bb93fe1b249bcd16155ef2260dc85e4058c4c6e", "status": "active", "title": "UI Design Intake Baseline", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" }, { "audience": "users-operators-designers-and-maintainers", "document_id": "phase-002-shared-ui-foundation-and-application-shell", "document_type": "phase", - "last_reviewed": "2026-07-25", + "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md", "phase_id": "PHASE-002", - "sha256": "f4ef536520aaaa30d23b691102ca355a84475eaa23c9b29a8a415e349b3f6ee5", + "sha256": "86f152eed2a86867667ee25ed82ea1293c36ae91af11a7c49b562576339ff4ec", "status": "active", "title": "Shared UI Foundation and Application Shell", - "version": "1.3.0-rc.1" + "version": "1.3.0-rc.2" + }, + { + "audience": "users-operators-and-maintainers", + "document_id": "phase-003-installation-recovery-and-inbound-delivery-reliability", + "document_type": "phase", + "last_reviewed": "2026-08-17", + "path": "documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md", + "phase_id": "PHASE-003", + "sha256": "a294f0cc03875fa95ba88feac9f460dd41e714fe2f06503f18365edef0e637ca", + "status": "active", + "title": "Installation, Recovery and Inbound Delivery Reliability", + "version": "1.3.0-rc.2" } ], "project": "MailStack", - "release_version": "1.3.0-rc.1", + "release_version": "1.3.0-rc.2", "schema_version": 1, "scope": "Managed user, administrator, how-to, policy, baseline, and phase Markdown documents.", "summary": { - "active": 15, - "documents": 15, + "active": 16, + "documents": 16, "draft": 0, "historical": 0, - "phases": 3 + "phases": 4 } } diff --git a/documents/DOCUMENTATION_POLICY.md b/documents/DOCUMENTATION_POLICY.md index bed41a5..c2f8104 100644 --- a/documents/DOCUMENTATION_POLICY.md +++ b/documents/DOCUMENTATION_POLICY.md @@ -4,8 +4,8 @@ title: Documentation Policy document_type: documentation-policy audience: contributors-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack documentation policy diff --git a/documents/HOW_TO_USE.md b/documents/HOW_TO_USE.md index 869281f..7eb8a1e 100644 --- a/documents/HOW_TO_USE.md +++ b/documents/HOW_TO_USE.md @@ -4,8 +4,8 @@ title: How to Use MailStack document_type: how-to audience: mailbox-users-and-administrators status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # How to use MailStack diff --git a/documents/README.md b/documents/README.md index 52c4173..f88775f 100644 --- a/documents/README.md +++ b/documents/README.md @@ -8,7 +8,7 @@ relevant user manual, how-to guide, or administrator guide. ```bash python scripts/manage_documents.py new-phase \ - --phase-id PHASE-003 \ + --phase-id PHASE-004 \ --title "Feature title" \ --summary "What the phase changes for users" @@ -27,21 +27,22 @@ required documentation update. | Document | Type | Audience | Status | Version | |---|---|---|---|---| -| [MailStack Administrator Guide](ADMIN_GUIDE.md) | Admin Guide | mailstack-administrators | active | 1.3.0-rc.1 | -| [MailStack Baseline](BASELINE.md) | Baseline | maintainers-and-operators | active | 1.3.0-rc.1 | -| [MailStack Accessibility Specification](design/ACCESSIBILITY_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack Future UI Roadmap](design/FUTURE_UI_ROADMAP.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack Responsive Specification](design/RESPONSIVE_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack UI Component Matrix](design/COMPONENT_MATRIX.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack UI Foundation](design/UI_FOUNDATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack UI Implementation Status](design/IMPLEMENTATION_STATUS.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack UI Screen Catalog](design/SCREEN_CATALOG.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.1 | -| [Documentation Policy](DOCUMENTATION_POLICY.md) | Documentation Policy | contributors-and-maintainers | active | 1.3.0-rc.1 | -| [How to Use MailStack](HOW_TO_USE.md) | How To | mailbox-users-and-administrators | active | 1.3.0-rc.1 | -| [Documentation and Feature Baseline](phases/PHASE-000-BASELINE.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.1 | -| [Shared UI Foundation and Application Shell](phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.1 | -| [UI Design Intake Baseline](phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.1 | -| [MailStack User Manual](USER_MANUAL.md) | User Manual | mailbox-users | active | 1.3.0-rc.1 | +| [MailStack Administrator Guide](ADMIN_GUIDE.md) | Admin Guide | mailstack-administrators | active | 1.3.0-rc.2 | +| [MailStack Baseline](BASELINE.md) | Baseline | maintainers-and-operators | active | 1.3.0-rc.2 | +| [MailStack Accessibility Specification](design/ACCESSIBILITY_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack Future UI Roadmap](design/FUTURE_UI_ROADMAP.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack Responsive Specification](design/RESPONSIVE_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack UI Component Matrix](design/COMPONENT_MATRIX.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack UI Foundation](design/UI_FOUNDATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack UI Implementation Status](design/IMPLEMENTATION_STATUS.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack UI Screen Catalog](design/SCREEN_CATALOG.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | +| [Documentation Policy](DOCUMENTATION_POLICY.md) | Documentation Policy | contributors-and-maintainers | active | 1.3.0-rc.2 | +| [How to Use MailStack](HOW_TO_USE.md) | How To | mailbox-users-and-administrators | active | 1.3.0-rc.2 | +| [Documentation and Feature Baseline](phases/PHASE-000-BASELINE.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.2 | +| [Installation, Recovery and Inbound Delivery Reliability](phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.2 | +| [Shared UI Foundation and Application Shell](phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.2 | +| [UI Design Intake Baseline](phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.2 | +| [MailStack User Manual](USER_MANUAL.md) | User Manual | mailbox-users | active | 1.3.0-rc.2 | ## Scope boundary diff --git a/documents/USER_MANUAL.md b/documents/USER_MANUAL.md index 55c6f87..b36ed74 100644 --- a/documents/USER_MANUAL.md +++ b/documents/USER_MANUAL.md @@ -4,8 +4,8 @@ title: MailStack User Manual document_type: user-manual audience: mailbox-users status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack user manual diff --git a/documents/design/ACCESSIBILITY_SPECIFICATION.md b/documents/design/ACCESSIBILITY_SPECIFICATION.md index 2e1cd3f..b6f5921 100644 --- a/documents/design/ACCESSIBILITY_SPECIFICATION.md +++ b/documents/design/ACCESSIBILITY_SPECIFICATION.md @@ -4,8 +4,8 @@ title: MailStack Accessibility Specification document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack accessibility specification diff --git a/documents/design/COMPONENT_MATRIX.md b/documents/design/COMPONENT_MATRIX.md index 69079ac..287c3f0 100644 --- a/documents/design/COMPONENT_MATRIX.md +++ b/documents/design/COMPONENT_MATRIX.md @@ -4,8 +4,8 @@ title: MailStack UI Component Matrix document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack UI component matrix diff --git a/documents/design/FUTURE_UI_ROADMAP.md b/documents/design/FUTURE_UI_ROADMAP.md index 71fa8b0..424d4a9 100644 --- a/documents/design/FUTURE_UI_ROADMAP.md +++ b/documents/design/FUTURE_UI_ROADMAP.md @@ -4,8 +4,8 @@ title: MailStack Future UI Roadmap document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-24 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack future UI roadmap diff --git a/documents/design/IMPLEMENTATION_STATUS.md b/documents/design/IMPLEMENTATION_STATUS.md index f6b8182..bc351c9 100644 --- a/documents/design/IMPLEMENTATION_STATUS.md +++ b/documents/design/IMPLEMENTATION_STATUS.md @@ -4,8 +4,8 @@ title: MailStack UI Implementation Status document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack UI implementation status diff --git a/documents/design/RESPONSIVE_SPECIFICATION.md b/documents/design/RESPONSIVE_SPECIFICATION.md index 6a90c5c..d109acb 100644 --- a/documents/design/RESPONSIVE_SPECIFICATION.md +++ b/documents/design/RESPONSIVE_SPECIFICATION.md @@ -4,8 +4,8 @@ title: MailStack Responsive Specification document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack responsive specification diff --git a/documents/design/SCREEN_CATALOG.md b/documents/design/SCREEN_CATALOG.md index d34fccc..5671f22 100644 --- a/documents/design/SCREEN_CATALOG.md +++ b/documents/design/SCREEN_CATALOG.md @@ -4,8 +4,8 @@ title: MailStack UI Screen Catalog document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-24 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack UI screen catalog diff --git a/documents/design/UI_FOUNDATION.md b/documents/design/UI_FOUNDATION.md index 1b2f626..32b4ace 100644 --- a/documents/design/UI_FOUNDATION.md +++ b/documents/design/UI_FOUNDATION.md @@ -4,8 +4,8 @@ title: MailStack UI Foundation document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 --- # MailStack UI foundation diff --git a/documents/phases/PHASE-000-BASELINE.md b/documents/phases/PHASE-000-BASELINE.md index bedf7df..fc4d4a5 100644 --- a/documents/phases/PHASE-000-BASELINE.md +++ b/documents/phases/PHASE-000-BASELINE.md @@ -4,8 +4,8 @@ title: Documentation and Feature Baseline document_type: phase audience: users-operators-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-24 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 phase_id: PHASE-000 --- diff --git a/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md b/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md index 8600ac2..55612dc 100644 --- a/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md +++ b/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md @@ -4,8 +4,8 @@ title: UI Design Intake Baseline document_type: phase audience: users-operators-designers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-24 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 phase_id: PHASE-001 --- diff --git a/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md b/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md index 67f1e20..1008f41 100644 --- a/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md +++ b/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md @@ -4,8 +4,8 @@ title: Shared UI Foundation and Application Shell document_type: phase audience: users-operators-designers-and-maintainers status: active -version: 1.3.0-rc.1 -last_reviewed: 2026-07-25 +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 phase_id: PHASE-002 --- diff --git a/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md b/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md new file mode 100644 index 0000000..630e73c --- /dev/null +++ b/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md @@ -0,0 +1,86 @@ +--- +document_id: phase-003-installation-recovery-and-inbound-delivery-reliability +title: Installation, Recovery and Inbound Delivery Reliability +document_type: phase +audience: users-operators-and-maintainers +status: active +version: 1.3.0-rc.2 +last_reviewed: 2026-08-17 +phase_id: PHASE-003 +--- + +# PHASE-003: Installation, recovery and inbound delivery reliability + +## Objective + +Harden the existing Ubuntu 24.04 installation, partial-install recovery, official verification, and +Postfix-to-Dovecot inbound-delivery path using failures reproduced during the first live staging +acceptance campaign, without changing MailStack product features or UI behavior. + +## Scope + +This phase is restricted to the ten installation and setup issues reproduced during live staging: +global `/var/log` permission mutation, inherited shell-environment contamination, missing mailbox +provisioning runtime directories, non-idempotent partial-install bootstrap recovery, delayed initial +administrator credential persistence, Dovecot static-userdb LMTP lookup failure, the official +verification script conflicting with the live ingestion lock, SSH/PuTTY session-resilience guidance, +qualified MariaDB compatibility warnings, and source/staging hotfix drift. + +No application page, URL, permission model, mailbox/message workflow, public-site feature, outbound +mail capability, database migration, dependency, service name, or legacy runtime identifier is +added, removed, renamed, or redesigned. + +## User-facing changes + +There are no UI changes. Operators receive a safer clean/repair installer, a repair path that can +preserve already-valid bootstrap objects while creating missing ones, earlier root-only persistence +of newly created administrator credentials, reliable LMTP delivery for Postfix-validated recipients, +and an application verifier that can run beside the live ingestion worker. + +## How to use + +Run mutating installation or repair commands inside `tmux` or `screen` when connected through SSH. +Use the normal clean installer only on an unmarked target. After a reviewed interrupted or partial +installation, rerun the same source and parameters with `--repair`; valid existing administrator and +system-mailbox bootstrap objects are preserved and only missing objects are created. The installer +never silently resets an existing administrator password. + +After installation, run `/opt/vibmail/app/scripts/health_check.sh` followed by +`/opt/vibmail/app/scripts/verify_application.sh` while the normal ingestion service remains active. +Complete acceptance with a real external message to an active mailbox and confirm Postfix acceptance, +Dovecot LMTP delivery, Maildir persistence, ingestion, and browser visibility. + +## Compatibility + +All baseline application behavior remains unchanged. The Postfix SQL mailbox table remains the +authoritative recipient gate; Dovecot's static userdb now trusts recipients already validated by +Postfix and therefore does not introduce catch-all delivery. Existing administrator passwords are +preserved during repair. Existing strict duplicate behavior remains the default for management +commands; idempotent preservation is available only through the explicit repair option. + +No database migration is introduced. MariaDB continues to use the configured +`utf8mb4_unicode_ci` application-database collation, and existing unique columns remain unchanged. +The two conservative Django/MariaDB warnings qualified by that deployment contract are silenced only +in production settings. Rollback is the baseline `1.3.0-rc.1` source plus the previously documented +manual staging workarounds; existing data does not require migration or rollback transformation. + +## Verification + +Regression coverage verifies that the installer does not change global `/var/log` permissions, +least-privilege Django commands run from a sanitized environment, runtime provisioning directories +exist before bootstrap, repair preserves valid existing administrator/system mailboxes, initial +credentials are persisted immediately after administrator creation, the Dovecot template includes +the static-userdb LMTP fix, and MariaDB collation/warning qualification remains narrow. + +Django tests verify strict default bootstrap behavior, explicit `--if-missing` recovery, inconsistent +repair-state rejection, exclusive locking for real ingestion, concurrent one-shot dry-run verification, +and zero ServiceHeartbeat mutation during dry-run. Qualification also requires documentation, +template, installer, operations, Ruff, Bandit, Django, forensic, deterministic-release, and full CI +gates plus clean Ubuntu 24.04 external SMTP/LMTP acceptance before stable promotion. + +## Documentation impact + +Updated the root and application changelogs, README release status, installation, quick-start, +operations, troubleshooting, release-process/build/publishing references, security/citation metadata, +roadmap qualification notes, release notes, administrator guidance, versioned managed-document +metadata, generated documentation/design/forensic manifests, and this PHASE-003 record. diff --git a/install.sh b/install.sh index 8340093..81989e8 100755 --- a/install.sh +++ b/install.sh @@ -136,12 +136,33 @@ backup_file() { } run_as_vmail() { - runuser -u vmail --preserve-environment -- env \ + runuser -u vmail -- env -i \ + PATH=/opt/vibmail/venv/bin:/usr/local/bin:/usr/bin:/bin \ + HOME=/var/vmail \ + USER=vmail \ + LOGNAME=vmail \ VIBMAIL_ENV_FILE=/etc/vibmail/vibmail.env \ DJANGO_SETTINGS_MODULE=config.settings.production \ "$@" } +write_initial_credentials() { + local password=$1 + CREDENTIALS_FILE="/root/vibmail-initial-credentials-$STAMP.txt" + cat > "$CREDENTIALS_FILE" <"$LOCK_FILE" flock -n 9 || die "Another MailStack installation is running" touch "$LOG_FILE" @@ -376,6 +400,14 @@ install -d -o vmail -g vmail -m 0750 \ install -d -o vmail -g www-data -m 0755 /var/lib/vibmail/static install -d -o vmail -g vmail -m 0700 /var/lib/vibmail/attachments install -d -o vmail -g adm -m 0750 /var/log/vibmail +install -d -o vmail -g vmail -m 0750 /run/vibmail +install -d -o vmail -g vmail -m 0700 /run/vibmail/mailbox-provision-locks +runuser -u vmail -- test -x /var/log \ + || die "The vmail runtime cannot traverse /var/log; preserve the host directory and correct its parent permissions" +runuser -u vmail -- test -w /var/log/vibmail \ + || die "The vmail runtime cannot write to /var/log/vibmail" +runuser -u vmail -- test -w /run/vibmail/mailbox-provision-locks \ + || die "The vmail runtime cannot write to the mailbox provisioning lock directory" install -d -o root -g www-data -m 0755 /var/www/letsencrypt/.well-known/acme-challenge install -d -o root -g root -m 0755 /opt/vibmail-public-site/releases install -d -o root -g www-data -m 0755 "/var/www/$PUBLIC_HOSTNAME" @@ -478,23 +510,45 @@ find /var/lib/vibmail/static -type d -exec chmod 0755 {} + find /var/lib/vibmail/static -type f -exec chmod 0644 {} + run_as_vmail /opt/vibmail/venv/bin/python /opt/vibmail/app/manage.py check --deploy -if [[ $REPAIR -eq 0 ]]; then - if [[ -n "$ADMIN_PASSWORD_ENV" ]]; then - ADMIN_PASSWORD=${!ADMIN_PASSWORD_ENV:-} - [[ -n "$ADMIN_PASSWORD" ]] || die "Environment variable $ADMIN_PASSWORD_ENV is empty" - else - ADMIN_PASSWORD="Vm!$(random_hex 12)" - fi - export VIBMAIL_INITIAL_ADMIN_PASSWORD="$ADMIN_PASSWORD" +CREDENTIALS_FILE="not-created-existing-admin" +if [[ -n "$ADMIN_PASSWORD_ENV" ]]; then + ADMIN_PASSWORD=${!ADMIN_PASSWORD_ENV:-} + [[ -n "$ADMIN_PASSWORD" ]] || die "Environment variable $ADMIN_PASSWORD_ENV is empty" +else + ADMIN_PASSWORD="Vm!$(random_hex 12)" +fi + +ADMIN_COMMAND_ARGS=( + --username "$ADMIN_USERNAME" + --password-env VIBMAIL_INITIAL_ADMIN_PASSWORD +) +if [[ $REPAIR -eq 1 ]]; then + ADMIN_COMMAND_ARGS+=(--if-missing) +fi +ADMIN_COMMAND_OUTPUT=$( run_as_vmail env VIBMAIL_INITIAL_ADMIN_PASSWORD="$ADMIN_PASSWORD" \ /opt/vibmail/venv/bin/python /opt/vibmail/app/manage.py create_initial_admin \ - --username "$ADMIN_USERNAME" --password-env VIBMAIL_INITIAL_ADMIN_PASSWORD - unset VIBMAIL_INITIAL_ADMIN_PASSWORD - for system_mailbox in postmaster abuse; do - run_as_vmail /opt/vibmail/venv/bin/python /opt/vibmail/app/manage.py create_system_mailbox \ - "$system_mailbox" --confirm - done + "${ADMIN_COMMAND_ARGS[@]}" +) +printf '%s\n' "$ADMIN_COMMAND_OUTPUT" +if grep -Fxq 'INITIAL_ADMIN_STATUS=created' <<< "$ADMIN_COMMAND_OUTPUT"; then + write_initial_credentials "$ADMIN_PASSWORD" +elif grep -Fxq 'INITIAL_ADMIN_STATUS=preserved' <<< "$ADMIN_COMMAND_OUTPUT"; then + CREDENTIALS_FILE="not-created-existing-admin" +else + die "Initial administrator command did not report a recognized status" fi +unset ADMIN_PASSWORD + +for system_mailbox in postmaster abuse; do + SYSTEM_MAILBOX_ARGS=("$system_mailbox" --confirm) + if [[ $REPAIR -eq 1 ]]; then + SYSTEM_MAILBOX_ARGS+=(--if-missing) + fi + run_as_vmail /opt/vibmail/venv/bin/python /opt/vibmail/app/manage.py create_system_mailbox \ + "${SYSTEM_MAILBOX_ARGS[@]}" +done +run_as_vmail /opt/vibmail/venv/bin/python /opt/vibmail/app/manage.py verify_mail_storage CURRENT_PHASE="nginx-bootstrap" log "Installing bootstrap Nginx configuration" @@ -616,24 +670,6 @@ path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") path.chmod(0o600) PY -if [[ $REPAIR -eq 0 ]]; then - CREDENTIALS_FILE="/root/vibmail-initial-credentials-$STAMP.txt" - cat > "$CREDENTIALS_FILE" < if not INGESTION_LOCK_FILE.is_absolute(): raise ImproperlyConfigured("INGESTION_LOCK_FILE must be an absolute path") +# MariaDB uses the configured utf8mb4_unicode_ci collation for the application database. +# The underlying unique=True columns remain case-insensitive and the approved long unique +# columns fit within the supported InnoDB index width. Django cannot infer those deployment +# guarantees, so these two conservative backend warnings are qualified by installer/tests. +SILENCED_SYSTEM_CHECKS = [*SILENCED_SYSTEM_CHECKS, "mysql.W003", "models.W044"] # noqa: F405 + if not MAILSERVER_INTEGRATION_ENABLED: # noqa: F405 raise ImproperlyConfigured("MAILSERVER_INTEGRATION_ENABLED=true is required in production") diff --git a/mailbox-app/pyproject.toml b/mailbox-app/pyproject.toml index 61d8724..58c2a13 100644 --- a/mailbox-app/pyproject.toml +++ b/mailbox-app/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mailstack" -version = "1.3.0rc1" +version = "1.3.0rc2" description = "MailStack self-hosted receive-only mail server and shared team inbox" authors = [{ name = "Vib Tools" }] maintainers = [{ name = "Vib Tools" }] diff --git a/mailbox-app/tests/integration/test_commands_health_postfix.py b/mailbox-app/tests/integration/test_commands_health_postfix.py index 2c213a1..289ddec 100644 --- a/mailbox-app/tests/integration/test_commands_health_postfix.py +++ b/mailbox-app/tests/integration/test_commands_health_postfix.py @@ -105,6 +105,13 @@ def test_create_initial_admin_from_environment(monkeypatch): password_env="VIBMAIL_TEST_ADMIN_PASSWORD", ) + preserved = StringIO() + call_command("create_initial_admin", username="owner", if_missing=True, stdout=preserved) + assert "INITIAL_ADMIN_STATUS=preserved" in preserved.getvalue() + assert get_user_model().objects.count() == 1 + with pytest.raises(CommandError, match="requested administrator|administrator .* is missing"): + call_command("create_initial_admin", username="second", if_missing=True) + @pytest.mark.django_db def test_create_system_mailbox_requires_confirmation(): @@ -117,6 +124,13 @@ def test_create_system_mailbox_requires_confirmation(): assert (maildir / "new").is_dir() call_command("verify_mail_storage") + preserved = StringIO() + call_command("create_system_mailbox", "postmaster", confirm=True, if_missing=True, stdout=preserved) + assert "SYSTEM_MAILBOX_STATUS=preserved" in preserved.getvalue() + (maildir / "cur").rmdir() + with pytest.raises(CommandError, match="incomplete mail storage"): + call_command("create_system_mailbox", "postmaster", confirm=True, if_missing=True) + @pytest.mark.django_db def test_create_system_mailbox_can_create_ui_reserved_address(): @@ -156,6 +170,12 @@ def test_ingestion_command_rejects_bad_interval_and_second_worker(settings): try: with pytest.raises(CommandError): call_command("ingest_maildir", once=True) + output = StringIO() + call_command("ingest_maildir", once=True, dry_run=True, stdout=output) + assert "errors=0" in output.getvalue() + from apps.core.models import ServiceHeartbeat + + assert ServiceHeartbeat.objects.filter(service_name="maildir_ingestion").count() == 0 finally: lock.release() diff --git a/mailbox-app/tests/security/test_deployment_assets.py b/mailbox-app/tests/security/test_deployment_assets.py index 0c65d5a..758877a 100644 --- a/mailbox-app/tests/security/test_deployment_assets.py +++ b/mailbox-app/tests/security/test_deployment_assets.py @@ -148,3 +148,15 @@ def test_v1_2_1_security_hotfix_pins_and_bleach_scope(): assert "parse_email=False" in parser assert "GHSA-g75f-g53v-794x" in audit assert "--ignore-vuln GHSA-g75f-g53v-794x" in audit + + +def test_production_mariadb_warning_qualification_is_narrow_and_documented(): + production = read("config/settings/production.py") + bootstrap = (ROOT.parent / "deployment/templates/mariadb/bootstrap.sql.tpl").read_text(encoding="utf-8") + assert '"mysql.W003"' in production + assert '"models.W044"' in production + assert "utf8mb4_unicode_ci" in bootstrap + mailbox_models = read("apps/mailboxes/models.py") + message_models = read("apps/messages/models.py") + assert "email_address = models.EmailField(max_length=320, unique=True)" in mailbox_models + assert "storage_relative_path = models.CharField(max_length=500, unique=True)" in message_models diff --git a/scripts/test_installer.py b/scripts/test_installer.py index ee1f93e..7d632e3 100644 --- a/scripts/test_installer.py +++ b/scripts/test_installer.py @@ -92,6 +92,31 @@ def main() -> int: for fragment in required_fragments: require(fragment in installer_text, f"installer contract missing: {fragment}") require("pip install --upgrade" not in installer_text, "installer performs an unpinned pip upgrade") + require("install -d -m 0750 /var/log" not in installer_text, "installer mutates global /var/log mode") + require("runuser -u vmail -- env -i" in installer_text, "vmail subprocess environment is not sanitized") + require( + "/run/vibmail/mailbox-provision-locks" in installer_text, + "installer does not prepare the mailbox provisioning runtime lock path", + ) + require("--if-missing" in installer_text, "repair bootstrap does not use idempotent management commands") + require( + installer_text.index('write_initial_credentials "$ADMIN_PASSWORD"') + < installer_text.index('CURRENT_PHASE="nginx-bootstrap"'), + "initial credentials are not persisted immediately after administrator creation", + ) + require("tmux/screen" in installer_text, "SSH session resilience warning is missing") + dovecot_template = (ROOT / "deployment/templates/dovecot/99-vibmail.conf.tpl").read_text(encoding="utf-8") + require("allow_all_users=yes" in dovecot_template, "Dovecot static userdb LMTP fix is missing") + mariadb_template = (ROOT / "deployment/templates/mariadb/bootstrap.sql.tpl").read_text(encoding="utf-8") + require( + mariadb_template.count("CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci") >= 2, + "MariaDB case-insensitive utf8mb4 collation contract is missing", + ) + production_settings = (ROOT / "mailbox-app/config/settings/production.py").read_text(encoding="utf-8") + require( + '"mysql.W003"' in production_settings and '"models.W044"' in production_settings, + "qualified MariaDB compatibility warnings are not scoped in production settings", + ) require( installer_text.index('CURRENT_PHASE="acceptance-checks"') < installer_text.index("VIBMAIL_INSTALL=PASS"), diff --git a/scripts/test_operations.py b/scripts/test_operations.py index ba82d64..a908a67 100644 --- a/scripts/test_operations.py +++ b/scripts/test_operations.py @@ -90,6 +90,14 @@ def main() -> int: require('-H "Host: $APP_HOSTNAME"' in health, "health check does not use configured app hostname") require('-H "Host: $APP_HOSTNAME"' in verify, "application verifier does not use configured app hostname") require("manage.py check --deploy" in verify, "application verifier omits deployment checks") + require( + "manage.py ingest_maildir --once --dry-run" in verify, + "application verifier omits non-mutating Maildir validation", + ) + require( + "systemctl stop vibmail-ingestion" not in verify, + "application verifier must not stop the live ingestion worker", + ) print("OPERATIONAL_SCRIPTS=4") print("BACKUP_RESTORE_CONTRACT=PASS") From 7a800eb9f0b6d0e3fa347f4d7f05b5ad88a5e18f Mon Sep 17 00:00:00 2001 From: Md Nurnobi Date: Mon, 17 Aug 2026 11:13:58 -0700 Subject: [PATCH 2/3] fix(ci): exclude delta package metadata from source tree --- PATCH_BASELINE.txt | 11 --------- PATCH_FILE_LIST.txt | 51 --------------------------------------- PATCH_MANIFEST.sha256 | 54 ------------------------------------------ PATCH_RELEASE_NOTES.md | 26 -------------------- 4 files changed, 142 deletions(-) delete mode 100644 PATCH_BASELINE.txt delete mode 100644 PATCH_FILE_LIST.txt delete mode 100644 PATCH_MANIFEST.sha256 delete mode 100644 PATCH_RELEASE_NOTES.md diff --git a/PATCH_BASELINE.txt b/PATCH_BASELINE.txt deleted file mode 100644 index 3506bf3..0000000 --- a/PATCH_BASELINE.txt +++ /dev/null @@ -1,11 +0,0 @@ -MailStack Replace-Ready Delta Patch Baseline -=========================================== -Baseline archive: Baseline_MailStack(1).zip -Baseline archive SHA256: 78aebfa70fae8e1f70d1c7f32876d35171d1e0030445e7b7e9ff7228ac51cfce -Baseline Git commit: edb11c198b7b21a6765a2f3f8fbd0f997d6b07b1 -Baseline version: 1.3.0-rc.1 -Target version: 1.3.0-rc.2 -Phase: PHASE-003 — Installation, Recovery & Inbound Delivery Reliability Hardening -Candidate local commit: ff2ab30a7a95e65af87b36bbf48a64210e211b4d -Deleted source files: none -Changed/new source files: 51 diff --git a/PATCH_FILE_LIST.txt b/PATCH_FILE_LIST.txt deleted file mode 100644 index 468b656..0000000 --- a/PATCH_FILE_LIST.txt +++ /dev/null @@ -1,51 +0,0 @@ -.github/ISSUE_TEMPLATE/question.yml -.github/workflows/ci.yml -CHANGELOG.md -CITATION.cff -README.md -ROADMAP.md -SECURITY.md -VERSION -deployment/templates/dovecot/99-vibmail.conf.tpl -design/DESIGN_MANIFEST.json -docs/BUILD.md -docs/FORENSIC_AUDIT_REPORT.md -docs/FORENSIC_FILE_INVENTORY.json -docs/INSTALLATION.md -docs/OPERATIONS.md -docs/PUBLISHING.md -docs/QUICKSTART.md -docs/RELEASE_NOTES_1.3.0.md -docs/RELEASE_PROCESS.md -docs/TEST_REPORT.md -docs/TROUBLESHOOTING.md -documents/ADMIN_GUIDE.md -documents/BASELINE.md -documents/DOCUMENTATION_MANIFEST.json -documents/DOCUMENTATION_POLICY.md -documents/HOW_TO_USE.md -documents/README.md -documents/USER_MANUAL.md -documents/design/ACCESSIBILITY_SPECIFICATION.md -documents/design/COMPONENT_MATRIX.md -documents/design/FUTURE_UI_ROADMAP.md -documents/design/IMPLEMENTATION_STATUS.md -documents/design/RESPONSIVE_SPECIFICATION.md -documents/design/SCREEN_CATALOG.md -documents/design/UI_FOUNDATION.md -documents/phases/PHASE-000-BASELINE.md -documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md -documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md -documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md -install.sh -mailbox-app/CHANGELOG.md -mailbox-app/apps/accounts/management/commands/create_initial_admin.py -mailbox-app/apps/ingestion/management/commands/ingest_maildir.py -mailbox-app/apps/ingestion/service.py -mailbox-app/apps/mailboxes/management/commands/create_system_mailbox.py -mailbox-app/config/settings/production.py -mailbox-app/pyproject.toml -mailbox-app/tests/integration/test_commands_health_postfix.py -mailbox-app/tests/security/test_deployment_assets.py -scripts/test_installer.py -scripts/test_operations.py diff --git a/PATCH_MANIFEST.sha256 b/PATCH_MANIFEST.sha256 deleted file mode 100644 index b209db6..0000000 --- a/PATCH_MANIFEST.sha256 +++ /dev/null @@ -1,54 +0,0 @@ -ef4c844680601fd26b1c13550ed9e7b64634f00a0c997a05f31e1bfced5c3b1c .github/ISSUE_TEMPLATE/question.yml -8938f7c5100d9aa2cdb022c93cff2e3c9cd2521f98ca1e36ba15013529955969 .github/workflows/ci.yml -28b4d4f3b535a5805deea800e85c07c811e618e1d936e61b39aaf9386a777f21 CHANGELOG.md -04c310da50d9e7f90dd773a7e20868131c04a32e49adbd667ea8987eb8c430f5 CITATION.cff -8ae650c2d48f3d9cf5df8f1eafa3e5bdf2869c628c11406f25858058b0dd9ead PATCH_BASELINE.txt -1dd8af3b422dd3fe4f1a93584631d2765b166545164601a9c74e26f6b9b4c2a4 PATCH_FILE_LIST.txt -0bf98dec2c174cc06317c70e56c9abcfee28ad4e889a3e011c62d1543465cb0e PATCH_RELEASE_NOTES.md -f98884d98deb7c56e6453663ef9e38cd1d7bf32016692cc47245a2a976622237 README.md -d34a6d69c0934af9111861c4a5eb1ec617e7145561c12860dc92359d9fe28837 ROADMAP.md -07cf4bce801bfd01d172269c78c6dc9a65bf183274c9176a3966fbe841f0880d SECURITY.md -e0019333d1ddc7558aa3803b957eed077855274ca7b112a1028172c473584db2 VERSION -9a3386e2fd3a7b066596760005d55dfa4d0f0ab25aaee1548584191d6b91dc28 deployment/templates/dovecot/99-vibmail.conf.tpl -2b3aa59b5279a4cdd2944a174ade9cde00d0ebb7f722fedabc950d1ed6980647 design/DESIGN_MANIFEST.json -3fd43e51482f4bea9f0cde4f9fb360915a7228116c0b4c6bbb299c0d3c221241 docs/BUILD.md -93a748cde90dc3a0c0b08d0f31c20207baafc4235665071e255fb017af90ba19 docs/FORENSIC_AUDIT_REPORT.md -b62bc3941ce9f444ee8cb36dacb8cade0bfeec44969564ff8d9e3b34235ae766 docs/FORENSIC_FILE_INVENTORY.json -c861c9464e076bb9cac0711be70099a7cd03e193e519d03e9f96898100f839f3 docs/INSTALLATION.md -25d347928a3b5ad7884632b19f3221160d4b1dffe867361831e126cd88eae660 docs/OPERATIONS.md -2ba14ec0d8842e8a2551d7e962c8e82445b1737c3773ce4033f3ee2441c70c83 docs/PUBLISHING.md -7fe067162d4b6db47e5e35ff841fa6acc577e97a51abfe9dc1e49f535e286d55 docs/QUICKSTART.md -e535f31438a58973f619166d79c94b2f1fc9ceb2f07471a99ce469716d2da8ee docs/RELEASE_NOTES_1.3.0.md -174f022988e4c49dca9c56df58aeb9cd65109966d53467954347ba332b1d9145 docs/RELEASE_PROCESS.md -b6614e4f8edccefdd2a3f7520bd2120822b780a35ac3bd95cf2a6c290f41ce2e docs/TEST_REPORT.md -7ff9f0a8afcd5348b72b7a37b7af51f0859f5ec8c920cc5721c8f5b3f24b21dc docs/TROUBLESHOOTING.md -ccc958123954f49d630b65022b66e54e915450a8d23e3d7374688a2a684b5dbb documents/ADMIN_GUIDE.md -ac3b47c424c89d0362ec9f241c81ad2307be9f5049312b8c9e79c5f8948bbee8 documents/BASELINE.md -75b5a206ca0cf3b76eed2351b553984e076d33549c0fdbe98e41307d69764f44 documents/DOCUMENTATION_MANIFEST.json -bf7d3a50a4908143bf6b79d158872e364dab1966c25a171b2dc463cd94e0ee71 documents/DOCUMENTATION_POLICY.md -74e1ba6d9794773d53fdc101e249f1cf6358571be037a53283c62cde95a23522 documents/HOW_TO_USE.md -277026a111f098ea780a005d767dd8427953bc90a6ca887a87314f576f4c0ede documents/README.md -629af8a82133856a9d100856992b43be4ce2d185c387b06c7f300e0ad008aa0c documents/USER_MANUAL.md -a09c0a02ea09c5670479d6f8435462cc40a39e6151a21e4458874831d9934bd0 documents/design/ACCESSIBILITY_SPECIFICATION.md -403c550dea587811ccd333bf5991adde6e8d1578811d95a7db56184816463672 documents/design/COMPONENT_MATRIX.md -562e0c4982f598776a353a34b9e6f2108fe428a97946b882a0cce0e647310a5e documents/design/FUTURE_UI_ROADMAP.md -928640f9b01083968f0db0d03c0f2d8d4e734b43c7ab6357509a3c92a5ae4a41 documents/design/IMPLEMENTATION_STATUS.md -df937d87f53f2ea9fb3a8927e35263c4ecc3dbfd4de9e43a4f5dc61c984d6e15 documents/design/RESPONSIVE_SPECIFICATION.md -895a17622c828fe240b8c8cdbd318a306563251c40fd9b031ecc8902d6eb4c82 documents/design/SCREEN_CATALOG.md -b8bab951b3d5ae666d35ef86653e32682c1e8dc7450b49f0f95e05e0f72766c9 documents/design/UI_FOUNDATION.md -1ee7772711cd7c2d7ba9c5f4188a31f093c800e6c25d869e9f0f544368e0b50e documents/phases/PHASE-000-BASELINE.md -7e0e2a0539cfc3215cae7aeb9bb93fe1b249bcd16155ef2260dc85e4058c4c6e documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md -86f152eed2a86867667ee25ed82ea1293c36ae91af11a7c49b562576339ff4ec documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md -a294f0cc03875fa95ba88feac9f460dd41e714fe2f06503f18365edef0e637ca documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md -f7db90e856ba9ef5598cdc248e24011a57f02c94d4bdb78ef2a42eadb6e71c31 install.sh -30b80318b9c7df92a597d56e4699816d5c974768c06b1443613dcd1dd20c85b5 mailbox-app/CHANGELOG.md -cd9e62ad4a57e567135dc22820fd6346b2908b7d4bc4d569c9ca567b6fc2063c mailbox-app/apps/accounts/management/commands/create_initial_admin.py -18f76e00e8a2f99810387030ad5af39f4214350fe7af2164da28f6a905816ae6 mailbox-app/apps/ingestion/management/commands/ingest_maildir.py -950050c62467b905dec31d0c303da7d89abd3179032a72d947da4a8ab2ca8250 mailbox-app/apps/ingestion/service.py -703988125d67eab971a5c4e5a6ef966f0e003d8f5af22e47e5ff26ac1cc1981d mailbox-app/apps/mailboxes/management/commands/create_system_mailbox.py -320db55b2db817c770cea4fcce2710b4f7820af89266bea3bdcb37d9d46f06f6 mailbox-app/config/settings/production.py -f08792aae7eb9efabe59112ea8707353f781cf691aefba3a5b52b46d72df4353 mailbox-app/pyproject.toml -ac21742565d672b152e883bf64f14119569470a5a06abd7ead55d81e99825fc9 mailbox-app/tests/integration/test_commands_health_postfix.py -79f339a39ff17d98f388430bdb2932393876ff999b86e511d53f647fa5dd7ed0 mailbox-app/tests/security/test_deployment_assets.py -a1aa7a67b450f99828cef66847a7d73769b77aea8993a6995418b80c2433c2d6 scripts/test_installer.py -10185ee3d8fb2a805c9b04787f7326dcd5c65db86a9965ed7fcbb3aa3d23ae23 scripts/test_operations.py diff --git a/PATCH_RELEASE_NOTES.md b/PATCH_RELEASE_NOTES.md deleted file mode 100644 index 1746534..0000000 --- a/PATCH_RELEASE_NOTES.md +++ /dev/null @@ -1,26 +0,0 @@ -# MailStack 1.3.0-rc.2 PHASE-003 Delta Patch - -This patch is valid only against the frozen `1.3.0-rc.1` baseline at commit -`edb11c198b7b21a6765a2f3f8fbd0f997d6b07b1` / baseline ZIP SHA-256 -`78aebfa70fae8e1f70d1c7f32876d35171d1e0030445e7b7e9ff7228ac51cfce`. - -## In-scope fixes - -1. Preserve host-wide `/var/log` permissions and validate the dedicated MailStack log path. -2. Isolate installer-launched Django commands from stale parent-shell environment variables. -3. Prepare the mailbox provisioning runtime lock directory before bootstrap. -4. Make reviewed partial-install repair idempotent for valid bootstrap objects. -5. Persist newly created initial administrator credentials before later installer phases. -6. Fix Dovecot static-userdb LMTP delivery with `allow_all_users=yes` while retaining Postfix recipient validation. -7. Make one-shot dry-run ingestion verification live-worker compatible and non-mutating. -8. Add SSH/PuTTY resilient-session guidance and installer warning. -9. Qualify the known MariaDB/Django uniqueness warnings without a schema migration. -10. Synchronize the staging hotfixes into the canonical source and bump to `1.3.0-rc.2`. - -## Scope preservation - -No UI page, route, authorization model, application feature, outbound mail function, database migration, dependency, service name, or legacy runtime identifier is intentionally changed. No source file is deleted. - -## Apply - -Verify the baseline first, then overlay the source paths from this ZIP onto the repository root. The four `PATCH_*` files are package metadata, not canonical source files; remove them from the repository working tree before committing if the ZIP was extracted directly into the repository root. Run the documented verification gates before deployment. From a03d4b47775cd478887767e84f954b2c560b71e6 Mon Sep 17 00:00:00 2001 From: Md Nurnobi Date: Mon, 17 Aug 2026 13:15:38 -0700 Subject: [PATCH 3/3] fix(ci): secure dependencies and harden cross-platform audit runtime --- .github/ISSUE_TEMPLATE/question.yml | 2 +- .github/workflows/ci.yml | 4 +- CHANGELOG.md | 14 ++ CITATION.cff | 2 +- CONTRIBUTING.md | 2 + README.md | 6 +- ROADMAP.md | 2 +- SECURITY.md | 4 +- VERSION | 2 +- design/DESIGN_MANIFEST.json | 2 +- docs/BUILD.md | 4 +- docs/DEPENDENCY_REVIEW.md | 9 +- docs/FORENSIC_AUDIT_REPORT.md | 47 +++-- docs/FORENSIC_FILE_INVENTORY.json | 190 ++++++++++------- docs/PUBLISHING.md | 16 +- docs/RELEASE_NOTES_1.3.0.md | 46 ++++- docs/RELEASE_PROCESS.md | 2 +- docs/TEST_REPORT.md | 52 ++++- documents/ADMIN_GUIDE.md | 2 +- documents/BASELINE.md | 2 +- documents/DOCUMENTATION_MANIFEST.json | 66 +++--- documents/DOCUMENTATION_POLICY.md | 2 +- documents/HOW_TO_USE.md | 2 +- documents/README.md | 32 +-- documents/USER_MANUAL.md | 2 +- .../design/ACCESSIBILITY_SPECIFICATION.md | 2 +- documents/design/COMPONENT_MATRIX.md | 2 +- documents/design/FUTURE_UI_ROADMAP.md | 2 +- documents/design/IMPLEMENTATION_STATUS.md | 2 +- documents/design/RESPONSIVE_SPECIFICATION.md | 2 +- documents/design/SCREEN_CATALOG.md | 2 +- documents/design/UI_FOUNDATION.md | 2 +- documents/phases/PHASE-000-BASELINE.md | 2 +- .../PHASE-001-UI-DESIGN-INTAKE-BASELINE.md | 2 +- ...RED-UI-FOUNDATION-AND-APPLICATION-SHELL.md | 2 +- ...COVERY-AND-INBOUND-DELIVERY-RELIABILITY.md | 2 +- mailbox-app/CHANGELOG.md | 10 + mailbox-app/pyproject.toml | 2 +- mailbox-app/requirements/constraints.txt | 2 +- mailbox-app/requirements/locked.txt | 2 +- .../tests/security/test_deployment_assets.py | 9 + scripts/forensic_audit.py | 41 ++-- scripts/shell_runtime.py | 192 ++++++++++++++++++ scripts/test_installer.py | 26 ++- scripts/test_operations.py | 12 +- 45 files changed, 614 insertions(+), 218 deletions(-) create mode 100644 scripts/shell_runtime.py diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml index 5e9150b..fc2c028 100644 --- a/.github/ISSUE_TEMPLATE/question.yml +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -10,7 +10,7 @@ body: id: version attributes: label: MailStack version - placeholder: "1.3.0-rc.2" + placeholder: "1.3.0-rc.4" validations: required: true - type: dropdown diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a79cca7..721655a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,5 +125,5 @@ jobs: - name: Release verification run: | python scripts/verify_release.py \ - dist/mailstack-1.3.0-rc.2-source.zip \ - --checksum dist/mailstack-1.3.0-rc.2-source.zip.sha256 + dist/mailstack-1.3.0-rc.4-source.zip \ + --checksum dist/mailstack-1.3.0-rc.4-source.zip.sha256 diff --git a/CHANGELOG.md b/CHANGELOG.md index b959891..ce849b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,12 +26,26 @@ All notable repository-level changes are recorded here. Application history befo - Established `MAILSTACK-1.3.0-RC1-DOCS-BASELINE-001` as the protected feature and documentation baseline. - Added the root `documents/` user-documentation hub with a user manual, task-based how-to guide, administrator guide, baseline record and mandatory phase history. - Added deterministic documentation index and manifest synchronization, phase scaffolding, contract tests and CI policy enforcement so maintained feature changes cannot merge without the required user documentation and changelog updates. +- Corrected the RC4 Windows audit harness so Git Bash maps installer-only `python3` calls to the exact Python interpreter running the local test process, without changing the Ubuntu production installer or requiring a machine-wide Windows alias. ### Compatibility - Preserved `VIBMAIL_*` environment variables, `vibmail-*` service names, `/etc/vibmail` paths, database identifiers, source directories, and legacy deployment contracts. - No application behavior, database migration operations, mail-flow, authentication, authorization, or deployment contract was intentionally changed. Legacy protocol headers such as `X-VibMail-CSRF` remain unchanged. +## 1.3.0-rc.3 — 2026-08-17 + +### Security + +- Upgraded the locked transitive `sqlparse` runtime from 0.5.5 to 0.6.0 after GitHub Actions run `32053931714` identified CVE-2026-71491, CVE-2026-59894, CVE-2026-59893, and CVE-2026-54284. +- Kept Django at 5.2.16; its declared `sqlparse>=0.3.1` dependency accepts sqlparse 0.6.0, and MailStack's Python 3.12 runtime satisfies sqlparse 0.6.0's Python 3.10+ requirement. +- Preserved all PHASE-003 installation, recovery, LMTP, ingestion, UI, route, schema, and deployment behavior; this maintenance delta changes only the vulnerable dependency pin plus required release metadata and verification records. + +### CI + +- Corrected RC2 qualification records that could be read as if the current PHASE-003 dependency audit had passed. The RC2 branch passed structural/documentation/installer/operations gates but stopped at the blocking online advisory gate. +- Regenerated deterministic documentation, design, and forensic inventories for RC3. Full dependency-backed GitHub Actions requalification remains mandatory before merge or release. + ## 1.3.0-rc.2 — 2026-08-17 ### Fixed diff --git a/CITATION.cff b/CITATION.cff index 3b30a30..0bcf46e 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,7 +2,7 @@ cff-version: 1.2.0 message: "If you use MailStack, cite the software release and link to the canonical source repository." title: "MailStack" type: software -version: "1.3.0-rc.2" +version: "1.3.0-rc.4" date-released: 2026-08-17 authors: - name: "Vib Tools" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20da9bb..676c620 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,8 @@ python scripts/forensic_audit.py --root . --full The full gate runs tests, coverage, Ruff, Bandit, Django checks, contact-service tests and source-safety checks. +On Windows, the repository audit tools require a real Bash runtime for shell syntax and installer-plan checks. They automatically prefer Git for Windows/Git Bash over the WSL `bash.exe` launcher and probe the runtime before use. Install Git for Windows or set `BASH_EXECUTABLE` to a known-good Bash path when using a custom shell. A broken WSL or Docker Desktop VM must not be mistaken for a MailStack shell-script failure. + ## Pull requests 1. Open an issue for significant behavior, schema or deployment changes. diff --git a/README.md b/README.md index f9f497e..b0390d7 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ It combines **Postfix**, **Dovecot LMTP**, **Django**, **MariaDB**, **Maildir**, **Gunicorn**, and **Nginx** in a hardened single-node reference deployment. MailStack provides a private browser-based inbox, administrator-managed users, object-level mailbox access, safe email rendering, protected attachments, live inbox updates, operational audit logs, backup and restore tooling, a public website, and an isolated contact service. -> **Release status:** `v1.3.0-rc.2` remains a release candidate. Clean Ubuntu 24.04 installation and real external SMTP/LMTP delivery have been exercised during the PHASE-003 hardening campaign; backup/restore, restart-recovery, legal, and final release-owner acceptance still gate stable promotion. +> **Release status:** `v1.3.0-rc.4` remains a release candidate. Clean Ubuntu 24.04 installation and real external SMTP/LMTP delivery have been exercised during the PHASE-003 hardening campaign; backup/restore, restart-recovery, legal, and final release-owner acceptance still gate stable promotion. ## Why MailStack @@ -211,8 +211,8 @@ Build and verify a deterministic source release: ```bash python scripts/build_release.py --root . python scripts/verify_release.py \ - dist/mailstack-1.3.0-rc.2-source.zip \ - --checksum dist/mailstack-1.3.0-rc.2-source.zip.sha256 + dist/mailstack-1.3.0-rc.4-source.zip \ + --checksum dist/mailstack-1.3.0-rc.4-source.zip.sha256 ``` ## Security diff --git a/ROADMAP.md b/ROADMAP.md index 5c4dcb5..84dbfe4 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -29,7 +29,7 @@ This roadmap is directional. Security, data integrity and backward compatibility ## 1.3 RC hardening - PHASE-003 qualifies clean-install, reviewed partial-install repair, live-safe verification, and real external SMTP-to-LMTP delivery fixes discovered during Ubuntu 24.04 staging acceptance. -- Keep 1.3.0-rc.2 as a release candidate until backup/restore, restart-recovery, final security/legal, and release-owner acceptance are complete. +- Keep 1.3.0-rc.4 as a release candidate until backup/restore, restart-recovery, final security/legal, and release-owner acceptance are complete. ## 1.3 stable diff --git a/SECURITY.md b/SECURITY.md index 4b85402..e197b0f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,9 @@ Security fixes are applied to the latest tagged release candidate or stable rele | Version | Support status | |---|---| -| 1.3.0-rc.2 | Supported release candidate | +| 1.3.0-rc.4 | Supported release candidate | +| 1.3.0-rc.3 | Superseded release candidate | +| 1.3.0-rc.2 | Superseded release candidate | | 1.3.0-rc.1 | Superseded release candidate | | 1.2.x | Legacy deployment assets retained; security support is best-effort | | Older versions | Unsupported | diff --git a/VERSION b/VERSION index 1d074f4..9b388ed 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0-rc.2 +1.3.0-rc.4 diff --git a/design/DESIGN_MANIFEST.json b/design/DESIGN_MANIFEST.json index 6a8f0c6..811dcce 100644 --- a/design/DESIGN_MANIFEST.json +++ b/design/DESIGN_MANIFEST.json @@ -561,7 +561,7 @@ "unsupported_features_inactive": true }, "project": "MailStack", - "release_version": "1.3.0-rc.2", + "release_version": "1.3.0-rc.4", "schema_version": 1, "source_archive": { "archive_crc": "PASS", diff --git a/docs/BUILD.md b/docs/BUILD.md index 6e62772..c5ddcee 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -20,8 +20,8 @@ python scripts/forensic_audit.py --root . --full ```bash python scripts/build_release.py --root . python scripts/verify_release.py \ - dist/mailstack-1.3.0-rc.2-source.zip \ - --checksum dist/mailstack-1.3.0-rc.2-source.zip.sha256 + dist/mailstack-1.3.0-rc.4-source.zip \ + --checksum dist/mailstack-1.3.0-rc.4-source.zip.sha256 ``` The builder normalizes archive timestamps, preserves executable permissions, writes a source manifest, excludes generated/runtime artifacts and emits a SHA-256 checksum. diff --git a/docs/DEPENDENCY_REVIEW.md b/docs/DEPENDENCY_REVIEW.md index f98554e..68defab 100644 --- a/docs/DEPENDENCY_REVIEW.md +++ b/docs/DEPENDENCY_REVIEW.md @@ -11,6 +11,7 @@ | gunicorn | 25.1.0 | MIT | WSGI server | | mysqlclient | 2.2.7 | GPL-2.0-or-later | MariaDB/MySQL adapter | | python-dotenv | 1.2.2 | BSD-3-Clause | Environment loading | +| sqlparse | 0.6.0 | BSD-3-Clause | Django SQL parsing dependency | | WhiteNoise | 6.11.0 | MIT | Static-file fallback | Transitive runtime versions are pinned in `mailbox-app/requirements/locked.txt`. Development tooling is pinned in `requirements/development.txt` and includes pytest, coverage, Ruff, Bandit and pip-audit. @@ -24,4 +25,10 @@ No obvious conflict was identified between the declared direct dependency licens ## Vulnerability review -`pip check` passes. Django is pinned to 5.2.16, the July 2026 security maintenance release for the 5.2 LTS line. The blocking network-enabled `pip-audit` gate passed in GitHub Actions run `30133728843` for commit `1e1737edea2e6c922265a15d8584b56671820c65` and remains mandatory for future changes. +Django remains pinned to 5.2.16, the July 2026 security maintenance release for the 5.2 LTS line. +GitHub Actions run `32053931714` on PHASE-003 RC2 passed the structural and repository gates but the +blocking `pip-audit` step found CVE-2026-71491, CVE-2026-59894, CVE-2026-59893, and CVE-2026-54284 +in `sqlparse==0.5.5`. RC3 introduced and RC4 preserves `sqlparse==0.6.0`, the upstream security release fixing those findings. +Django 5.2.16 permits `sqlparse>=0.3.1`, and sqlparse 0.6.0 supports Python 3.10+, including +MailStack's required Python 3.12 runtime. `pip check` and the network-enabled blocking `pip-audit` +gate remain mandatory for RC4 qualification; no advisory is ignored or suppressed. diff --git a/docs/FORENSIC_AUDIT_REPORT.md b/docs/FORENSIC_AUDIT_REPORT.md index db14e63..d05dbec 100644 --- a/docs/FORENSIC_AUDIT_REPORT.md +++ b/docs/FORENSIC_AUDIT_REPORT.md @@ -1,9 +1,9 @@ -# Forensic audit report — MailStack 1.3.0 RC2 +# Forensic audit report — MailStack 1.3.0 RC3 **PHASE-003 audit date:** 2026-08-17 -**Release version:** `1.3.0-rc.2` +**Release version:** `1.3.0-rc.4` **Target runtime:** Ubuntu Server 24.04 LTS and CPython 3.12 -**Release classification:** CI-qualified and clean-clone-qualified open-source release candidate +**Release classification:** locally structurally qualified release candidate; dependency-backed RC3 CI requalification pending ## Executive disposition @@ -26,13 +26,13 @@ | Installer and operations contract tests | PASS | | Template rendering and placeholder validation | PASS | | Forensic file/symbol inventory | PASS | -| Deterministic release ZIP, manifest and checksum | PASS in GitHub CI | -| Online dependency advisory query | PASS in GitHub CI | -| Clean Ubuntu 24.04 RC2 full-stack acceptance | PENDING exact-RC2 clean VPS requalification | -| Real inbound SMTP/LMTP acceptance | PASS in staging after equivalent PHASE-003 LMTP hotfix; exact-RC2 clean requalification pending | +| Deterministic release ZIP, manifest and checksum | PENDING RC4 CI; deterministic local build/verification required before patch handoff | +| Online dependency advisory query | PENDING RC4 rerun — RC2 run `32053931714` failed on sqlparse 0.5.5; RC3/RC4 pin 0.6.0 | +| Clean Ubuntu 24.04 RC3 full-stack acceptance | PENDING exact-RC3 clean VPS requalification | +| Real inbound SMTP/LMTP acceptance | PASS in staging after equivalent PHASE-003 LMTP hotfix; exact-RC3 clean requalification pending | | Copyright ownership/license confirmation | PENDING release owner | -**OPEN_SOURCE_RELEASE_CANDIDATE:** PASS +**OPEN_SOURCE_RELEASE_CANDIDATE:** PENDING RC4 CI **PRODUCTION_ACCEPTANCE:** PENDING ## Audited scope @@ -96,10 +96,11 @@ See `FEATURE_MATRIX.md` for the feature-by-feature verification record. 15. The complete 25-image UI and logo archive is preserved with stable IDs, SHA-256 hashes, PNG structural validation, scope classification, and CI enforcement. 16. PHASE-002 adds the frozen runtime design tokens, responsive authenticated and sign-in shells, local SVG assets, accessible navigation behavior, and focused UI contract gates without changing page business logic. 17. Cross-platform verification now closes contact-service SQLite handles deterministically, preserves POSIX-only permission assertions, and subjects the standalone contact service to Ruff and Bandit in both the full forensic gate and CI. +18. RC3 updates the vulnerable transitive sqlparse 0.5.5 lock to upstream 0.6.0 after the blocking PHASE-003 CI advisory scan identified four 2026 CVEs; no advisory suppression or application behavior change is introduced. ## Automated evidence -- Django tests: **195 passed, 1 capability-based skip, 0 failed** +- Last completed pre-RC3 Django suite: **195 passed, 1 capability-based skip, 0 failed**; RC3 full rerun pending - Application coverage: **94.99%**; minimum: **85%** - Ruff: **PASS** - Bandit: **PASS** @@ -121,7 +122,12 @@ The authoritative repository qualification is GitHub Actions run `30133728843` o Verified controls include root-only generated secrets, strict configuration validation, Argon2 password hashing, CSRF and secure-cookie controls, login throttling, object-level mailbox authorization, safe HTML sanitization, protected attachments, receive-only SMTP, no public registration, no IMAP/POP3/submission in the reference deployment, MariaDB least privilege, systemd sandboxing, safe archive extraction, checksum verification and fail-closed CI/release gates. -The network-enabled `pip-audit` command passed as a blocking step in GitHub Actions run `30133728843`. Future dependency changes must continue to pass the locked dependency, `pip check`, and online advisory gates. +The historical pre-PHASE-003 `pip-audit` gate passed in GitHub Actions run `30133728843`. +For the current PHASE-003 branch, run `32053931714` failed at the blocking advisory step because +`sqlparse==0.5.5` was newly reported for CVE-2026-71491, CVE-2026-59894, CVE-2026-59893, and +CVE-2026-54284. RC3 pins upstream sqlparse 0.6.0, which contains those security fixes. The RC3 +dependency audit, `pip check`, and all downstream CI gates remain mandatory; no vulnerability is +ignored or waived. ## Performance review @@ -149,10 +155,19 @@ management-command/test contracts, release metadata, and required documentation. migration, dependency, UI page, URL, permission model, outbound mail path, or data transformation. The structural forensic gate passes with zero blocking findings after regenerating the deterministic -file inventory. Dependency-free documentation, design, UI-foundation, template, installer, operations, -Python compile, and shell-syntax gates pass locally. Dependency-backed Django/Ruff/Bandit/coverage -and full-forensic qualification remain blocking in GitHub Actions because the local artifact builder -does not contain or have network access to the pinned development environment. +file inventory. RC4 additionally centralizes verified Bash runtime discovery for repository-level +installer, operations, and forensic tooling: Windows prefers Git for Windows Bash over the WSL +launcher, while Linux keeps the system Bash contract. This prevents an unavailable WSL/Docker +Desktop backing disk from being reported as multiple MailStack shell syntax defects. A subsequent +Windows run proved Git Bash selection was correct but exposed a second host-compatibility edge: the +Windows Python installation provided `python` but no Git-Bash-visible `python3` command. The RC4 +audit harness now uses a process-local `BASH_ENV` bridge to map `python3` to the exact interpreter +running the test harness on Windows only; `install.sh` and Ubuntu's production `python3` behavior +remain unchanged. Dependency-free documentation, design, UI-foundation, template, installer, +operations, Python compile, and shell-syntax gates pass locally. Dependency-backed +Django/Ruff/Bandit/coverage and full-forensic qualification remain blocking in GitHub Actions because +the local artifact builder does not contain or have network access to the pinned development +environment. ## External acceptance gates @@ -162,8 +177,8 @@ Before stable promotion: 2. Preserve the successful GitHub CI baseline and require every subsequent release commit to pass all blocking gates. 3. Install on a clean isolated Ubuntu Server 24.04 VPS. 4. Verify DNS, MX, PTR/rDNS, TLS, firewall, unknown-recipient rejection, LMTP delivery, ingestion, authorization, contact delivery, backup, restore and restart recovery. -5. Publish `v1.3.0-rc.2` only after its blocking CI gates pass; promote to stable only after the remaining acceptance gates pass. +5. Publish `v1.3.0-rc.4` only after its blocking CI gates pass; promote to stable only after the remaining acceptance gates pass. ## Final classification -The repository remains a **MailStack 1.3.0 RC2 release candidate**, not yet a proven stable production release. PHASE-003 source qualification is complete only when its blocking GitHub Actions run passes. +The repository remains a **MailStack 1.3.0 RC4 release candidate**, not yet a proven stable production release. PHASE-003 source qualification is complete only when its blocking GitHub Actions run passes. diff --git a/docs/FORENSIC_FILE_INVENTORY.json b/docs/FORENSIC_FILE_INVENTORY.json index 2b49cfb..30aed4f 100644 --- a/docs/FORENSIC_FILE_INVENTORY.json +++ b/docs/FORENSIC_FILE_INVENTORY.json @@ -53,7 +53,7 @@ "kind": "text", "lines": 35, "path": ".github/ISSUE_TEMPLATE/question.yml", - "sha256": "ef4c844680601fd26b1c13550ed9e7b64634f00a0c997a05f31e1bfced5c3b1c", + "sha256": "b11e421c9750e355cc98bf19a7fca8429a59ab4579143c19f40c0249f65bd69f", "size_bytes": 902 }, { @@ -74,7 +74,7 @@ "kind": "text", "lines": 129, "path": ".github/workflows/ci.yml", - "sha256": "8938f7c5100d9aa2cdb022c93cff2e3c9cd2521f98ca1e36ba15013529955969", + "sha256": "0b719b1125e6cd730da533e1bc3d729e00b067a8f0707c7c2837ada2d3fd8b81", "size_bytes": 4106 }, { @@ -93,16 +93,16 @@ }, { "kind": "text", - "lines": 82, + "lines": 96, "path": "CHANGELOG.md", - "sha256": "28b4d4f3b535a5805deea800e85c07c811e618e1d936e61b39aaf9386a777f21", - "size_bytes": 6937 + "sha256": "fa3285b026126a1d98da9cef4817015c86decf0793b8205bf4357ad8b6773f10", + "size_bytes": 8261 }, { "kind": "text", "lines": 21, "path": "CITATION.cff", - "sha256": "04c310da50d9e7f90dd773a7e20868131c04a32e49adbd667ea8987eb8c430f5", + "sha256": "777a7a70e2b7d3423c634f347af2e894dcc77b4f179347c9bbb14af2f03e6f91", "size_bytes": 493 }, { @@ -114,10 +114,10 @@ }, { "kind": "text", - "lines": 77, + "lines": 79, "path": "CONTRIBUTING.md", - "sha256": "87157dd3cb0ba2d2080bc9008d81aca213a8d6d920c83a76f74e02cca03050cb", - "size_bytes": 3236 + "sha256": "59cf3331d32ee1373d0de6937355f83bdb9e657d1f574e6e92738474ca1904ce", + "size_bytes": 3661 }, { "kind": "text", @@ -137,22 +137,22 @@ "kind": "text", "lines": 314, "path": "README.md", - "sha256": "f98884d98deb7c56e6453663ef9e38cd1d7bf32016692cc47245a2a976622237", + "sha256": "55aea5c0a8828fa26d10913c82a3fc16a78c2ee3bf44c00bbb7aff1bd68a7719", "size_bytes": 13033 }, { "kind": "text", "lines": 56, "path": "ROADMAP.md", - "sha256": "d34a6d69c0934af9111861c4a5eb1ec617e7145561c12860dc92359d9fe28837", + "sha256": "6b512eb665b048062222dfc2eafbd201f248b9da214ecc0aa0131b9f7be2978b", "size_bytes": 2806 }, { "kind": "text", - "lines": 38, + "lines": 40, "path": "SECURITY.md", - "sha256": "07cf4bce801bfd01d172269c78c6dc9a65bf183274c9176a3966fbe841f0880d", - "size_bytes": 1872 + "sha256": "fdae4f0771a6ff2dcec0bc10cdef9d40d7c34e03f3539e172161fffd4bfc2659", + "size_bytes": 1964 }, { "kind": "text", @@ -165,7 +165,7 @@ "kind": "text", "lines": 1, "path": "VERSION", - "sha256": "e0019333d1ddc7558aa3803b957eed077855274ca7b112a1028172c473584db2", + "sha256": "87892d5c7aa3d86de1ccd4742825eb8ff6f9c6350133351dd01a6007e1163e87", "size_bytes": 11 }, { @@ -277,7 +277,7 @@ "kind": "text", "lines": 581, "path": "design/DESIGN_MANIFEST.json", - "sha256": "2b3aa59b5279a4cdd2944a174ade9cde00d0ebb7f722fedabc950d1ed6980647", + "sha256": "353323c27340d064b3226a94ad5dd6286d5c3efad3001b2fbd77f55601a420aa", "size_bytes": 21143 }, { @@ -469,7 +469,7 @@ "kind": "text", "lines": 31, "path": "docs/BUILD.md", - "sha256": "3fd43e51482f4bea9f0cde4f9fb360915a7228116c0b4c6bbb299c0d3c221241", + "sha256": "04fdbccd2bb6484d38cfe49d4df429454c5309178f7c297b22bf3b8de3a852bf", "size_bytes": 804 }, { @@ -488,10 +488,10 @@ }, { "kind": "text", - "lines": 27, + "lines": 34, "path": "docs/DEPENDENCY_REVIEW.md", - "sha256": "8e4eec877b9efb8e3e55118da2c0097c46ece6d8f0d2b2915415edf6d9142790", - "size_bytes": 1714 + "sha256": "2a9e2eeb011457c9ca9711e9422febd731c006aafc22c1760c17d173fdb1d2c6", + "size_bytes": 2184 }, { "kind": "text", @@ -537,10 +537,10 @@ }, { "kind": "text", - "lines": 169, + "lines": 184, "path": "docs/FORENSIC_AUDIT_REPORT.md", - "sha256": "93a748cde90dc3a0c0b08d0f31c20207baafc4235665071e255fb017af90ba19", - "size_bytes": 11056 + "sha256": "d02d831ed01c89a80badec752f7fed92a4a23e934d51270c49f864f30cd082bd", + "size_bytes": 12561 }, { "kind": "text", @@ -623,7 +623,7 @@ "kind": "text", "lines": 61, "path": "docs/PUBLISHING.md", - "sha256": "2ba14ec0d8842e8a2551d7e962c8e82445b1737c3773ce4033f3ee2441c70c83", + "sha256": "d8e887b3cf2af8c3688c71d9bb535f90911d354dfc31d09121e4b1031dbdfa19", "size_bytes": 2279 }, { @@ -642,16 +642,16 @@ }, { "kind": "text", - "lines": 66, + "lines": 92, "path": "docs/RELEASE_NOTES_1.3.0.md", - "sha256": "e535f31438a58973f619166d79c94b2f1fc9ceb2f07471a99ce469716d2da8ee", - "size_bytes": 4011 + "sha256": "43626199bdc76f4b30c9b57b9630687df3b76307db2e45e8af711b39e7a78f25", + "size_bytes": 6059 }, { "kind": "text", "lines": 14, "path": "docs/RELEASE_PROCESS.md", - "sha256": "174f022988e4c49dca9c56df58aeb9cd65109966d53467954347ba332b1d9145", + "sha256": "e6ab1c94eecadb55d6cdf6eb040ed312ace2f74d5818f4bbadb1df3f55eb9196", "size_bytes": 973 }, { @@ -670,10 +670,10 @@ }, { "kind": "text", - "lines": 104, + "lines": 142, "path": "docs/TEST_REPORT.md", - "sha256": "b6614e4f8edccefdd2a3f7520bd2120822b780a35ac3bd95cf2a6c290f41ce2e", - "size_bytes": 6305 + "sha256": "881bf725b8338853a82726d7627feedbfaefa21e4bd88e79590cb84247abeda1", + "size_bytes": 8779 }, { "kind": "text", @@ -700,126 +700,126 @@ "kind": "text", "lines": 68, "path": "documents/ADMIN_GUIDE.md", - "sha256": "ccc958123954f49d630b65022b66e54e915450a8d23e3d7374688a2a684b5dbb", + "sha256": "dd61ab74c2b54cb6e637d5097c789a44c792feba592a591a5d1348a587625f4f", "size_bytes": 3855 }, { "kind": "text", "lines": 44, "path": "documents/BASELINE.md", - "sha256": "ac3b47c424c89d0362ec9f241c81ad2307be9f5049312b8c9e79c5f8948bbee8", + "sha256": "5a8adbcc64d6ea48c11dbdb04f238caa51b621475b8df3993e4138717195b236", "size_bytes": 2050 }, { "kind": "text", "lines": 209, "path": "documents/DOCUMENTATION_MANIFEST.json", - "sha256": "75b5a206ca0cf3b76eed2351b553984e076d33549c0fdbe98e41307d69764f44", + "sha256": "c6cc10f50dbc600cf1f08a49b473b8aa456367bd30b8b1d6cab2b84da8263fbb", "size_bytes": 7880 }, { "kind": "text", "lines": 76, "path": "documents/DOCUMENTATION_POLICY.md", - "sha256": "bf7d3a50a4908143bf6b79d158872e364dab1966c25a171b2dc463cd94e0ee71", + "sha256": "7786aef25b75254c38870b61329b9ca8ccdf367244ef8a97a0f13f692697879e", "size_bytes": 3189 }, { "kind": "text", "lines": 76, "path": "documents/HOW_TO_USE.md", - "sha256": "74e1ba6d9794773d53fdc101e249f1cf6358571be037a53283c62cde95a23522", + "sha256": "0dd3e9d1d352ddb152180af3858afbec7f89b0aeb6b5a39881455e0ecd661655", "size_bytes": 3253 }, { "kind": "text", "lines": 53, "path": "documents/README.md", - "sha256": "277026a111f098ea780a005d767dd8427953bc90a6ca887a87314f576f4c0ede", + "sha256": "fd28dd001bd954ae9d635fe695beb799e6f40ce8990e977e5449c804352175b4", "size_bytes": 3693 }, { "kind": "text", "lines": 82, "path": "documents/USER_MANUAL.md", - "sha256": "629af8a82133856a9d100856992b43be4ce2d185c387b06c7f300e0ad008aa0c", + "sha256": "374ee5a683de0098b59dc39b25efdf6a04efdd7e33873517792160093f6fb6f2", "size_bytes": 4218 }, { "kind": "text", "lines": 46, "path": "documents/design/ACCESSIBILITY_SPECIFICATION.md", - "sha256": "a09c0a02ea09c5670479d6f8435462cc40a39e6151a21e4458874831d9934bd0", + "sha256": "8f31425f3cdec0165a4d0e6246ea68a770a898aed919ae01ac39da082326c11b", "size_bytes": 2187 }, { "kind": "text", "lines": 55, "path": "documents/design/COMPONENT_MATRIX.md", - "sha256": "403c550dea587811ccd333bf5991adde6e8d1578811d95a7db56184816463672", + "sha256": "b3c29ce563f1e794a22e60868dc47afd1efda6c20c27b56a05638e9288311b81", "size_bytes": 3465 }, { "kind": "text", "lines": 52, "path": "documents/design/FUTURE_UI_ROADMAP.md", - "sha256": "562e0c4982f598776a353a34b9e6f2108fe428a97946b882a0cce0e647310a5e", + "sha256": "c08173fb7454572a326b2aeccb3706531113fbd4c635239242e283630bc8d2e5", "size_bytes": 1567 }, { "kind": "text", "lines": 48, "path": "documents/design/IMPLEMENTATION_STATUS.md", - "sha256": "928640f9b01083968f0db0d03c0f2d8d4e734b43c7ab6357509a3c92a5ae4a41", + "sha256": "61ed02ae43021e603a2fdc2b9cf0b91414a213b2bec14c4917b8cf0da44b6e02", "size_bytes": 2516 }, { "kind": "text", "lines": 51, "path": "documents/design/RESPONSIVE_SPECIFICATION.md", - "sha256": "df937d87f53f2ea9fb3a8927e35263c4ecc3dbfd4de9e43a4f5dc61c984d6e15", + "sha256": "fcc959dd904bece5f2b481b5f5e77ec8af349136c3d750b56bc41ac1547e0280", "size_bytes": 2027 }, { "kind": "text", "lines": 62, "path": "documents/design/SCREEN_CATALOG.md", - "sha256": "895a17622c828fe240b8c8cdbd318a306563251c40fd9b031ecc8902d6eb4c82", + "sha256": "36db5fa837255ef66f98edc25cfeb599d704a079071338739e5b14965584f9f9", "size_bytes": 3292 }, { "kind": "text", "lines": 89, "path": "documents/design/UI_FOUNDATION.md", - "sha256": "b8bab951b3d5ae666d35ef86653e32682c1e8dc7450b49f0f95e05e0f72766c9", + "sha256": "0b3184ec7e1f9133f0f80360698d2abf04ecf9cc9df6cdfb58350aa07d77ecc2", "size_bytes": 3758 }, { "kind": "text", "lines": 59, "path": "documents/phases/PHASE-000-BASELINE.md", - "sha256": "1ee7772711cd7c2d7ba9c5f4188a31f093c800e6c25d869e9f0f544368e0b50e", + "sha256": "6970ddf63b8c2aa45f1d6f8c62244f25b4aaf09f00922acfa83947a5184e0129", "size_bytes": 2543 }, { "kind": "text", "lines": 68, "path": "documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md", - "sha256": "7e0e2a0539cfc3215cae7aeb9bb93fe1b249bcd16155ef2260dc85e4058c4c6e", + "sha256": "204a4a66e2017081242d15b15d01481dddcde6cdcd269cda43f477e0d7ae0f01", "size_bytes": 2770 }, { "kind": "text", "lines": 98, "path": "documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md", - "sha256": "86f152eed2a86867667ee25ed82ea1293c36ae91af11a7c49b562576339ff4ec", + "sha256": "450c51bf90048366e91615aecefb366247fa0b3a13a9eea7a1039a3ca3e4718d", "size_bytes": 5493 }, { "kind": "text", "lines": 86, "path": "documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md", - "sha256": "a294f0cc03875fa95ba88feac9f460dd41e714fe2f06503f18365edef0e637ca", + "sha256": "b2e0b9e88d6410d20e916ea420b1151fb9d014dc253177dc5a71b2274b47b822", "size_bytes": 5055 }, { @@ -867,10 +867,10 @@ }, { "kind": "text", - "lines": 66, + "lines": 76, "path": "mailbox-app/CHANGELOG.md", - "sha256": "30b80318b9c7df92a597d56e4699816d5c974768c06b1443613dcd1dd20c85b5", - "size_bytes": 4149 + "sha256": "c95cbde106aec016141e31d445e4f0b0f091be7a8b66f894d47812ea98ad222e", + "size_bytes": 4864 }, { "kind": "text", @@ -3161,7 +3161,7 @@ "kind": "text", "lines": 80, "path": "mailbox-app/pyproject.toml", - "sha256": "f08792aae7eb9efabe59112ea8707353f781cf691aefba3a5b52b46d72df4353", + "sha256": "44bd9c76b1cd6a108c3d217871936ff428e55fa2876b41f2f72e34ed8a99b1cd", "size_bytes": 2411 }, { @@ -3175,7 +3175,7 @@ "kind": "text", "lines": 24, "path": "mailbox-app/requirements/constraints.txt", - "sha256": "630380654b621e01b2b958c3a61922b5bf66ec7413fc48d460b49545fd9e65c7", + "sha256": "2e80e4879e04939f363b13b54ab6aa59a8a79b27f65d5d1546f8f24219423efd", "size_bytes": 448 }, { @@ -3189,7 +3189,7 @@ "kind": "text", "lines": 17, "path": "mailbox-app/requirements/locked.txt", - "sha256": "fc2a49c85d73c11ca6702c7c8ed91570b3aa0d30e0b7026e9b6bfc008f89b24a", + "sha256": "283b4df38fc1dbc06538ebb55eb3de7c078caac6abbf253c8a07d44ea9a0f3e9", "size_bytes": 358 }, { @@ -4032,7 +4032,7 @@ }, { "kind": "text", - "lines": 162, + "lines": 171, "path": "mailbox-app/tests/security/test_deployment_assets.py", "python": { "classes": [], @@ -4047,6 +4047,7 @@ "test_release_pins_and_verifies_django_security_patch", "test_release_source_cannot_be_the_live_application_tree", "test_restore_preserves_service_readable_environment_and_quiesces_postfix", + "test_sqlparse_security_release_is_pinned_consistently", "test_systemd_runtime_directories_are_isolated", "test_ui_reserves_system_addresses", "test_v1_2_1_security_hotfix_pins_and_bleach_scope", @@ -4059,8 +4060,8 @@ ], "methods": [] }, - "sha256": "79f339a39ff17d98f388430bdb2932393876ff999b86e511d53f647fa5dd7ed0", - "size_bytes": 6933 + "sha256": "eb134d939bba5c099d0412c4825ac97c6222956522537439a52b02fae916d013", + "size_bytes": 7228 }, { "kind": "text", @@ -4683,7 +4684,7 @@ }, { "kind": "text", - "lines": 380, + "lines": 397, "path": "scripts/forensic_audit.py", "python": { "classes": [], @@ -4702,14 +4703,15 @@ "os", "pathlib", "re", + "shell_runtime", "subprocess", "sys", "tempfile" ], "methods": [] }, - "sha256": "98e78dada595005bd3fa0095f11a8a9c1ed642b21d6fd1e8cf973de9e1492b12", - "size_bytes": 13836 + "sha256": "3615ebd13d33786631d50b025240f4fca85005eee84e27183450c1958ed7ffbf", + "size_bytes": 14391 }, { "kind": "text", @@ -4869,6 +4871,42 @@ "sha256": "4b591c23da8ba83f90b1e315406af74c40bf58c6a991cb3d18124ab198154e99", "size_bytes": 1667 }, + { + "kind": "text", + "lines": 192, + "path": "scripts/shell_runtime.py", + "python": { + "classes": [], + "functions": [ + "_add_candidate", + "_bash_path", + "_candidate_bashes", + "_probe", + "_python3_bridge_bash_env", + "_shell_single_quote", + "_windows_git_bash_candidates", + "bash_environment", + "bash_script_command", + "bash_syntax_command", + "resolve_bash", + "script_argument" + ], + "imports": [ + "__future__", + "atexit", + "functools", + "os", + "pathlib", + "shutil", + "subprocess", + "sys", + "tempfile" + ], + "methods": [] + }, + "sha256": "20e15ae4b227e01de56f7a386936aef67a97d748c1e1a374c41f464cf4d9d651", + "size_bytes": 6826 + }, { "kind": "text", "lines": 136, @@ -4929,7 +4967,7 @@ }, { "kind": "text", - "lines": 133, + "lines": 153, "path": "scripts/test_installer.py", "python": { "classes": [], @@ -4940,18 +4978,18 @@ ], "imports": [ "__future__", - "os", "pathlib", + "shell_runtime", "subprocess" ], "methods": [] }, - "sha256": "a1aa7a67b450f99828cef66847a7d73769b77aea8993a6995418b80c2433c2d6", - "size_bytes": 6691 + "sha256": "9e5d4fe960b57ded16295514a2e451862a4f90ce84ea6a159d2d442a2857f141", + "size_bytes": 7354 }, { "kind": "text", - "lines": 110, + "lines": 116, "path": "scripts/test_operations.py", "python": { "classes": [], @@ -4963,15 +5001,15 @@ "imports": [ "__future__", "ipaddress", - "os", "pathlib", "re", + "shell_runtime", "subprocess" ], "methods": [] }, - "sha256": "10185ee3d8fb2a805c9b04787f7326dcd5c65db86a9965ed7fcbb3aa3d23ae23", - "size_bytes": 3896 + "sha256": "5f1917f0b6ecd769c4caecf0a7bb5cff8ae13b847d2f097ecbfa49630d9ed685", + "size_bytes": 4332 }, { "kind": "text", @@ -5052,7 +5090,7 @@ "size_bytes": 6930 } ], - "release_version": "1.3.0-rc.2", + "release_version": "1.3.0-rc.4", "schema_version": 1, "scope": "All maintained repository files excluding generated archives, caches, build output and this inventory file itself.", "self_entry": { @@ -5061,15 +5099,15 @@ }, "summary": { "binary_files": 25, - "files": 402, + "files": 403, "python_classes": 73, - "python_files": 143, - "python_functions": 392, + "python_files": 144, + "python_functions": 405, "python_methods": 61, "shell_files": 13, "shell_functions": 31, - "text_files": 377, - "total_bytes": 31637349, - "total_text_lines": 26245 + "text_files": 378, + "total_bytes": 31655177, + "total_text_lines": 26603 } } diff --git a/docs/PUBLISHING.md b/docs/PUBLISHING.md index 3724e5b..e163221 100644 --- a/docs/PUBLISHING.md +++ b/docs/PUBLISHING.md @@ -12,10 +12,10 @@ This repository is prepared for public hosting on GitHub, GitLab, Codeberg, or a 4. Build and verify the deterministic release archive: ```bash - python scripts/build_release.py --root . --version 1.3.0-rc.2 + python scripts/build_release.py --root . --version 1.3.0-rc.4 python scripts/verify_release.py \ - dist/mailstack-1.3.0-rc.2-source.zip \ - --checksum dist/mailstack-1.3.0-rc.2-source.zip.sha256 + dist/mailstack-1.3.0-rc.4-source.zip \ + --checksum dist/mailstack-1.3.0-rc.4-source.zip.sha256 ``` 5. Complete the clean Ubuntu 24.04 acceptance checklist before promoting a release candidate to production-ready. @@ -27,7 +27,7 @@ From the audited repository root: ```bash git init git add . -git commit -m "Release MailStack 1.3.0-rc.2" +git commit -m "Release MailStack 1.3.0-rc.4" git branch -M main git remote add origin git push -u origin main @@ -36,14 +36,14 @@ git push -u origin main Create an annotated release-candidate tag only after CI passes: ```bash -git tag -a v1.3.0-rc.2 -m "MailStack 1.3.0 RC2" -git push origin v1.3.0-rc.2 +git tag -a v1.3.0-rc.4 -m "MailStack 1.3.0 RC4" +git push origin v1.3.0-rc.4 ``` Attach these files to the forge release: -- `mailstack-1.3.0-rc.2-source.zip` -- `mailstack-1.3.0-rc.2-source.zip.sha256` +- `mailstack-1.3.0-rc.4-source.zip` +- `mailstack-1.3.0-rc.4-source.zip.sha256` - `docs/RELEASE_NOTES_1.3.0.md` - `docs/FORENSIC_AUDIT_REPORT.md` diff --git a/docs/RELEASE_NOTES_1.3.0.md b/docs/RELEASE_NOTES_1.3.0.md index 3cc85e0..97ac9c4 100644 --- a/docs/RELEASE_NOTES_1.3.0.md +++ b/docs/RELEASE_NOTES_1.3.0.md @@ -1,12 +1,39 @@ -# MailStack 1.3.0 RC2 release notes +# MailStack 1.3.0 RC4 release notes ## Purpose -Version 1.3.0 RC2 is a reliability-hardening release candidate built from the frozen 1.3.0 RC1 -baseline. It incorporates only the installation, partial-recovery, official-verification, MariaDB -qualification, and inbound SMTP/LMTP fixes reproduced during the first live Ubuntu 24.04 staging -acceptance campaign. Existing MailStack product features, UI/UX, routes, data model, receive-only -scope, and deployment identifiers remain preserved. +Version 1.3.0 RC4 is a cross-platform audit-tooling maintenance successor to RC3. It preserves the +frozen 1.3.0 RC1 product baseline, all approved RC2 installation/recovery/inbound-delivery fixes, and +the RC3 `sqlparse` 0.6.0 security update. RC4 changes only repository-level Bash runtime discovery +used by installer, operations, and forensic contract tooling so Windows validation does not blindly +invoke a broken WSL `bash.exe` launcher when Git Bash is available. Existing MailStack product +features, UI/UX, routes, data model, receive-only scope, runtime dependencies, and deployment +identifiers remain preserved. + +## Local Windows audit fix in RC4 + +A Windows CMD qualification run showed documentation, design, UI-foundation, inventory, and template +gates passing, while installer/operations shell checks failed before any MailStack shell code ran. +The `bash` command resolved to the Windows WSL launcher, which failed to attach Docker Desktop's WSL2 +`ext4.vhdx` with `E_ACCESSDENIED`. The forensic audit then repeated the same environment failure for +every shell syntax check, creating 16 cascading findings from one unusable Bash runtime. + +RC4 introduces verified Bash discovery for repository tooling. On Windows it prefers Git for Windows +Bash, supports an explicit `BASH_EXECUTABLE` override, probes candidates before use, and passes +repository-relative POSIX script paths. Linux/GitHub Actions continue to use the normal system Bash. +If no usable Bash exists, the tools fail once with an actionable runtime diagnostic instead of +misclassifying WSL startup failure as multiple MailStack shell syntax defects. + +## Security fix carried from RC3 + +GitHub Actions run `32053931714` passed the source forensic audit, documentation, design, inventory, +deployment-template, installer, and operations gates, then stopped at the blocking online dependency +audit. `pip-audit` reported four vulnerabilities in `sqlparse==0.5.5`: CVE-2026-71491, +CVE-2026-59894, CVE-2026-59893, and CVE-2026-54284, with 0.6.0 as the fixed version. + +RC3 introduced, and RC4 preserves, the existing transitive runtime pin to `sqlparse==0.6.0` in both the production lock and +constraints. Django remains 5.2.16 and declares `sqlparse>=0.3.1`; sqlparse 0.6.0 requires Python +3.10+, while MailStack remains fixed to Python 3.12. No new dependency is introduced. ## Fixed in RC2 @@ -33,7 +60,7 @@ scope, and deployment identifiers remain preserved. ## Compatibility - No database migration. -- No dependency upgrade or new dependency. +- One existing transitive dependency is security-upgraded: `sqlparse` 0.5.5 → 0.6.0; no new dependency is introduced. - No route, template, CSS, JavaScript, UI page, authorization, mailbox, message, public-site, or contact-workflow redesign. - No SMTP submission, IMAP, POP3, reply, forward, sent, draft, or public-registration feature. @@ -52,7 +79,7 @@ Postfix queue, ingested, and displayed by the application. ## Release qualification -RC2 must pass the full documentation, installer, operations, template, Ruff, Bandit, Django, +RC4 must pass the full documentation, installer, operations, template, Ruff, Bandit, Django, coverage, forensic, deterministic-release, and CI gates. Clean Ubuntu 24.04 installation and real external SMTP/LMTP acceptance are required. Stable promotion remains blocked until backup/restore, restart-recovery, final security/legal, and release-owner acceptance are complete. @@ -62,5 +89,4 @@ restart-recovery, final security/legal, and release-owner acceptance are complet RC1 established the configurable Ubuntu 24.04 installer, MariaDB/Postfix/Dovecot/Nginx/systemd templates, reproducible source packaging, public governance/security documentation, Django 5.2.16 security pin, protected user/documentation baseline, UI design intake, and shared application shell. -RC2 does not replace or redesign those foundations; it hardens the operational paths exercised by -staging acceptance. +RC4 does not replace or redesign those foundations; it preserves the RC2 operational hardening and RC3 dependency-security fix while hardening only cross-platform repository audit execution. diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md index edd4079..e0c962e 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/RELEASE_PROCESS.md @@ -7,7 +7,7 @@ 5. Run the online dependency audit in CI. 6. Test a clean installation on an isolated Ubuntu Server 24.04 LTS VPS. 7. Verify external inbound SMTP, login, authorization boundaries, live updates, safe HTML, and attachment downloads. -8. Build with `python scripts/build_release.py --root . --version 1.3.0-rc.2`. +8. Build with `python scripts/build_release.py --root . --version 1.3.0-rc.4`. 9. Verify the ZIP and checksum with `python scripts/verify_release.py`. 10. Publish the source ZIP, checksum, release notes, user-document manifest, and exact Git commit/tag. diff --git a/docs/TEST_REPORT.md b/docs/TEST_REPORT.md index e785991..618e3b4 100644 --- a/docs/TEST_REPORT.md +++ b/docs/TEST_REPORT.md @@ -1,8 +1,8 @@ -# Test report — MailStack 1.3.0 RC2 +# Test report — MailStack 1.3.0 RC3 **PHASE-003 verification date:** 2026-08-17 -## Application suite +## Last completed dependency-backed application suite (pre-RC3) ```text Collected: 196 @@ -13,6 +13,11 @@ Coverage: 94.99% Required coverage: 85% ``` +RC3 adds one focused deployment-security regression contract that requires both +`requirements/locked.txt` and `requirements/constraints.txt` to pin `sqlparse==0.6.0` and reject +the vulnerable 0.5.5 pin. The full RC3 collection/pass count is intentionally not claimed until the +dependency-backed GitHub Actions rerun completes. + Covered areas include authentication, user management, mailbox membership isolation, mailbox lifecycle, Postfix contract behavior, Maildir provisioning/ingestion, MIME parsing, HTML sanitization, attachment storage and authorization, duplicate handling, counters, live updates, reliability, security controls, responsive routes, deterministic user-document synchronization, draft blocking and feature-document policy enforcement. ## Additional automated gates @@ -44,20 +49,53 @@ Covered areas include authentication, user management, mailbox membership isolat | Shared UI foundation dependency-free contracts | PASS — 8 tests | | Shared shell Django functional tests | PASS — 7 focused tests | | Standalone contact-service Ruff/Bandit | Enforced by the final verifier, full forensic gate, and CI | -| Locked dependency advisory audit | PASS — no known vulnerabilities found | +| Locked dependency advisory audit | PENDING RC4 requalification — RC2 run `32053931714` found four sqlparse 0.5.5 vulnerabilities; RC3/RC4 pin 0.6.0 | | Canonical stored-entry release packaging and ZIP metadata verification | PASS | ## Runtime qualification -GitHub Actions run `30133728843` completed successfully on Ubuntu 24.04 with Python 3.12 for source safety, documentation, dependency audit, Ruff, Bandit, tests and coverage, contact tests, Django checks, shell syntax, the full forensic gate, deterministic release build and release verification. The qualifying source commit is `1e1737edea2e6c922265a15d8584b56671820c65`. +The last fully completed pre-PHASE-003 dependency-backed qualification remains GitHub Actions run +`30133728843` on Ubuntu 24.04 with Python 3.12 at commit +`1e1737edea2e6c922265a15d8584b56671820c65`. That historical evidence does not qualify RC4. + +For the PHASE-003 branch, GitHub Actions run `32053931714` at commit +`7a800eb9f0b6d0e3fa347f4d7f05b5ad88a5e18f` passed source safety, documentation, design, +forensic inventory, deployment-template, installer, and operations gates. It then failed at the +blocking dependency vulnerability audit, so Ruff, Bandit, Django tests/coverage, Django checks, +full forensic audit, and deterministic release steps were correctly skipped. ## Dependency advisory qualification -The network-enabled blocking `pip-audit` gate passed in GitHub Actions after Django was upgraded to 5.2.16. Dependency changes remain subject to `pip check`, the locked requirements contract and the blocking online advisory gate. +The failed RC2 `pip-audit` gate reported four vulnerabilities in `sqlparse==0.5.5`: +CVE-2026-71491, CVE-2026-59894, CVE-2026-59893, and CVE-2026-54284. Upstream sqlparse 0.6.0 is the +security release containing fixes for those issues. RC3 introduced and RC4 preserves `sqlparse==0.6.0` in both +`requirements/locked.txt` and `requirements/constraints.txt`. No advisory suppression is added. +RC4 must pass `pip check`, the network-enabled `pip-audit` gate, and every subsequent CI stage before +qualification. + +## RC4 Windows Bash-runtime qualification + +A Windows CMD run of the RC3 candidate passed documentation, managed-document, design, shared UI, +forensic inventory, deployment-template, and `git diff --check` gates. `test_installer.py`, +`test_operations.py`, and the shell portions of `forensic_audit.py` then failed before executing any +MailStack shell logic because the generic `bash` executable resolved to the WSL launcher. WSL failed +to attach Docker Desktop's `ext4.vhdx` with `E_ACCESSDENIED`. The forensic report's 16 findings were +therefore cascading manifestations of one local shell-runtime failure, not 16 independent source +defects. + +RC4 centralizes Bash discovery in `scripts/shell_runtime.py`. Windows prefers a probed Git for +Windows Bash runtime, supports `BASH_EXECUTABLE`, and falls back only to candidates that pass a +non-mutating startup probe. Installer plans and shell syntax checks use repository-relative POSIX +paths so the same contracts remain valid on Linux and Git Bash. A follow-up Windows qualification +showed that Git Bash correctly launched but did not expose a `python3` command even though the host +provided `python`. The RC4 harness now supplies a process-local `BASH_ENV` bridge that maps +installer-only `python3` calls to the exact Python interpreter running the test process. The bridge +is regression-tested and does not alter the production installer, Ubuntu's native `python3` contract, +or the developer machine. ## PHASE-003 installation/recovery reliability qualification -Dependency-free local qualification for 1.3.0-rc.2 passes the documentation index/manifest gate, +Dependency-free local qualification for 1.3.0-rc.4 passes the documentation index/manifest gate, documentation contract tests, design manifest and PNG integrity tests, shared UI foundation contracts, deployment-template rendering, installer contracts, operations contracts, Python compile, shell syntax, forensic inventory generation/check, and the structural forensic audit. The installer @@ -72,7 +110,7 @@ does not waive those gates. The live staging campaign that motivated PHASE-003 demonstrated real external Gmail delivery through Postfix and Dovecot LMTP into Maildir, queue drain after the static-userdb correction, ingestion, and -web-inbox visibility. A final clean installation from the exact RC2 source remains a release-candidate +web-inbox visibility. A final clean installation from the exact RC4 source remains a release-candidate acceptance requirement before stable promotion. ## Manual acceptance still required diff --git a/documents/ADMIN_GUIDE.md b/documents/ADMIN_GUIDE.md index e556f0c..c10c57c 100644 --- a/documents/ADMIN_GUIDE.md +++ b/documents/ADMIN_GUIDE.md @@ -4,7 +4,7 @@ title: MailStack Administrator Guide document_type: admin-guide audience: mailstack-administrators status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/BASELINE.md b/documents/BASELINE.md index 75e5ebe..bf71019 100644 --- a/documents/BASELINE.md +++ b/documents/BASELINE.md @@ -4,7 +4,7 @@ title: MailStack Baseline document_type: baseline audience: maintainers-and-operators status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 baseline_id: MAILSTACK-1.3.0-RC1-DOCS-BASELINE-001 source_commit: 1e1737edea2e6c922265a15d8584b56671820c65 diff --git a/documents/DOCUMENTATION_MANIFEST.json b/documents/DOCUMENTATION_MANIFEST.json index 40e87a3..3f0686d 100644 --- a/documents/DOCUMENTATION_MANIFEST.json +++ b/documents/DOCUMENTATION_MANIFEST.json @@ -9,10 +9,10 @@ "last_reviewed": "2026-08-17", "path": "documents/ADMIN_GUIDE.md", "phase_id": null, - "sha256": "ccc958123954f49d630b65022b66e54e915450a8d23e3d7374688a2a684b5dbb", + "sha256": "dd61ab74c2b54cb6e637d5097c789a44c792feba592a591a5d1348a587625f4f", "status": "active", "title": "MailStack Administrator Guide", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "maintainers-and-operators", @@ -21,10 +21,10 @@ "last_reviewed": "2026-08-17", "path": "documents/BASELINE.md", "phase_id": null, - "sha256": "ac3b47c424c89d0362ec9f241c81ad2307be9f5049312b8c9e79c5f8948bbee8", + "sha256": "5a8adbcc64d6ea48c11dbdb04f238caa51b621475b8df3993e4138717195b236", "status": "active", "title": "MailStack Baseline", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "contributors-and-maintainers", @@ -33,10 +33,10 @@ "last_reviewed": "2026-08-17", "path": "documents/DOCUMENTATION_POLICY.md", "phase_id": null, - "sha256": "bf7d3a50a4908143bf6b79d158872e364dab1966c25a171b2dc463cd94e0ee71", + "sha256": "7786aef25b75254c38870b61329b9ca8ccdf367244ef8a97a0f13f692697879e", "status": "active", "title": "Documentation Policy", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "mailbox-users-and-administrators", @@ -45,10 +45,10 @@ "last_reviewed": "2026-08-17", "path": "documents/HOW_TO_USE.md", "phase_id": null, - "sha256": "74e1ba6d9794773d53fdc101e249f1cf6358571be037a53283c62cde95a23522", + "sha256": "0dd3e9d1d352ddb152180af3858afbec7f89b0aeb6b5a39881455e0ecd661655", "status": "active", "title": "How to Use MailStack", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "mailbox-users", @@ -57,10 +57,10 @@ "last_reviewed": "2026-08-17", "path": "documents/USER_MANUAL.md", "phase_id": null, - "sha256": "629af8a82133856a9d100856992b43be4ce2d185c387b06c7f300e0ad008aa0c", + "sha256": "374ee5a683de0098b59dc39b25efdf6a04efdd7e33873517792160093f6fb6f2", "status": "active", "title": "MailStack User Manual", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -69,10 +69,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/ACCESSIBILITY_SPECIFICATION.md", "phase_id": null, - "sha256": "a09c0a02ea09c5670479d6f8435462cc40a39e6151a21e4458874831d9934bd0", + "sha256": "8f31425f3cdec0165a4d0e6246ea68a770a898aed919ae01ac39da082326c11b", "status": "active", "title": "MailStack Accessibility Specification", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -81,10 +81,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/COMPONENT_MATRIX.md", "phase_id": null, - "sha256": "403c550dea587811ccd333bf5991adde6e8d1578811d95a7db56184816463672", + "sha256": "b3c29ce563f1e794a22e60868dc47afd1efda6c20c27b56a05638e9288311b81", "status": "active", "title": "MailStack UI Component Matrix", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -93,10 +93,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/FUTURE_UI_ROADMAP.md", "phase_id": null, - "sha256": "562e0c4982f598776a353a34b9e6f2108fe428a97946b882a0cce0e647310a5e", + "sha256": "c08173fb7454572a326b2aeccb3706531113fbd4c635239242e283630bc8d2e5", "status": "active", "title": "MailStack Future UI Roadmap", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -105,10 +105,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/IMPLEMENTATION_STATUS.md", "phase_id": null, - "sha256": "928640f9b01083968f0db0d03c0f2d8d4e734b43c7ab6357509a3c92a5ae4a41", + "sha256": "61ed02ae43021e603a2fdc2b9cf0b91414a213b2bec14c4917b8cf0da44b6e02", "status": "active", "title": "MailStack UI Implementation Status", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -117,10 +117,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/RESPONSIVE_SPECIFICATION.md", "phase_id": null, - "sha256": "df937d87f53f2ea9fb3a8927e35263c4ecc3dbfd4de9e43a4f5dc61c984d6e15", + "sha256": "fcc959dd904bece5f2b481b5f5e77ec8af349136c3d750b56bc41ac1547e0280", "status": "active", "title": "MailStack Responsive Specification", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -129,10 +129,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/SCREEN_CATALOG.md", "phase_id": null, - "sha256": "895a17622c828fe240b8c8cdbd318a306563251c40fd9b031ecc8902d6eb4c82", + "sha256": "36db5fa837255ef66f98edc25cfeb599d704a079071338739e5b14965584f9f9", "status": "active", "title": "MailStack UI Screen Catalog", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "designers-developers-and-maintainers", @@ -141,10 +141,10 @@ "last_reviewed": "2026-08-17", "path": "documents/design/UI_FOUNDATION.md", "phase_id": null, - "sha256": "b8bab951b3d5ae666d35ef86653e32682c1e8dc7450b49f0f95e05e0f72766c9", + "sha256": "0b3184ec7e1f9133f0f80360698d2abf04ecf9cc9df6cdfb58350aa07d77ecc2", "status": "active", "title": "MailStack UI Foundation", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "users-operators-and-maintainers", @@ -153,10 +153,10 @@ "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-000-BASELINE.md", "phase_id": "PHASE-000", - "sha256": "1ee7772711cd7c2d7ba9c5f4188a31f093c800e6c25d869e9f0f544368e0b50e", + "sha256": "6970ddf63b8c2aa45f1d6f8c62244f25b4aaf09f00922acfa83947a5184e0129", "status": "active", "title": "Documentation and Feature Baseline", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "users-operators-designers-and-maintainers", @@ -165,10 +165,10 @@ "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md", "phase_id": "PHASE-001", - "sha256": "7e0e2a0539cfc3215cae7aeb9bb93fe1b249bcd16155ef2260dc85e4058c4c6e", + "sha256": "204a4a66e2017081242d15b15d01481dddcde6cdcd269cda43f477e0d7ae0f01", "status": "active", "title": "UI Design Intake Baseline", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "users-operators-designers-and-maintainers", @@ -177,10 +177,10 @@ "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md", "phase_id": "PHASE-002", - "sha256": "86f152eed2a86867667ee25ed82ea1293c36ae91af11a7c49b562576339ff4ec", + "sha256": "450c51bf90048366e91615aecefb366247fa0b3a13a9eea7a1039a3ca3e4718d", "status": "active", "title": "Shared UI Foundation and Application Shell", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" }, { "audience": "users-operators-and-maintainers", @@ -189,14 +189,14 @@ "last_reviewed": "2026-08-17", "path": "documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md", "phase_id": "PHASE-003", - "sha256": "a294f0cc03875fa95ba88feac9f460dd41e714fe2f06503f18365edef0e637ca", + "sha256": "b2e0b9e88d6410d20e916ea420b1151fb9d014dc253177dc5a71b2274b47b822", "status": "active", "title": "Installation, Recovery and Inbound Delivery Reliability", - "version": "1.3.0-rc.2" + "version": "1.3.0-rc.4" } ], "project": "MailStack", - "release_version": "1.3.0-rc.2", + "release_version": "1.3.0-rc.4", "schema_version": 1, "scope": "Managed user, administrator, how-to, policy, baseline, and phase Markdown documents.", "summary": { diff --git a/documents/DOCUMENTATION_POLICY.md b/documents/DOCUMENTATION_POLICY.md index c2f8104..577ccf6 100644 --- a/documents/DOCUMENTATION_POLICY.md +++ b/documents/DOCUMENTATION_POLICY.md @@ -4,7 +4,7 @@ title: Documentation Policy document_type: documentation-policy audience: contributors-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/HOW_TO_USE.md b/documents/HOW_TO_USE.md index 7eb8a1e..c7f8cee 100644 --- a/documents/HOW_TO_USE.md +++ b/documents/HOW_TO_USE.md @@ -4,7 +4,7 @@ title: How to Use MailStack document_type: how-to audience: mailbox-users-and-administrators status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/README.md b/documents/README.md index f88775f..0e3747e 100644 --- a/documents/README.md +++ b/documents/README.md @@ -27,22 +27,22 @@ required documentation update. | Document | Type | Audience | Status | Version | |---|---|---|---|---| -| [MailStack Administrator Guide](ADMIN_GUIDE.md) | Admin Guide | mailstack-administrators | active | 1.3.0-rc.2 | -| [MailStack Baseline](BASELINE.md) | Baseline | maintainers-and-operators | active | 1.3.0-rc.2 | -| [MailStack Accessibility Specification](design/ACCESSIBILITY_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack Future UI Roadmap](design/FUTURE_UI_ROADMAP.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack Responsive Specification](design/RESPONSIVE_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack UI Component Matrix](design/COMPONENT_MATRIX.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack UI Foundation](design/UI_FOUNDATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack UI Implementation Status](design/IMPLEMENTATION_STATUS.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack UI Screen Catalog](design/SCREEN_CATALOG.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.2 | -| [Documentation Policy](DOCUMENTATION_POLICY.md) | Documentation Policy | contributors-and-maintainers | active | 1.3.0-rc.2 | -| [How to Use MailStack](HOW_TO_USE.md) | How To | mailbox-users-and-administrators | active | 1.3.0-rc.2 | -| [Documentation and Feature Baseline](phases/PHASE-000-BASELINE.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.2 | -| [Installation, Recovery and Inbound Delivery Reliability](phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.2 | -| [Shared UI Foundation and Application Shell](phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.2 | -| [UI Design Intake Baseline](phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.2 | -| [MailStack User Manual](USER_MANUAL.md) | User Manual | mailbox-users | active | 1.3.0-rc.2 | +| [MailStack Administrator Guide](ADMIN_GUIDE.md) | Admin Guide | mailstack-administrators | active | 1.3.0-rc.4 | +| [MailStack Baseline](BASELINE.md) | Baseline | maintainers-and-operators | active | 1.3.0-rc.4 | +| [MailStack Accessibility Specification](design/ACCESSIBILITY_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack Future UI Roadmap](design/FUTURE_UI_ROADMAP.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack Responsive Specification](design/RESPONSIVE_SPECIFICATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack UI Component Matrix](design/COMPONENT_MATRIX.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack UI Foundation](design/UI_FOUNDATION.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack UI Implementation Status](design/IMPLEMENTATION_STATUS.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack UI Screen Catalog](design/SCREEN_CATALOG.md) | Design Reference | designers-developers-and-maintainers | active | 1.3.0-rc.4 | +| [Documentation Policy](DOCUMENTATION_POLICY.md) | Documentation Policy | contributors-and-maintainers | active | 1.3.0-rc.4 | +| [How to Use MailStack](HOW_TO_USE.md) | How To | mailbox-users-and-administrators | active | 1.3.0-rc.4 | +| [Documentation and Feature Baseline](phases/PHASE-000-BASELINE.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.4 | +| [Installation, Recovery and Inbound Delivery Reliability](phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md) | Phase | users-operators-and-maintainers | active | 1.3.0-rc.4 | +| [Shared UI Foundation and Application Shell](phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.4 | +| [UI Design Intake Baseline](phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md) | Phase | users-operators-designers-and-maintainers | active | 1.3.0-rc.4 | +| [MailStack User Manual](USER_MANUAL.md) | User Manual | mailbox-users | active | 1.3.0-rc.4 | ## Scope boundary diff --git a/documents/USER_MANUAL.md b/documents/USER_MANUAL.md index b36ed74..673f044 100644 --- a/documents/USER_MANUAL.md +++ b/documents/USER_MANUAL.md @@ -4,7 +4,7 @@ title: MailStack User Manual document_type: user-manual audience: mailbox-users status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/ACCESSIBILITY_SPECIFICATION.md b/documents/design/ACCESSIBILITY_SPECIFICATION.md index b6f5921..20ea3a6 100644 --- a/documents/design/ACCESSIBILITY_SPECIFICATION.md +++ b/documents/design/ACCESSIBILITY_SPECIFICATION.md @@ -4,7 +4,7 @@ title: MailStack Accessibility Specification document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/COMPONENT_MATRIX.md b/documents/design/COMPONENT_MATRIX.md index 287c3f0..4298da5 100644 --- a/documents/design/COMPONENT_MATRIX.md +++ b/documents/design/COMPONENT_MATRIX.md @@ -4,7 +4,7 @@ title: MailStack UI Component Matrix document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/FUTURE_UI_ROADMAP.md b/documents/design/FUTURE_UI_ROADMAP.md index 424d4a9..e0a83a7 100644 --- a/documents/design/FUTURE_UI_ROADMAP.md +++ b/documents/design/FUTURE_UI_ROADMAP.md @@ -4,7 +4,7 @@ title: MailStack Future UI Roadmap document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/IMPLEMENTATION_STATUS.md b/documents/design/IMPLEMENTATION_STATUS.md index bc351c9..ae75755 100644 --- a/documents/design/IMPLEMENTATION_STATUS.md +++ b/documents/design/IMPLEMENTATION_STATUS.md @@ -4,7 +4,7 @@ title: MailStack UI Implementation Status document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/RESPONSIVE_SPECIFICATION.md b/documents/design/RESPONSIVE_SPECIFICATION.md index d109acb..c87b628 100644 --- a/documents/design/RESPONSIVE_SPECIFICATION.md +++ b/documents/design/RESPONSIVE_SPECIFICATION.md @@ -4,7 +4,7 @@ title: MailStack Responsive Specification document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/SCREEN_CATALOG.md b/documents/design/SCREEN_CATALOG.md index 5671f22..f698e33 100644 --- a/documents/design/SCREEN_CATALOG.md +++ b/documents/design/SCREEN_CATALOG.md @@ -4,7 +4,7 @@ title: MailStack UI Screen Catalog document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/design/UI_FOUNDATION.md b/documents/design/UI_FOUNDATION.md index 32b4ace..c5f31c0 100644 --- a/documents/design/UI_FOUNDATION.md +++ b/documents/design/UI_FOUNDATION.md @@ -4,7 +4,7 @@ title: MailStack UI Foundation document_type: design-reference audience: designers-developers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 --- diff --git a/documents/phases/PHASE-000-BASELINE.md b/documents/phases/PHASE-000-BASELINE.md index fc4d4a5..e790b28 100644 --- a/documents/phases/PHASE-000-BASELINE.md +++ b/documents/phases/PHASE-000-BASELINE.md @@ -4,7 +4,7 @@ title: Documentation and Feature Baseline document_type: phase audience: users-operators-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 phase_id: PHASE-000 --- diff --git a/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md b/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md index 55612dc..411eecf 100644 --- a/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md +++ b/documents/phases/PHASE-001-UI-DESIGN-INTAKE-BASELINE.md @@ -4,7 +4,7 @@ title: UI Design Intake Baseline document_type: phase audience: users-operators-designers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 phase_id: PHASE-001 --- diff --git a/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md b/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md index 1008f41..e7320b4 100644 --- a/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md +++ b/documents/phases/PHASE-002-SHARED-UI-FOUNDATION-AND-APPLICATION-SHELL.md @@ -4,7 +4,7 @@ title: Shared UI Foundation and Application Shell document_type: phase audience: users-operators-designers-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 phase_id: PHASE-002 --- diff --git a/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md b/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md index 630e73c..3e1428c 100644 --- a/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md +++ b/documents/phases/PHASE-003-INSTALLATION-RECOVERY-AND-INBOUND-DELIVERY-RELIABILITY.md @@ -4,7 +4,7 @@ title: Installation, Recovery and Inbound Delivery Reliability document_type: phase audience: users-operators-and-maintainers status: active -version: 1.3.0-rc.2 +version: 1.3.0-rc.4 last_reviewed: 2026-08-17 phase_id: PHASE-003 --- diff --git a/mailbox-app/CHANGELOG.md b/mailbox-app/CHANGELOG.md index d56190f..c7a5ba6 100644 --- a/mailbox-app/CHANGELOG.md +++ b/mailbox-app/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.3.0-rc.4 — Cross-platform audit tooling maintenance + +- Added portable Bash runtime discovery for repository-level installer, operations, and forensic audit tooling on Windows while preserving Linux CI behavior. +- No application model, migration, route, UI, mailbox, ingestion, LMTP, authorization, or runtime dependency behavior changed. + +## 1.3.0-rc.3 — Dependency security maintenance + +- Upgraded the transitive runtime lock for `sqlparse` from 0.5.5 to 0.6.0 to resolve CVE-2026-71491, CVE-2026-59894, CVE-2026-59893, and CVE-2026-54284 reported by the blocking GitHub Actions `pip-audit` gate. +- No application model, migration, route, UI, mailbox, ingestion, LMTP, or authorization behavior changed. + ## 1.3.0-rc.2 — Installation and inbound-delivery reliability - Added explicit idempotent repair behavior for the initial administrator and reserved system-mailbox bootstrap commands while preserving strict default duplicate rejection. diff --git a/mailbox-app/pyproject.toml b/mailbox-app/pyproject.toml index 58c2a13..73ec3cd 100644 --- a/mailbox-app/pyproject.toml +++ b/mailbox-app/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mailstack" -version = "1.3.0rc2" +version = "1.3.0rc4" description = "MailStack self-hosted receive-only mail server and shared team inbox" authors = [{ name = "Vib Tools" }] maintainers = [{ name = "Vib Tools" }] diff --git a/mailbox-app/requirements/constraints.txt b/mailbox-app/requirements/constraints.txt index b8da6f3..1fe0a5f 100644 --- a/mailbox-app/requirements/constraints.txt +++ b/mailbox-app/requirements/constraints.txt @@ -17,7 +17,7 @@ pytest-cov==7.0.0 pytest-django==4.12.0 python-dotenv==1.2.2 ruff==0.15.17 -sqlparse==0.5.5 +sqlparse==0.6.0 webencodings==0.5.1 whitenoise==6.11.0 diff --git a/mailbox-app/requirements/locked.txt b/mailbox-app/requirements/locked.txt index 9cd5655..83bf6b9 100644 --- a/mailbox-app/requirements/locked.txt +++ b/mailbox-app/requirements/locked.txt @@ -10,7 +10,7 @@ gunicorn==25.1.0 packaging==26.2 pycparser==3.0 python-dotenv==1.2.2 -sqlparse==0.5.5 +sqlparse==0.6.0 webencodings==0.5.1 whitenoise==6.11.0 diff --git a/mailbox-app/tests/security/test_deployment_assets.py b/mailbox-app/tests/security/test_deployment_assets.py index 758877a..ff34256 100644 --- a/mailbox-app/tests/security/test_deployment_assets.py +++ b/mailbox-app/tests/security/test_deployment_assets.py @@ -122,6 +122,15 @@ def test_release_pins_and_verifies_django_security_patch(): assert '"Django": "5.2.16"' in verify +def test_sqlparse_security_release_is_pinned_consistently(): + locked = read("requirements/locked.txt") + constraints = read("requirements/constraints.txt") + + for source in (locked, constraints): + assert "sqlparse==0.6.0" in source + assert "sqlparse==0.5.5" not in source + + def test_release_source_cannot_be_the_live_application_tree(): deploy = read("scripts/deploy_application.sh") preflight = read("scripts/preflight_v1_2_1.sh") diff --git a/scripts/forensic_audit.py b/scripts/forensic_audit.py index 61eccf6..051350e 100644 --- a/scripts/forensic_audit.py +++ b/scripts/forensic_audit.py @@ -6,14 +6,16 @@ import ast import hashlib import ipaddress -import json import os +import json import re import subprocess import sys import tempfile from pathlib import Path +from shell_runtime import bash_environment, bash_script_command, bash_syntax_command, resolve_bash + REQUIRED = { "README.md", "VERSION", @@ -37,6 +39,7 @@ "docs/FORENSIC_FILE_INVENTORY.json", "scripts/test_installer.py", "scripts/test_operations.py", + "scripts/shell_runtime.py", "scripts/validate_templates.py", "docs/FEATURE_MATRIX.md", "docs/FORENSIC_AUDIT_REPORT.md", @@ -127,7 +130,6 @@ r"(? bool: return any(part in BLOCKED_DIRS or part in {"dist", "artifacts"} for part in relative.parts) -def run(command: list[str], cwd: Path) -> tuple[int, str]: - completed = subprocess.run(command, cwd=cwd, text=True, capture_output=True) +def run(command: list[str], cwd: Path, *, bash_runtime: bool = False) -> tuple[int, str]: + completed = subprocess.run( + command, + cwd=cwd, + text=True, + capture_output=True, + env=bash_environment() if bash_runtime else None, + ) return completed.returncode, (completed.stdout + completed.stderr).strip() @@ -244,7 +252,7 @@ def main() -> int: findings.append(f"JSON_SYNTAX:{relative}:{exc.lineno}") elif path.suffix == ".sh" or path.name == "install.sh": shell_count += 1 - code, output = run([BASH, "-n", str(path)], root) + code, output = run(bash_syntax_command(path, cwd=root), root, bash_runtime=True) if code: findings.append(f"SHELL_SYNTAX:{relative}:{output}") @@ -278,18 +286,25 @@ def main() -> int: if re.search(r"__[A-Z][A-Z0-9_]*__", rendered_text): findings.append(f"UNRESOLVED_PUBLIC_TEMPLATE_TOKEN:{rendered.relative_to(destination)}") - plan = [ - BASH, - str(root / "install.sh"), + try: + bash_runtime = resolve_bash() + except RuntimeError as exc: + findings.append(f"BASH_RUNTIME:{exc}") + bash_runtime = None + + plan = bash_script_command( + root / "install.sh", "--domain", "example.com", "--admin-email", "admin@example.com", "--server-ip", "203.0.113.10", "--non-interactive", "--plan", - ] - code, output = run(plan, root) - if code or "PLAN_VALIDATION=PASS" not in output: - findings.append(f"INSTALLER_PLAN:{output}") + cwd=root, + ) if bash_runtime else [] + if plan: + code, output = run(plan, root, bash_runtime=True) + if code or "PLAN_VALIDATION=PASS" not in output: + findings.append(f"INSTALLER_PLAN:{output}") for command, label in ( ([sys.executable, str(root / "scripts/manage_documents.py"), "--root", str(root), "check"], "USER_DOCUMENTATION_GATE"), @@ -365,6 +380,8 @@ def main() -> int: print(f"FILES_SCANNED={file_count}") print(f"PYTHON_FILES={python_count}") print(f"SHELL_FILES={shell_count}") + if bash_runtime: + print(f"BASH_RUNTIME={bash_runtime}") if findings: for finding in sorted(set(findings)): print(f"FINDING={finding}") diff --git a/scripts/shell_runtime.py b/scripts/shell_runtime.py new file mode 100644 index 0000000..4202bb7 --- /dev/null +++ b/scripts/shell_runtime.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +"""Portable Bash runtime discovery for MailStack audit and contract tooling.""" +from __future__ import annotations + +import atexit +import os +import shutil +import subprocess +import sys +import tempfile +from functools import lru_cache +from pathlib import Path + +_PROBE_MARKER = "MAILSTACK_BASH_OK" + + +def _add_candidate(candidates: list[str], value: str | os.PathLike[str] | None) -> None: + if not value: + return + text = os.fspath(value) + key = os.path.normcase(os.path.abspath(text)) if os.path.isabs(text) else os.path.normcase(text) + if all( + (os.path.normcase(os.path.abspath(item)) if os.path.isabs(item) else os.path.normcase(item)) != key + for item in candidates + ): + candidates.append(text) + + +def _windows_git_bash_candidates() -> list[str]: + candidates: list[str] = [] + git = shutil.which("git") + if git: + git_path = Path(git).resolve() + # Git for Windows normally exposes git.exe from /cmd or /bin. + roots = [git_path.parent.parent, git_path.parent] + for root in roots: + _add_candidate(candidates, root / "bin" / "bash.exe") + _add_candidate(candidates, root / "usr" / "bin" / "bash.exe") + + for env_name in ("ProgramFiles", "ProgramFiles(x86)", "LOCALAPPDATA"): + base = os.environ.get(env_name) + if not base: + continue + base_path = Path(base) + if env_name == "LOCALAPPDATA": + root = base_path / "Programs" / "Git" + else: + root = base_path / "Git" + _add_candidate(candidates, root / "bin" / "bash.exe") + _add_candidate(candidates, root / "usr" / "bin" / "bash.exe") + return candidates + + +def _candidate_bashes() -> list[str]: + candidates: list[str] = [] + _add_candidate(candidates, os.environ.get("BASH_EXECUTABLE")) + + if os.name == "nt": + # Prefer Git Bash over the Windows `bash.exe` WSL launcher. The latter may + # exist on PATH even when the WSL VM/Docker Desktop backing disk is broken. + for candidate in _windows_git_bash_candidates(): + _add_candidate(candidates, candidate) + + _add_candidate(candidates, shutil.which("bash")) + if os.name != "nt": + _add_candidate(candidates, "/bin/bash") + _add_candidate(candidates, "/usr/bin/bash") + return candidates + + +def _probe(candidate: str) -> tuple[bool, str]: + path = Path(candidate) + if os.path.isabs(candidate) and not path.is_file(): + return False, "not found" + try: + completed = subprocess.run( + [candidate, "--noprofile", "--norc", "-c", f"printf '{_PROBE_MARKER}\\n'"], + text=True, + capture_output=True, + check=False, + timeout=10, + ) + except (OSError, subprocess.TimeoutExpired) as exc: + return False, f"{type(exc).__name__}: {exc}" + if completed.returncode == 0 and _PROBE_MARKER in completed.stdout: + return True, "" + detail = (completed.stderr or completed.stdout or f"exit={completed.returncode}").strip() + return False, " ".join(detail.split())[:300] + + +@lru_cache(maxsize=1) +def resolve_bash() -> str: + """Return a verified Bash executable, preferring Git Bash on Windows.""" + failures: list[str] = [] + for candidate in _candidate_bashes(): + ok, detail = _probe(candidate) + if ok: + return candidate + failures.append(f"{candidate}: {detail}") + joined = "; ".join(failures) if failures else "no Bash candidates were found" + raise RuntimeError( + "No usable Bash runtime is available. Install Git for Windows/Git Bash or set " + f"BASH_EXECUTABLE to a working bash executable. Attempts: {joined}" + ) + + + +def _shell_single_quote(value: str) -> str: + return "'" + value.replace("'", "'\"'\"'") + "'" + + +def _bash_path(candidate: str, path: Path) -> str: + """Translate a host path for the selected Bash runtime when required.""" + if os.name != "nt": + return path.resolve().as_posix() + try: + completed = subprocess.run( + [candidate, "--noprofile", "--norc", "-c", 'cygpath -u "$1"', "mailstack", str(path)], + text=True, + capture_output=True, + check=False, + timeout=10, + ) + except (OSError, subprocess.TimeoutExpired) as exc: + raise RuntimeError(f"Unable to translate Windows path for Bash: {path}: {exc}") from exc + translated = completed.stdout.strip() + if completed.returncode or not translated: + detail = (completed.stderr or completed.stdout or f"exit={completed.returncode}").strip() + raise RuntimeError(f"Unable to translate Windows path for Bash: {path}: {detail}") + return translated + + +@lru_cache(maxsize=1) +def _python3_bridge_bash_env() -> str: + """Create a process-local BASH_ENV that maps python3 to this Python interpreter.""" + bash = resolve_bash() + python_path = _bash_path(bash, Path(sys.executable)) + temporary = Path(tempfile.mkdtemp(prefix="mailstack-python3-bridge-")) + atexit.register(shutil.rmtree, temporary, ignore_errors=True) + env_file = temporary / "bash_env" + env_file.write_text( + "python3() {\n" + f" {_shell_single_quote(python_path)} \"$@\"\n" + "}\n", + encoding="utf-8", + newline="\n", + ) + return _bash_path(bash, env_file) + + +def bash_environment(*, force_python3_bridge: bool = False) -> dict[str, str]: + """Return a deterministic environment for Bash-backed repository checks. + + Ubuntu production keeps its native ``python3`` contract. Windows local tests + receive a process-local BASH_ENV function that maps ``python3`` to the exact + interpreter running the audit harness, without modifying install.sh or the + developer machine. ``force_python3_bridge`` exists for platform-independent + contract testing of the bridge itself. + """ + environment = os.environ.copy() + if os.name == "nt" or force_python3_bridge: + environment["BASH_ENV"] = _python3_bridge_bash_env() + return environment + +def script_argument(path: Path, *, cwd: Path) -> str: + """Return a Bash-friendly script path relative to cwd when possible.""" + try: + relative = path.resolve().relative_to(cwd.resolve()) + except ValueError: + return path.as_posix() + value = relative.as_posix() + return value if value.startswith(".") else f"./{value}" + + +def bash_script_command(path: Path, *arguments: str, cwd: Path) -> list[str]: + return [ + resolve_bash(), + "--noprofile", + "--norc", + script_argument(path, cwd=cwd), + *arguments, + ] + + +def bash_syntax_command(path: Path, *, cwd: Path) -> list[str]: + return [ + resolve_bash(), + "--noprofile", + "--norc", + "-n", + script_argument(path, cwd=cwd), + ] diff --git a/scripts/test_installer.py b/scripts/test_installer.py index 7d632e3..05d4116 100644 --- a/scripts/test_installer.py +++ b/scripts/test_installer.py @@ -2,22 +2,23 @@ """Non-destructive contract tests for the root Ubuntu installer.""" from __future__ import annotations -import os import subprocess from pathlib import Path +from shell_runtime import bash_environment, bash_script_command, resolve_bash + ROOT = Path(__file__).resolve().parents[1] INSTALLER = ROOT / "install.sh" -BASH = os.getenv("BASH_EXECUTABLE", "bash") def run(*arguments: str) -> subprocess.CompletedProcess[str]: return subprocess.run( - [BASH, str(INSTALLER), *arguments], + bash_script_command(INSTALLER, *arguments, cwd=ROOT), cwd=ROOT, text=True, capture_output=True, check=False, + env=bash_environment(), ) @@ -27,6 +28,25 @@ def require(condition: bool, message: str) -> None: def main() -> int: + print(f"BASH_RUNTIME={resolve_bash()}") + bridge = subprocess.run( + [ + resolve_bash(), + "--noprofile", + "--norc", + "-c", + "python3 -c 'print(\"MAILSTACK_PYTHON3_BRIDGE_OK\")'", + ], + cwd=ROOT, + text=True, + capture_output=True, + check=False, + env=bash_environment(force_python3_bridge=True), + ) + require( + bridge.returncode == 0 and "MAILSTACK_PYTHON3_BRIDGE_OK" in bridge.stdout, + bridge.stdout + bridge.stderr or "python3 compatibility bridge failed", + ) valid = run( "--domain", "example.com", "--admin-email", "admin@example.com", diff --git a/scripts/test_operations.py b/scripts/test_operations.py index a908a67..5fdb097 100644 --- a/scripts/test_operations.py +++ b/scripts/test_operations.py @@ -3,14 +3,14 @@ from __future__ import annotations import ipaddress -import os import re import subprocess from pathlib import Path +from shell_runtime import bash_syntax_command, resolve_bash + ROOT = Path(__file__).resolve().parents[1] APP_SCRIPTS = ROOT / "mailbox-app/scripts" -BASH = os.getenv("BASH_EXECUTABLE", "bash") def require(condition: bool, message: str) -> None: @@ -21,12 +21,18 @@ def require(condition: bool, message: str) -> None: def read(name: str) -> str: path = APP_SCRIPTS / name require(path.is_file(), f"missing operational script: {name}") - result = subprocess.run([BASH, "-n", str(path)], capture_output=True, text=True) + result = subprocess.run(bash_syntax_command(path, cwd=ROOT), capture_output=True, text=True) require(result.returncode == 0, result.stdout + result.stderr) return path.read_text(encoding="utf-8") def main() -> int: + print(f"BASH_RUNTIME={resolve_bash()}") + shell_runtime = (ROOT / "scripts/shell_runtime.py").read_text(encoding="utf-8") + require("_windows_git_bash_candidates" in shell_runtime, "Git Bash discovery contract is missing") + require("BASH_EXECUTABLE" in shell_runtime, "Bash runtime override contract is missing") + require("MAILSTACK_BASH_OK" in shell_runtime, "Bash runtime probe contract is missing") + backup = read("backup.sh") restore = read("restore.sh") health = read("health_check.sh")