Changelog

September 1, 2026 ยท View on GitHub

Narrative notes by release. Per-file binaries and assets: GitHub releases.

Unreleased

On main only; not part of the latest tagged release until you tag and publish. Compare against the current tag on GitHub releases.

  • Docs: Pointer to marchat-bot (standalone Go WebSocket bot SDK; not a plugin) in README, PROTOCOL, docs/README, CLIENT_HOOKS, and PLUGIN_ECOSYSTEM.
  • Packaging: Drop versioned scripts/post-release-v*.ps1 helpers; pattern is gitignored. Chocolatey remains choco pack in packaging/chocolatey/ (PACKAGING.md).

v1.3.6

Released 2026-09-01. Since v1.3.5; compare v1.3.5...v1.3.6. Commits: git log v1.3.5..v1.3.6 --oneline.

  • Server: Fix: file-backed SQLite uses a single-connection writer pool (MaxOpenConns(1), _txlock=immediate) plus a bounded WAL reader pool (MaxOpenConns(4), _query_only=1) so health/admin/history reads are not serialized behind inserts; :memory: and Postgres/MySQL are unchanged. Close with CloseDB. No schema migration (#126, #127).
  • Docs: TESTING notes that CI golangci-lint is govet/ineffassign/staticcheck SA* and that gopls unusedparams is not in CI; main-module coverage refreshed to 48.3%.
  • Dependencies: modernc.org/sqlite v1.57.0; charm.land/bubbletea/v2 v2.0.9; charm.land/bubbles/v2 v2.2.1; github.com/mattn/go-runewidth v0.0.28.
  • Packaging: Version strings and URLs for v1.3.6 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.6 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).
  • Docker: the v1.3.6 image rebuild runs apk upgrade on Alpine 3.22 and picks up openssl 3.5.8 (no Dockerfile change).

v1.3.5

Released 2026-08-15. Since v1.3.4; compare v1.3.4...v1.3.5. Commits: git log v1.3.4..v1.3.5 --oneline.

  • Server: Fix: :kick / :ban (and admin TUI / web user actions) reject self-targets case-insensitively and return clear errors instead of disconnecting the admin and writing a 24h ban; KickUser / BanUser return errors so callers claim success only on nil; kicking an already permanently banned user returns an error without claiming success (#115).
  • Server: Fix: :kick (and admin TUI / web kick actions) are online-only: KickUser requires an active WebSocket connection, disconnects the target, and applies a 24h temporary ban; offline or never-connected users return ErrKickNotConnected with no tempKicks entry or ban_history row; :ban / BanUser remain offline-capable (#116).
  • Server: Fix: reject empty or whitespace-only plaintext on text, dm, and edit when encrypted is false (System reply, no persist/broadcast); encrypted opaque content is not treated as empty (#117).
  • Server: Fix: SQLite InitDB applies busy_timeout / WAL / related pragmas via the DSN on every connection and sets MaxOpenConns(1) / MaxIdleConns(1), so concurrent inserts no longer fail with SQLITE_BUSY from one-shot PRAGMA + the default database/sql pool (#118).
  • Server: Fix: active permanent bans and unexpired temp kicks load from ban_history on hub start (expires_at NULL = permanent; non-NULL = kick expiry), so moderation survives process restart. Pre-upgrade open rows without expires_at load as permanent. Permanent bans are presence-only in memory (no 100-year sentinel expiry).
  • Server: Fix: schema bootstrap uses versioned MigrateSchema (schema_version) and hard-fails when required tables or ban_history.expires_at are missing instead of warning and continuing. SQLite/Postgres apply each version inside a transaction (mid-migration failure rolls back); MySQL DDL cannot participate in multi-statement transactions (implicit commit), so steps run without a wrapping transaction and schema_version is recorded only after a successful apply.
  • Server: Connected-user lookups (KickUser, kickUser, ForceDisconnectUser, broadcastDM) use an O(1) clientsByUsername map under clientsMutex.
  • Server: Fix: :ban / :kick close any open ban_history row before inserting (at most one open row per user), persist to the DB before updating in-memory enforcement state, and load the latest open row by id on hub start so kick-then-ban (and legacy duplicates) restart cleanly.
  • Docs: ARCHITECTURE documents MigrateSchema / schema_version / ban_history.expires_at; TESTING local lint install pins match CI (no @latest); main-module coverage refreshed to 47.8%.
  • Dependencies: modernc.org/sqlite v1.56.0 (journal-rollback corruption fix; modernc.org/libc v1.74.4); github.com/lucasb-eyer/go-colorful v1.4.1; golang.org/x/crypto v0.55.0; github.com/charmbracelet/x/ansi v0.11.8; charm.land/lipgloss/v2 v2.0.6.
  • Toolchain: Go 1.25.13 in go.mod, nested plugin modules, CI, and Dockerfile (stdlib fixes for the 6 reachable findings govulncheck reported on 1.25.12).
  • CI: govulncheck ./... without -show verbose (default symbol scan; unreachable module advisories stay informational). Pin golangci-lint v2.12.2 and govulncheck v1.6.0 (no @latest) in the main and nested-module jobs; add .golangci.yml (v2) enabling govet/ineffassign/staticcheck with all minus ST*/QF* (bug-focused checks, not SA*-only).
  • Packaging: Version strings and URLs for v1.3.5 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.5 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.3.4

Released 2026-08-03. Since v1.3.3; compare v1.3.3...v1.3.4. Commits: git log v1.3.3..v1.3.4 --oneline.

  • Server: Fix: oversized file uploads that fit under the WebSocket DoS read ceiling (32 MiB) are rejected with a System message and a live connection (app-layer size check); SetReadLimit uses that ceiling above policy wire size so gorilla does not close with empty 1009 before the reply can flush. Residual over-ceiling reads still log ErrReadLimit without enqueueing a System message (#114 follow-up).
  • Docs: PROTOCOL / TESTING / README document DoS ceiling vs policy reject; main-module coverage refreshed to 46.3%.
  • Packaging: Version strings and URLs for v1.3.4 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.4 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.3.3

Released 2026-08-03. Since v1.3.2; compare v1.3.2...v1.3.3. Commits: git log v1.3.2..v1.3.3 --oneline.

  • Server: Fix: oversized file WebSocket messages hit explicit read-limit handling with correct rejection logging and a System reply when the connection is still writable; read limit accounts for base64 JSON wire size (not raw bytes only); rejects declared size and actual payload length above the limit (#114).
  • Client: Fix: WebSocket close 1009 (message too big) shows a file-size error instead of a generic reconnect warning.
  • Plugins: Archive extraction uses os.OpenRoot scoped writes and filepath.IsLocal entry validation so zip-slip static analysis and runtime checks align (CodeQL go/zipslip).
  • Docs: PROTOCOL and TESTING note file rejection behavior and refreshed coverage (46.4% main module).
  • Dependencies: modernc.org/sqlite v1.55.0 (SQLite 3.53.3; modernc.org/libc v1.74.1); github.com/mattn/go-runewidth v0.0.27.
  • Packaging: Version strings and URLs for v1.3.3 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.3 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.3.2

Released 2026-07-15. Since v1.3.1; compare v1.3.1...v1.3.2. Commits: git log v1.3.1..v1.3.2 --oneline.

  • Server: Security: overwrites client-supplied sender on text and file outbound paths (stampSenderTimedOutbound); rejects NUL bytes in persistable content before insert; does not broadcast when message persistence fails.
  • Client: Security: desktop notifications hardened: Windows toast XML built in Go with xml.EscapeText and shown via powershell -EncodedCommand; macOS osascript uses strconv.Quote string literals (no shell interpolation of wire content).
  • Docs: PROTOCOL, SECURITY, ARCHITECTURE, and TESTING document server sender stamping and safe desktop notification paths for untrusted wire content.
  • Packaging: Version strings and URLs for v1.3.2 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.2 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.3.1

Released 2026-07-14. Since v1.3.0; compare v1.3.0...v1.3.1. Commits: git log v1.3.0..v1.3.1 --oneline.

  • Server: Fix: handshake history replay sets type: "text" on channel messages so reconnect scrollback renders in the client (was omitted due to zero-value omitempty).
  • Toolchain: Go 1.25.12 in go.mod, nested plugin modules, CI, and Dockerfile (stdlib fixes for reachable GO-2026-5856 / crypto/tls ECH privacy leak and package-level GO-2026-4970 / os symlink escape reported by govulncheck on 1.25.11).
  • Dependencies: charm.land/bubbles/v2 v2.1.1 (textarea prompt styling fix); golang.org/x/crypto v0.54.0; golang.org/x/term v0.45.0 (transitive golang.org/x/sys v0.47.0, golang.org/x/text v0.40.0, github.com/sahilm/fuzzy v0.1.3).
  • Packaging: Version strings and URLs for v1.3.1 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.1 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.3.0

Released 2026-07-06. Since v1.2.0; compare v1.2.0...v1.3.0. Commits: git log v1.2.0..v1.3.0 --oneline.

  • Client: ANSI-aware word-wrap for chat bodies; reaction aliases thumbsup / thumbsdown and :unreact, :thumbsup, :thumbsdown; when E2E is on and server search returns no matches, a System line notes ciphertext-only matching. Charm v2: Bubble Tea, Bubbles, and Lip Gloss on charm.land/*/v2 (tea.View, KeyPressMsg, bubbles setters). Long URLs wrap at path boundaries; wrapped segments keep hyperlink style and OSC 8 Style.Hyperlink hrefs (ASCII hyphens); mouse click-to-open fallback (#103). Fix: composer chrome, multiline keys, placeholder cursor, and scroll-to-tail follow; mouse wheel routes to the active viewport; help/DB overlays suppress typing, URL clicks, and read-receipt flush until closed; ephemeral System feedback uses the banner; reconnect backoff advances on failure; transcript notices, reactions, and read receipts stay channel-scoped; E2E paths do not log plaintext.
  • Server: Replay up to 50 visible messages on every handshake (including reconnect). Fix: Postgres boolean SQL for search, pin toggle, and pinned listing; MySQL parseTime=true when unset; outbound messages stamped to the sender's channel; typing, reactions, and read receipts channel-scoped; :backup SQLite-only; admin TUI mouse scroll on tabs and tables; :cleardb clears user_message_state.
  • Plugins: Fix: serialized plugin stdin writes so chat fan-out and command RPC cannot corrupt IPC lines.
  • Docs: README, TESTING, ARCHITECTURE, and PROTOCOL for channel stamping, reconnect, wrapped URL limitation (#103), SQLite-only :backup, and plugin IPC; agent skills under .cursor/skills/.
  • Tooling: Project Agent skills and .cursor/rules/marchat.mdc; .gitignore tracks shared rules and skills.
  • Dependencies: charm.land/bubbletea/v2 v2.0.8, charm.land/bubbles/v2 v2.1.0, charm.land/lipgloss/v2 v2.0.5 (replaces Charm v1); github.com/charmbracelet/colorprofile v0.4.3, github.com/charmbracelet/x/ansi v0.11.7, github.com/lucasb-eyer/go-colorful v1.4.0, github.com/mattn/go-runewidth v0.0.24. github.com/jackc/pgx/v5 v5.10.0, golang.org/x/crypto v0.53.0, golang.org/x/term v0.44.0, modernc.org/sqlite v1.53.0.
  • Packaging: Version strings and URLs for v1.3.0 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.3.0 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.2.0

Released 2026-06-06. Since v1.1.0; compare v1.1.0...v1.2.0. Commits: git log v1.1.0..v1.2.0 --oneline.

  • Server: WebSocket Origin checks compare parsed hostnames (no substring matching); optional MARCHAT_ALLOWED_ORIGINS allowlist. getClientIP and web-admin login rate limiting honor X-Forwarded-For / X-Real-IP only when the immediate peer is in MARCHAT_TRUSTED_PROXIES (comma-separated IPs or CIDRs).
  • Client: Direct messages use the same E2E wire path as channel text when encryption is enabled (encrypted plus base64 nonce || ciphertext with the global key). Applies to :dm <user> <msg>, DM mode compose, and code snippets sent while a DM thread is open (:code / Alt+C). Fix: code snippets in DM mode route through the DM send path, not channel text.
  • Plugins: Plugin store downloads validate SHA-256 checksums before extraction (HTTP and file://), reject oversize archives, parse file:// paths correctly on Linux and Windows (registry and download URLs via plugin/fileurl), detect archive type from the URL path (including query strings), extract to a staging directory with zip-slip checks, roll back failed updates, set the execute bit on the plugin binary by exact name match after ZIP/TAR extract, and do not leave an empty plugin directory when install download fails.
  • Toolchain / dependencies: Go 1.25.11 in go.mod, nested plugin modules, CI, and Dockerfile (stdlib fixes for GO-2026-5037, GO-2026-5038, GO-2026-5039 reported by govulncheck on 1.25.10); golang.org/x/crypto v0.52.0; modernc.org/sqlite v1.51.0 (was v1.50.0). Transitive filippo.io/edwards25519 v1.2.0 (MySQL driver).
  • Packaging: Version strings and URLs for v1.2.0 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.2.0 manifest set, Chocolatey, AUR) with SHA256 from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh).

v1.1.0

Released 2026-05-12. Since v1.0.0; compare v1.0.0...v1.1.0. Commits: git log v1.0.0..v1.1.0 --oneline.

  • Server: messages.channel column (default general) with startup migration; channel messages persist and replay on the correct channel. Direct messages store recipient; reconnect history includes DM rows only for sender and recipient. TypingMessage with non-empty recipient uses the same DM delivery path as chat DMs.
  • Client: Transcript and typing scoped to the active channel; DM thread sidebar (unread, hide, reappear), dm_state.json under the client config directory, footer shows the active DM peer, commands :dm / :dm off / :dms / :dmhide. Fix: :dmhide and :dms handled before :dm (prefix collision). Typing: DM compose sends optional recipient on the wire; reference TUI hides DM-scoped typing unless that thread is open and hides channel typing while a DM thread is open.
  • Diagnostics: Client -doctor reports dm_state.json and E2E key source; server -doctor includes a DM history note.
  • Docs / protocol: ARCHITECTURE, PROTOCOL, README, TESTING, CONTRIBUTING, QUICKSTART, PLUGIN_ECOSYSTEM, docs/README for DMs, typing recipient, and doctor output; optional graphical clients and marchat-plugins discovery where relevant.
  • Toolchain / dependencies: Go 1.25.10 in go.mod, CI, Dockerfile; golang.org/x/crypto v0.51.0, golang.org/x/term v0.43.0; github.com/jackc/pgx/v5 v5.9.2, modernc.org/sqlite v1.50.0, github.com/go-sql-driver/mysql v1.10.0.
  • CI: Downstream AUR publish clones aur.archlinux.org over HTTPS before SSH push.
  • Packaging: Version strings and URLs for v1.1.0 in install.ps1, install.sh, build-release.ps1, scripts/build-*.ps1/sh, README, SECURITY.md, .github/workflows/release.yml, and packaging/ (Homebrew, Scoop, winget 1.1.0 manifest set, Chocolatey, AUR). SHA256 fields are placeholders (000000...) until replaced from published release zips (PACKAGING.md, packaging/ci/render-release-manifests.sh). Regenerate packaging/aur/.SRCINFO on Arch after final PKGBUILD checksums (makepkg --printsrcinfo).

v1.0.0

Released 2026-04-17. Since v0.11.0-beta.5; compare v0.11.0-beta.5...v1.0.0. Commits: git log v0.11.0-beta.5..v1.0.0 --oneline.

  • Client: Terminal-native footer and banner chrome; read receipts in the transcript; reconnect clears stale transcript state; sending indicator and unread count refinements; rate limit notice when the server throttles; theme loader updates and THEMES.md examples.
  • Server: Clearer handling for unknown admin commands over the admin connection; related client/server sending-state fixes after chat writes.
  • Docs / protocol: ARCHITECTURE, PROTOCOL, README, TESTING aligned with TUI behavior and coverage.
  • Packaging: v1.0.0 templates across Homebrew, Scoop, winget, Chocolatey, and AUR; Chocolatey nuspec iconUrl (repo logo on main) and clearer title; refresh zip SHA256 values from published release assets before choco pack / local manifest validation (see PACKAGING.md).

v0.11.0-beta.5

Released 2026-04-10. Since v0.11.0-beta.4; compare v0.11.0-beta.4...v0.11.0-beta.5. Commits: git log v0.11.0-beta.4..v0.11.0-beta.5 --oneline.

  • Server: RFC 6455 WebSocket close frames on handshake errors; hub stays off plugin IPC with bounded, best-effort, at-most-once plugin chat fan-out.
  • Client: Experimental env-driven exthook and -doctor integration.
  • Plugin SDK: RunStdio / HandlePluginRequest stdio loop; echo sample uses the SDK; docs and README plugin examples aligned (GetConfig, Marshal); plugin/sdk/cov gitignored; CI runs nested plugin modules (fmt, govulncheck).
  • Tests / CI: Server loadverify benches and rate-limit coverage; -doctor tests use the injectable osEnviron hook under environMu (no parallel buildEnvLines tests that swap it); plugin host StopPlugin waits for stdout/stderr reader goroutines before reuse so -race is clean on disable/enable; Dependabot Node 20 note in .github/dependabot.yml.
  • Docs: TESTING bench section; coverage tables refreshed from go test -coverprofile=mergedcoverage ./...; hook example lives under _example_hook; prose uses ASCII hyphens where edited.
  • Deps: golang.org/x/crypto, golang.org/x/term, modernc.org/sqlite.

v0.11.0-beta.4

Released 2026-04-09. Compare from beta.3. E2E edit consistency; deterministic theme cycle; security scanner vs govulncheck docs; .gitattributes LF normalization.

v0.11.0-beta.3

Released 2026-04-09. Compare from beta.2. Keystore v3 and config/path fixes; web admin refresh; plugin SDK context and host fixes; DB smoke CI; Go 1.25.9; demos, E2E docs, and release asset workflow updates.

v0.11.0-beta.2

Go 1.25.8 toolchain/docs; -doctor and env reflection improvements; terminal chrome and :msginfo metadata; license cache and server hardening; static release zips + linux-arm64 for Termux.

Earlier

  • v0.11.0-beta.1: Multi-DB (SQLite / Postgres / MySQL), reactions, read receipts, message state, serialized WS writes, admin TUI (PR #83).
  • v0.10.x: Core chat features (edit/delete/pin/search, DMs, channels, E2E files, plugins), -doctor, Docker, Caddy TLS proxy docs (deploy/CADDY-REVERSE-PROXY.md), config/.env precedence.