You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Roadmap: config-as-code, Sigstore signing and Scorecard shipped
build/hmconfig.ps1 closes the config-as-code item the roadmap called the
highest-value discretionary one. Signing is half done and recorded as such:
Sigstore covers verifiability for anyone who checks, but SmartScreen and the UAC
prompt care about Authenticode and not about Sigstore, so Windows still shows an
unknown-publisher warning and that half stays open. Scorecard publishes to code
scanning rather than to the public badge API, since there is no reason to send
repository telemetry to a third party for a number we can read ourselves.
|[Future-proofing: platform and supply chain](#future-proofing-platform-and-supply-chain)|2|–|4| 2 |
68
-
|[Future-proofing: deployment and operations](#future-proofing-deployment-and-operations)|2| – |7| – |
69
-
|**Total**|**520**|**5**|**181**|**14**|
67
+
|[Future-proofing: platform and supply chain](#future-proofing-platform-and-supply-chain)|3|1|2| 2 |
68
+
|[Future-proofing: deployment and operations](#future-proofing-deployment-and-operations)|3| – |6| – |
69
+
|**Total**|**521**|**5**|**177**|**14**|
70
70
71
71
Three things stand out and are worth naming rather than leaving to be inferred.
72
72
**Storage and the administration surface are the best-covered areas**, and the
@@ -1025,9 +1025,9 @@ Future-proofing: platform and supply chain
1025
1025
| ✅ | **.NET target policy, written down** | Audited every project in the tree, not just the shipping ones. **All six shipped artefacts and the Control Panel publish as `net10.0`** — verified from their `runtimeconfig.json`, so nothing reaching a user is on .NET 8. The rest is deliberate, and now recorded so it is a decision rather than drift: the **regression suite and its three companions stay on .NET Framework 4.8.1**, which has *no end date* (Component Lifecycle Policy — supported as long as its host Windows) and drives the server over COM interop, so moving it to .NET 10 would be a large, risky change to the release gate for no benefit. **Six projects still on .NET Framework 4.5 — support ended 13 January 2016 — were bumped to 4.8.1**; they are dormant upstream dev tools referenced by no script, and since the 4.5 targeting pack is absent from a modern VS install they could not build at all before. **Seven projects remain older still**: five on .NET Framework 3.5 SP1 (supported to 10 January 2029), and two — `MemoryTests` and `TestInvalidConnections` — that declare no `TargetFramework` at all and carry Visual Studio 2005 markers (`ProductVersion 8.0.50727`, `ToolsVersion 3.5`, `OldToolsVersion 2.0`). All seven are dormant, referenced by no solution the build touches, and buildable by nobody today. Modernising code nothing exercises is a decision to take deliberately rather than blind; retiring them is the more honest option and is worth a separate look. |
1026
1026
| ⬜ |**Decide the OpenSSL branch**| 4.0.x is not LTS and dies 14 May 2027. Decide by Q1 2027 whether to follow to the next LTS or move back to 3.5. |
1027
1027
| ⬜ |**Declare a supported Windows floor**| Server 2019 / Windows 10 21H2, effective with the first release after 12 Jan 2027. Zero code cost — Server 2019 is still `_WIN32_WINNT=0x0A00`, and that macro should *not* be raised. |
1028
-
|⬜|**Artifact signing**|SBOMs are already attached to every release in both SPDX and CycloneDX. The gap is signing: Authenticode on the installer and binaries, and ideally Sigstore for the release artefacts. This is the supply-chain item that is actually missing, and the CRA makes update integrity a formal requirement if scope ever changes. |
1028
+
|🔄|**Artifact signing**|**Sigstore half done.**[`sign-release.yml`](.github/workflows/sign-release.yml) signs every release asset with cosign, keyless via OIDC -- deliberately keyless, because for a single-maintainer project a long-lived signing key stored in Actions is itself the thing most worth stealing. The signature is verified in the same job, so a malformed bundle cannot reach a user. **Authenticode is still open, and the two are complementary rather than alternatives:** SmartScreen and the UAC prompt care about Authenticode and not about Sigstore, so cosign gives verifiability to anyone who checks while Windows still shows an unknown-publisher warning. Azure Trusted Signing is the realistic route. |
1029
1029
| ✅ |**security.txt (RFC 9116)**| Served at `/.well-known/security.txt` by the web services listener, with a derived (never hardcoded) `Expires`, a `Policy` pointing at `SECURITY.md`, and a deliberate refusal to serve anything at all when no contact is configured — a placeholder address is worse than no file. Note it inherits the listener caveat above: the web services ports default to 0, and the server now says so at startup. |
1030
-
|⬜|**OpenSSF Scorecard / OSPS Baseline**| Run it, publish the score, fix what is cheap. Buyers increasingly ask. |
1030
+
|✅|**OpenSSF Scorecard**|[`scorecard.yml`](.github/workflows/scorecard.yml), weekly and on push, publishing SARIF to code scanning so supply-chain posture sits beside the CodeQL findings. Deliberately **not** published to the public OpenSSF badge API -- there is no reason to send repository telemetry to a third party to obtain a number we can read ourselves. Expect Branch-Protection and Code-Review to fail by design on a single-maintainer repository; the actionable checks are Pinned-Dependencies, Token-Permissions and Dangerous-Workflow. OSPS Baseline remains open. |
1031
1031
| ⏸️ |**SQL Server Compact**| Long dead upstream and the one real dependency liability. Migration targets are SQLite or LocalDB. Deferred because it is a data-migration project for existing installs, not a swap, and no date forces it. |
1032
1032
| ⏸️ |**Boost and C++ standard upgrades**| No forcing function. Move when there is a reason. |
1033
1033
@@ -1038,7 +1038,7 @@ Future-proofing: deployment and operations
1038
1038
|:-:|---|---|
1039
1039
| ✅ |**Document the silent-install contract**| Done — see [README](README.md#unattended-install). Two limitations are now written down rather than discovered: the administrator password cannot be set on the command line at all, and a silent *upgrade* therefore cannot supply it either. Originally: The Inno script *already* branches on `WizardSilent()` and forwards credentials to `DBSetupQuick`, and already checks both `Exec` success and the child exit code so a failed schema upgrade cannot masquerade as success. None of that is documented, so an admin automating a hundred installs has to read the Pascal. Publish the switches and exit codes. Cheapest high-value win on this list. |
1040
1040
| ⬜ |**Migration *into* the server**| The biggest genuine opportunity here and currently unexploited: with Microsoft turning off Basic auth and EWS, people are moving. There is no documented import path. Competitors have imapsync, `doveadm import`, PST and Maildir/mbox routes. |
1041
-
|⬜|**Configuration as code**|The highest-value discretionary item. Config lives in a database plus an INI file, which is the right architecture; the missing piece is a dump/diff/apply CLI so config is reviewable and reproducible. Note the Unix competitors offer less here than the "declarative config" framing suggests. |
1041
+
|✅|**Configuration as code**|[`build/hmconfig.ps1`](build/hmconfig.ps1) -- `export`, `diff` and `apply` the whole configuration as reviewable JSON, over the COM API so it can never disagree with the rules the server itself enforces. Two design decisions worth knowing: **`apply` is a dry run without `-Force`, and deletions additionally require `-AllowDelete`**, because deleting an account deletes its mail and a stale checkout, a partial export or a mistyped domain must not be able to do that -- additive and update operations are the default precisely because they cannot lose mail. And **passwords are never exported**: the server stores hashes, a hash is not a password, and a config file that silently reset every mailbox password on apply would be far worse than one that cannot set them at all. New accounts get a random password that must be reset. |
1042
1042
| ⬜ |**Prometheus naming fixes**| The bulk is idiomatic — `hmailserver_` prefix, `_total` on counters, `_seconds` base units, protocol as a label. Five cheap deviations: `hmailserver_state` is a numeric enum where OpenMetrics defines StateSet; `uptime_seconds` should be `start_time_seconds` as a Unix timestamp; no `build_info`; the two latency metrics are summaries with no quantiles, so only a mean is computable — histograms with buckets would give p95/p99; `database_up` collides conceptually with Prometheus's synthetic `up`. |
1043
1043
| ⬜ |**Per-domain and per-account metric labels**| Every counter is global, which is what stops metrics becoming reporting. |
1044
1044
| ✅ |**Ship Grafana dashboard JSON in the repo**|[`hmailserver/docs/grafana-dashboard.json`](hmailserver/docs/grafana-dashboard.json) — 16 panels, built against the metric names the exporter actually publishes rather than the ones it should. The panel descriptions state where the exporter is weak, so the dashboard does not quietly imply a tail-latency signal that is not there. |
Copy file name to clipboardExpand all lines: hmailserver/docs/README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,7 @@ Elsewhere in the repository
21
21
|||
22
22
|---|---|
23
23
|[README.md](../../README.md)| What the server does, how to build it, how to install it |
24
+
|[build/hmconfig.ps1](../../build/hmconfig.ps1)| Putting the server configuration in version control: export it as JSON, review a change as a diff, apply it to another machine. `apply` is a dry run unless you pass `-Force`, and deletions need `-AllowDelete` on top of that. |
24
25
|[ARCHITECTURE.md](../../ARCHITECTURE.md)| Changing the code: the module map, where a given change belongs, and the constraints that have caused real bugs here |
25
26
|[Roadmap.md](../../Roadmap.md)| The full capability matrix — what exists, what is planned, what is deliberately not, and every known defect |
26
27
|[.github/SUPPORT.md](../../.github/SUPPORT.md)| Where to ask what: issues, discussions, or the hMailServer forum |
0 commit comments