Skip to content

v0.36.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 11:49
· 496 commits to main since this release

This release can refuse to start — read this first

Two new production-readiness checks are upgrade blockers for an existing deployment. There is no bypass
flag, by design (ADR 0343): outside Development the process refuses to start and names what is wrong.

OpenBao:Address is now required. Without it the transit encryptor is a pass-through, so every secret
routed through it is stored in plaintext — TOTP secrets, tenant SMTP passwords, audit-webhook signing
secrets, module settings and the mail ingest key. It is not only TOTP, which is how this stayed small in
people's heads while the list grew. The downgrade was silent in the other direction too: decryption passes
through anything without the vault prefix, so removing OpenBao from a working installation kept serving
while new secrets began being written in the clear, with nothing marking where it changed. Configure
OpenBao (ADR 0338/0339) before upgrading, or run locally with ASPNETCORE_ENVIRONMENT=Development.

App:TrustProxyHeaders must name the proxies it trusts. With the flag on and neither
App:KnownProxies nor App:KnownProxyNetworks set, X-Forwarded-* was believed from any peer — whoever
could reach the process could choose the host its generated links carry and the address the sign-in
throttle counts against. The refusal is on the combination, not the flag. The Helm chart defaults
config.app.knownProxyNetworks to the pod network, so chart deployments are covered; a Compose or bare
deployment that turned the flag on needs to name its proxy. An entry that does not parse is reported too,
because an ignored entry narrows nothing.

Read this before upgrading

Three behaviour changes landed in v0.35.0 and were not announced there. They are repeated here
because nothing in the software will tell you about them.

A tenant on Encryption:Modes:<tenant> = Storage no longer receives enveloped mail over IMAP.
Enveloping mail is delivery; Storage protects content at rest and nothing more. Such a tenant's mail
becomes plaintext over IMAP, exactly as a tenant with no registered certificate already gets — the funnel
fails open with a warning. To keep sealed mail, move the tenant to SealedDeliveryPermissive, or
SealedDeliveryStrict to close the other content doors as well. Both tiers require the Encryption Module
to be mounted and licensed for that tenant; a half-done move refuses at startup rather than degrading
quietly.

A tenant on Strict or SealedDeliveryStrict can no longer compare two versions of a document,
nor a checked-out working copy against its version. The comparison answered with both sides' full plain
text for a client-side diff, which made it the most direct plaintext door in the API. There is no setting
to change: the tier's premise is that content leaves only as an envelope, and a diff of two documents is
not one. A tenant that needs comparison belongs on Storage or SealedDeliveryPermissive. Both clients
hide the action rather than offering one that fails.

A reader whose certificate holds an EC key needs the Encryption Service at its ADR 0018 or later.
That is the ordinary case for a smart card's key-management slot. Before that version the service produced
CMS that can address RSA key-transport recipients only, so such a reader could not be addressed at all on a
Strict tenant. The symptom is a content read answering 500 CONTENT_ENVELOPE_REFUSED with the
recipients' key algorithms in the server log. Readers with RSA certificates are unaffected in either
direction.

New in this release

Security. A chosen password is checked against a policy. A TOTP code is single-use, as the standard's
name says it should be. XML from outside refuses entities by assertion rather than by coincidence. The two
production-readiness refusals above belong to this tranche as well. A refused credential, a sign-out and a lockout now reach
the audit trail — before this, the trail showed that somebody got in, but not that somebody had been
trying for an hour.

Token lifetimes changed, and an integrator will notice. An access token now expires after
15 minutes (it was about an hour) and a refresh token after 30 days (it was fourteen) — the
numbers chosen in ADR 0083, which until now was never implemented, so the framework's own defaults
applied. The short access token is the half that matters: there is no way to invalidate an issued access
token, so its lifetime is the revocation window. Refresh-token rotation is on and reuse is refused, so
the longer refresh is not a relaxation. Both are configurable —
OpenIddict:AccessTokenLifetimeMinutes and OpenIddict:RefreshTokenLifetimeDays — because finding out
mid-incident that they are not is the wrong moment. A client that refreshes properly needs no change.

Published images carry provenance, an SBOM, and a keyless signature. Note the image tag carries no
v prefix
— the release is v0.36.0, the image is 0.36.0. Verification:

cosign verify ghcr.io/hebelconsulting/simplarchive:0.36.0 \
  --certificate-identity-regexp '^https://github\.com/HebelConsulting/SimplArchive/\.github/workflows/docker-publish\.yml@' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Encryption. A new StrictRehearsal mode is Storage with envelope delivery switched on — the
readiness exercise before Strict, which is otherwise a cliff. It protects nothing beyond Storage: every
plaintext door still serves. Each encrypted object now records which key wrapped it, and a mismatch is
refused before the key is ever asked — a re-provisioned or restored token used to mint a locally-correct
key and permanently orphan everything the previous one had wrapped, surfacing months later as an
unexplained error. People can now enrol their own certificate from either client.

Outbound mail per tenant. A tenant configures its own SMTP account in both clients, with a test
message that proves it. Leaving the password box empty keeps the stored password.

Recovering an overwritten intray file. The bytes an overwrite set aside can be listed and restored
(saconsole intray previous list / restore), and expire on their own.

Smaller things. The Dock and taskbar carry the count of open tasks. The request log reports the status
the client actually got. Two instances can no longer both ingest one dropped file.


Full Changelog: v0.35.0...v0.36.0