• v4.0.1 db77a4e657

    v4.0.1
    All checks were successful
    CI / Frontend tests, audit, and build (push) Successful in 1m18s
    CI / Backend lint (ruff) (push) Successful in 33s
    CI / Docker image build (push) Successful in 13s
    Release / Create Forgejo release (push) Successful in 19s
    CI / Bot/backend version sync (push) Successful in 59s
    CI / Summarisation accuracy eval harness (stub provider) (push) Successful in 2m41s
    Release / Build and push versioned images (push) Successful in 1m26s
    CI / Backend migration, tests, and audit (push) Successful in 4m54s
    CI / Bot tests and audit (push) Successful in 4m38s
    Stable

    claude-bot released this 2026-08-29 07:01:42 +00:00 | 407 commits to main since this release

    Raw audio is never destroyed without a recovery window, and the secrets at
    rest are both readable and actually encrypted.

    No migrations. Nothing to rehearse, nothing to back out.

    ⚠️ Rebuild, do not pull. The backend image changes (it now pins its
    PostgreSQL client to the database's major version), so deploy with
    docker compose up -d --build. A pull-and-restart will not apply it.

    ⚠️ Audio now occupies disk for longer. Every retention mode reaches
    deletion through the trash, so audio survives its retention window plus the
    trash grace (minimum 7 days) rather than being removed the moment the window
    expires. Under the previous default it was deleted during processing and
    occupied nothing at all.

    If the startup log names a setting as having been stored in plaintext,
    rotate that credential.
    Re-encrypting protects it going forward; it does not
    un-expose a value that was already readable in every backup taken until then.

    Security

    • Sensitive settings stored in plaintext are now encrypted on startup.
      Encryption only ever happened on write, and rows written before it was
      introduced were passed through on read forever — so a credential that had
      never been rotated since could still be sitting in clear text in the database
      and in every backup, with nothing reporting it. Any such row is re-encrypted
      at startup and logged by name. If a row is reported, rotate that credential
      — it was readable in every backup taken until now.

    Fixed

    • Database backups are restorable again. The backup ran pg_dump from the
      backend image while a restore runs pg_restore against the database image,
      and the two were on different major versions — so every dump was rejected by
      the server's own tooling with unsupported version (1.16) in file header,
      while the job reported success and recorded it as one. The backend image now
      pins its client to the database's major version, a test asserts the two stay
      in step, and the backup refuses to run rather than writing a dump it knows
      cannot be restored. Existing dumps written before this are not restorable
      without a matching newer client
      — replace them by taking a fresh backup
      after upgrading.
      crashes on first use.** Sensitive settings are encrypted with a key derived
      from SECRET_KEY, which lives in .env and is not part of a database dump —
      so restoring elsewhere left them unreadable, and reading one raised a raw
      cryptography error out of the settings layer instead of taking the
      "not configured" path every caller already handles. Unreadable settings now
      degrade to unconfigured, are reported once and clearly, and are listed at
      startup so the problem is found during the restore rather than days later
      when a session fails to summarise. docs/OPERATIONS.md now states that
      SECRET_KEY must be backed up alongside the database.

    • Admin can distinguish "never configured" from "configured but
      unreadable".
      They render identically in every settings form, which is how a
      restore produced a system that looked complete and was not.

    • Raw audio is no longer deleted the moment a transcript exists. The
      recommended retention mode destroyed the per-speaker WAVs inline during
      processing, on the reasoning that there was nothing left to reprocess. That is
      wrong in the case that matters, because the pipeline itself changes: v4.0.0
      rewrote transcription, extraction, validation and composition, and no real
      recording survived to validate any of it against. The mode is now
      trash_after_processing — the audio moves to the trash, stays recoverable
      there, and is deleted only once the grace has elapsed. The old
      delete_after_processing name is still accepted from existing configuration
      and normalised on read, so no action is needed on upgrade.

    • The trash grace is a floor, not a default. A configured value below seven
      days is clamped up, so a misconfiguration cannot reintroduce same-day loss.

    • An expiring retention window no longer deletes directly. Under
      retain_days the sweeper now moves expired audio to the trash rather than
      removing it, so every mode reaches deletion through the same recoverable
      window. Audio therefore lives for the configured window plus the trash
      grace
      — budget disk accordingly.

    • A trashed recording is no longer deleted early by the retention window.
      The grace-period sweep and the policy sweep raced, and the shorter one won: a
      recording trashed today, whose window had already passed, was removed on the
      next scheduled run without its grace ever being observed.

    • A silent speaker is no longer reported as having spoken. The fix that was
      supposed to report a backend-dropped silent track as present-but-uncaptured
      was overridden, because the same person was still counted among the captured
      speakers and that assignment won. It only affected recordings the current bot
      did not produce — which is precisely the case the guard existed for.

    Downloads