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*.ps1helpers; pattern is gitignored. Chocolatey remainschoco packinpackaging/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 withCloseDB. No schema migration (#126, #127). - Docs: TESTING notes that CI golangci-lint is govet/ineffassign/staticcheck SA* and that gopls
unusedparamsis 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 upgradeon 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/BanUserreturn errors so callers claim success only onnil; 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:KickUserrequires an active WebSocket connection, disconnects the target, and applies a 24h temporary ban; offline or never-connected users returnErrKickNotConnectedwith notempKicksentry orban_historyrow;:ban/BanUserremain offline-capable (#116). - Server: Fix: reject empty or whitespace-only plaintext on
text,dm, andeditwhenencryptedis false (System reply, no persist/broadcast); encrypted opaquecontentis not treated as empty (#117). - Server: Fix: SQLite
InitDBappliesbusy_timeout/ WAL / related pragmas via the DSN on every connection and setsMaxOpenConns(1)/MaxIdleConns(1), so concurrent inserts no longer fail withSQLITE_BUSYfrom one-shotPRAGMA+ the defaultdatabase/sqlpool (#118). - Server: Fix: active permanent bans and unexpired temp kicks load from
ban_historyon hub start (expires_atNULL = permanent; non-NULL = kick expiry), so moderation survives process restart. Pre-upgrade open rows withoutexpires_atload 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 orban_history.expires_atare 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 andschema_versionis recorded only after a successful apply. - Server: Connected-user lookups (
KickUser,kickUser,ForceDisconnectUser,broadcastDM) use an O(1)clientsByUsernamemap underclientsMutex. - Server: Fix:
:ban/:kickclose any openban_historyrow 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 byidon 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 withallminusST*/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);
SetReadLimituses that ceiling above policy wire size so gorilla does not close with empty 1009 before the reply can flush. Residual over-ceiling reads still logErrReadLimitwithout 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
sizeand 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.OpenRootscoped writes andfilepath.IsLocalentry 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
senderon 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.EscapeTextand shown viapowershell -EncodedCommand; macOSosascriptusesstrconv.Quotestring 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-valueomitempty). - 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/thumbsdownand:unreact,:thumbsup,:thumbsdown; when E2E is on and server search returns no matches, aSystemline notes ciphertext-only matching. Charm v2: Bubble Tea, Bubbles, and Lip Gloss oncharm.land/*/v2(tea.View,KeyPressMsg, bubbles setters). Long URLs wrap at path boundaries; wrapped segments keep hyperlink style and OSC 8Style.Hyperlinkhrefs (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; ephemeralSystemfeedback 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=truewhen unset; outbound messages stamped to the sender's channel; typing, reactions, and read receipts channel-scoped;:backupSQLite-only; admin TUI mouse scroll on tabs and tables;:cleardbclearsuser_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;.gitignoretracks 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_ORIGINSallowlist.getClientIPand web-admin login rate limiting honorX-Forwarded-For/X-Real-IPonly when the immediate peer is inMARCHAT_TRUSTED_PROXIES(comma-separated IPs or CIDRs). - Client: Direct messages use the same E2E wire path as channel
textwhen encryption is enabled (encryptedplus 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 channeltext. - Plugins: Plugin store downloads validate SHA-256 checksums before extraction (HTTP and
file://), reject oversize archives, parsefile://paths correctly on Linux and Windows (registry and download URLs viaplugin/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.channelcolumn (defaultgeneral) with startup migration; channel messages persist and replay on the correct channel. Direct messages storerecipient; reconnect history includes DM rows only for sender and recipient.TypingMessagewith non-emptyrecipientuses 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.jsonunder the client config directory, footer shows the active DM peer, commands:dm/:dm off/:dms/:dmhide. Fix::dmhideand:dmshandled before:dm(prefix collision). Typing: DM compose sends optionalrecipienton 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
-doctorreportsdm_state.jsonand E2E key source; server-doctorincludes 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 beforechoco 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
-doctorintegration. - Plugin SDK:
RunStdio/HandlePluginRequeststdio loop; echo sample uses the SDK; docs and README plugin examples aligned (GetConfig, Marshal);plugin/sdk/covgitignored; CI runs nested plugin modules (fmt, govulncheck). - Tests / CI: Server loadverify benches and rate-limit coverage;
-doctortests use the injectableosEnvironhook underenvironMu(no parallelbuildEnvLinestests that swap it); plugin hostStopPluginwaits for stdout/stderr reader goroutines before reuse so-raceis 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/.envprecedence.