# Changelog All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows independent [Semantic Versioning](https://semver.org) for the plugin and the server. ## [Unreleased] ## [1.10.4], 2026-10-15 A full review of the code (plan 018, phases 1 and 2). ### Fixed: data and security - A device now refuses changes from others for paths Havemind never syncs (for example `evil.html` or `.git/hooks/…`); such a change is parked. - A failed read of `.obsidian` no longer deletes every setting, theme and snippet on every device. - When many notes go missing at once at startup, Havemind holds the deletes and asks before removing them on every device. - A conflict copy you edited is never overwritten when the same change arrives again; a deleted-vs-edited conflict says "Deleted on another device" instead of an empty note, and Keep theirs moves the note to trash. - `Note.md` from one device and `note.md` on another become a conflict copy instead of a stuck change. - Agents: a write without text no longer empties a note; a run is never repeated (and paid for) because the server blinked; folders like `havemind/` or `.Obsidian` are refused as `Havemind/` and `.obsidian`; two agents with similar names no longer share a folder; Codex and Cursor stay off on servers. - Server: each vault keeps its own agent login, so one vault's owner cannot change another vault's agents (an existing login moves to the vault on a one-vault server); errors never show internal details; a device left waiting more than an hour must be invited again. - Installer: an unattended re-run no longer prints a new owner code; files with secrets are private from the start; a failed download or a missing or old Docker Compose stops with a clear message. ### Fixed: what you saw - Invite someone and Add agent only for the owner. - Typing a code or a key survives the pane refreshing; the pane no longer jumps to the top. - The six-digit code goes straight into the "wants to join" card. - A fresh vault on a new device no longer reports a settings file as a conflict; when files are held, the notice names them. - Old requests to join disappear when they expire and after you disconnect. - After Undo, an agent does not redo the same work; Inbox sorter adds only tags a note does not have. ## [1.10.3], 2026-10-15 ### Fixed - 1.10.2 never reached the plugin catalogue: the Server URL field's example address looked like a real tailnet name, and the distribution release refuses any. The example is generic now, and a test applies the same rule to the plugin sources before a release. ## [1.10.2], 2026-10-15 ### Fixed - A new invitation showed the code and, under it, an empty box that read as a place to type. It is one box now. - Joining with an invitation or the installer line no longer asks for a server URL; the field appears only for a pairing token without one. - The agents service no longer shows as unhealthy: it inherited the server's healthcheck, which calls a port it never opens. Run the installer again (or pull the new compose file) to pick it up. ## [1.10.1], 2026-10-14 ### Fixed Found by running all five ready-made agents live on a fresh server: - After the agents service restarted (every server update), every agent was skipped in silence: locks from the old container carried the same process id as the new one. A lock now carries a token of its own process. - Agents now know who changed each note, so a daily summary names people instead of "author unknown". - An agent that thinks with Claude Code now sees its own notes, so it can update one it wrote before (the daily summary) instead of skipping it. ## [1.10.0], 2026-10-14 ### Added - AI agents as members of the vault (plans 014 to 016). People, Add agent: pick a task (Inbox sorter, Daily summary, Link gardener, Meeting to tasks, Clip digester, or Custom) and it joins with one click; no invitation, no command, no six digits. It works in the background on its schedule, only when people changed something, and only in the folders its task note (Havemind/Agents/.md) names. - Agents are yellow, each with a pixel icon, in the flower, People and Activity; the approval card says "As AI agent". - How agents think: your Claude subscription (claude setup-token; your server installs Claude Code on first use, it is not in the public image), an Anthropic API key, or any OpenAI-compatible model (OpenRouter, OpenAI, Gemini, Ollama). The login is a 0600 file on your server, never in the database or its backups. A running agent sends the changed notes to that model. - Undo only covers what this device's Activity remembers (200 changes). - Undo on an agent's row: everything it changed goes back. - Server: member kind (migration 008), agent enrollments (009), the agents service in the installer and the image, and a havemind-agent command with an MCP server for Claude Code. ### Changed - Over the invite form the status is one compact line. ### Note - Back up before updating the server: migrations 008 and 009 cannot be undone, and an older server refuses a newer database. ## [1.9.2], 2026-10-14 ### Fixed - Installer: run as `curl ... | bash`, it stopped silently after "Your vault" without printing the code, because `docker compose exec` read the rest of the script from stdin. The script now runs from a function that bash reads in full first. Tested piped on Ubuntu 24.04 arm64, fresh and re-run. ## [1.9.1], 2026-10-14 ### Fixed - Installer, found on a fresh Ubuntu 24.04 arm64 machine: the server could not open its database because the new data volume was owned by root; the installer now gives it to the server's user before starting it. - Installer: a run that stopped before owner setup now sets the owner up when run again, instead of asking for a new code from a server with no owner. - Plugin: the connect form names the installer line and says the Server URL can stay empty for it. ## [1.9.0], 2026-10-14 ### Added - One-command server install for Linux and macOS (`install.sh`, attached to each release): Docker and Tailscale through their official installers after asking, the published image, Tailscale Serve only, never Funnel. It ends with one line to paste. Guide: `docs/install.md`. - Server image `ghcr.io/mikolajsapek/havemind-server` (amd64, arm64) with build provenance, published on every release. - Server CLI prints the pairing code as one line, `hm_pt_...@`, after setup, create-vault and rotate-pairing. - Plugin: the connect form takes that one line; the "I'll run the server" screen shows the one command. ### Fixed - Server: a restart no longer pushes the next scheduled backup a whole interval out. When the newest backup is recent, the first run is armed for the time it has left, so backups stay daily across deploys instead of leaving a gap of up to 47 hours. ## [1.8.2], 2026-10-14 ### Fixed - Plugin: in the history window, picking two versions quickly could show the first one's diff beside the second one's mark; only the latest pick draws. - Plugin: the history window reserves the diff's space up front, so it no longer grows and moves the list under the pointer on the first pick, and says what to do before a version is picked. ## [1.8.1], 2026-10-14 ### Fixed - Plugin: a diff, in the history and in the conflict window, shows the changed lines with three lines around each and folds the rest into "... N unchanged lines"; a long note used to show in full. - Plugin: the history window marks the picked version, puts Restore above the diff, and says when a version is the same as the note now. - Plugin: the conflict and history windows get the pane's design tokens. They render outside the pane, so their hairlines and accent resolved to nothing. - Plugin: modal titles drop the hexagon icon (plan 010, fewer icons), and the history rows read like the Activity list. - Plugin: a history or version that fails to load is logged to the console with its error, besides the message on screen. ## [1.8.0], 2026-10-14 ### Added - Plugin: "Show history of this note", in the command palette and the file menu (plan 012). Every synced version of the note with who made it and when it reached the server, the difference between a picked version and the note now, and a two-step Restore that writes it back as a normal edit. Plugin-only: the time comes from the `serverTime` every server already puts on each receipt. ### Changed - Plugin: the stored revision log moves to format 2, which carries each revision's accepted time. A log stored by an earlier version is read again in full once, on the first connection after the update. ## [1.7.5], 2026-10-14 ### Changed - Plugin: the Activity feed survives a restart. The last 200 entries are kept in the device's client database (never data.json), saved at most once per burst, emptied on Disconnect and deleted with Reset. ### Fixed - Plugin: invitation expiry reads as a clock time ("Expires at 16:05") for the owner, and the joining device now sees it, together with the name of the person who invited it. - Plugin: Rejoin, Remove, Retry, Discard and Retry now are 44px tall on a touch screen. ## [1.7.4], 2026-10-14 ### Fixed - Plugin: "Open activity" opens the pane on the Activity tab, not on whichever tab was last open. - Plugin: "Connect to Havemind" is offered only while nothing is connected; on a live vault it only swapped the pane for the connect form. - Plugin: a pending retry ("Retrying...") offers Retry now, as offline does. - Plugin: Getting started no longer tells a connected user to paste a token "below", where there is no form. ## [1.7.3], 2026-10-14 ### Fixed - Plugin: the stylesheet no longer uses `!important`, which the Obsidian plugin review flags. Reduced motion now names each animated rule instead. ## [1.7.2], 2026-10-13 ### Fixed - Plugin: on a phone the pane's More options (Sync now, Getting started, Disconnect, Reset connection) could not be reached. 1.7.0 left the button to Obsidian's view header, and the sidebar drawer the pane opens in on a phone shows none. The pane now draws its own "..." next to the tabs on every platform. ## [1.7.1], 2026-10-07 ### Changed - Plugin: the flower's core spins while this device sends and turns green when everything is on the server; a conflict keeps it violet. The status bar hexagon is green when synced too. ### Fixed - Plugin: the first-run screen opened with the old "Not connected" row and its two network lines above the empty flower. Reconnect and reset keep the row, it carries the way out. - Plugin: on the join screen the token and server fields ran 24px past the pane's right edge, and the first line sat on the pane's top edge. ## [1.7.0], 2026-10-07 ### Changed - Plugin: a new, quieter pane (plan 010). Three text tabs, Status, Activity and People, with no header strip and no Connect tab. Sync now, Getting started, Disconnect and Reset connection moved into the More options menu, which on a phone is the view's own menu. - Plugin: the Status tab draws the vault as a flower: the server in the middle, one hexagon per device, initials for the others. Its shape is the state: filled in sync, dashed when this device cannot reach the server, split in red on the other side of a conflict, "?" for a device waiting for approval, "+N" past six devices. One title and one line say the rest. - Plugin: Activity groups changes by day and leads with the note's name, then who changed it and when. - Plugin: the status bar shows the state as a small hexagon and counts what waits: "3 to send" while syncing, "3 waiting" offline, "2 notes" in conflict. - Plugin: the first-run screen leads with the empty flower and two choices. ### Added - Plugin: a device waiting for approval shows as a card in People, with a button that opens the code entry. - Plugin: Disconnect asks first and names the server it stops syncing with. ## [1.6.0], 2026-10-06 ### Fixed - Plugin: a queued local change, including a 25 MiB attachment, used to sit inside data.json until the server accepted it. It now goes to the device's local database first, as intended, so data.json stays small. - Plugin: a local change is now saved in one write instead of four. A crash or a full disk in the middle of a save can no longer leave a queued change without its file identity or the saved state half-recorded. A recovery journal covered most of these gaps since the duplicate file ids of 2026-09-19; the delete of a never-sent file was not covered. - Plugin: a corrupt sync state recovered from its backup right after the upgrade can no longer drop the list of files. ### Changed - Plugin: the list of files the plugin tracks now lives inside the sync state instead of a separate `pushProducer` entry in data.json. On first start the old entry is moved over once and removed. Nothing to do on your side. Devices on 1.5.9 and 1.6.0 sync with each other normally. No server change and no deploy needed. - Plugin: internal cleanup. The glue between the two stores (local-base-lifecycle and the seed hooks) is gone, a test now replays a real, anonymised 1.5.9 data.json through the migration, and a timing test in the protocol package no longer flakes. ## [1.5.9], 2026-10-06 ### Fixed - Plugin: when the server had already accepted the same merge from another device, the plugin kept a full copy of the note in its sync state forever, and that file is rewritten on every save, phones included. It no longer keeps that copy, and copies made by a recovery are dropped after 7 days. On one device the file shrinks from about 577 KB to about 110 KB as the old copies age out. ## [1.5.8], 2026-09-30 ### Fixed - Plugin: a change queued while the plugin was still starting up could drop out of the queue, so that edit never reached the server. - Plugin: an attachment deleted on another device is now deleted here too. It used to stay, with an empty conflict copy next to it. - Plugin: a server whose responses carry fields this version does not know no longer stops a device from joining. - Plugin: stored copies of queued changes no longer pile up in the device's local database, and Reset connection deletes the database it leaves behind. ### Changed - Plugin: opening the app no longer reads and hashes every file. A file whose size and modification time match what the last start saw is skipped. - Plugin: each change carries a note's text once instead of twice, so an edit uploads and stores half as much, and notes of roughly 250 to 490 KiB that were refused as too large now sync. Merges keep the old form, so devices on older versions still recognise the same merge made on both sides. - Plugin: sending and receiving a large attachment takes about a third of the time and a fraction of the memory it did (a 25 MiB file: about 3 s instead of 9 to 12 s, measured on a computer). - Server: `HAVEMIND_TRUSTED_PROXIES` (empty by default) names the proxy in front of the server, such as `tailscale serve`, so each client gets its own pre-login rate limit instead of all of them sharing one. - Which paths sync is decided in one place, the protocol package, so the plugin can no longer queue a path the server would refuse. - Plugin (internal): notes and attachments share one apply flow, `main.ts` is split into modules, the longest files carry half the comment lines, and duplicated helpers, eight-parameter functions and an apply branch that never ran are gone. ## [1.5.7], 2026-09-29 ### Fixed - Plugin: an incoming change this device cannot apply (like the PDF iOS refused to read on 26 September) no longer stops the whole vault. After three tries it is set aside, listed in the pane with its reason and Retry and Discard, and everything else keeps syncing. ### Changed - Plugin: Restore in the Activity feed now really restores. It fetches the note's text from that revision in the server history and writes it as a normal edit. It used to add a feed entry only. - Plugin: the status bar names who last edited the open note, from the author the server stamps on each revision, so it survives a restart. - Plugin: People no longer shows an invented connected/disconnected state; Rejoin is offered on every other member. The owner can reject a waiting device. ### Removed - Server: encrypted checkpoints (`havemind checkpoint`, plans/006) and the `@havemind/crypto` package with its libsodium dependency. They were never enabled in production; backups stay plaintext on the host and are encrypted off it by restic, as the docs now say. - sync-core: the revision DAG and recipe modules the old Restore used. - Plugin: two one-time data migrations. The July canonicalization rebase predates the public release; the September mapping compaction now happens on the next ordinary save of the producer state. - Plugin: the author overlay and its Show authors toggle. It coloured a whole note by an in-memory entry lost on restart; the status-bar label replaces it. sync-core's provenance module went with it. ## [1.5.6], 2026-09-29 ### Fixed - Plugin: conflict resolution never loses both versions or locks a copy, and the automatic sweep never overwrites a note edited mid-merge or a same-named note of a different file. - Plugin: the panel says why a sync failed; Sync now and Retry run one cycle instead of rebuilding the connection, and never show Synced over a refused session. - Plugin: one quarantined change no longer blocks every later edit of that file, a full vault quota no longer quarantines the whole queue, and Retry never silently drops a change whose queued copy was lost. - Plugin: a failing upload no longer stops the device receiving changes, and instant updates keep arriving while an apply waits on an unsaved editor. - Plugin: Reset connection asks first; Disconnect and Reset are not undone by a connection still being built. - Plugin: a rejected guest is no longer stuck failing every start, a case-only name collision no longer stops the whole vault, and a guest device stops getting 403 and 401 at every start. - Plugin: a half-typed approval code survives a repaint, and a huge conflict diff no longer exhausts memory. - Server: a device that keeps syncing stays signed in; the session deadline slides on every refresh instead of ending 30 days after pairing. - Server: attachments up to the advertised 25 MiB are accepted, and a slow upload is no longer cut off after 60 to 90 seconds. - Server: a replayed old refresh token still burns its session, junk pre-auth traffic can no longer starve token refresh, `cleanup-stale` keeps onboardings in progress, and backup intervals above 596 hours no longer run back to back. ### Changed - Plugin: faster and lighter on phones. data.json is read once instead of on every access, no longer holds the text of every note, and a sync-state save is one write; a remote change costs fewer writes; `.obsidian` is polled every 30 s and only changed files are read; the author overlay costs nothing while off; a new connection reads only the revision history it lacks; obsolete revisions are skipped before download; attachments are no longer kept in memory. - Server: expired tokens and idempotency records are pruned hourly, each pushed payload is hashed once, and backups hard-link unchanged blobs instead of copying them again. - README: no longer claims line-level history or live presence, and says which file types are not synced. ### Removed - The unused database key (`HAVEMIND_DB_KEY_FILE`, `generate-db-key`); the live database was never encrypted with it, and the docs now say so. - Dead code: the unscheduled end-to-end encryption module, unused sync-core modules, the old server invitation path, the plugin's second secret store and IndexedDB queue, the join-time bootstrap phase in the plugin, and the unregistered Activity view. ## [1.5.5], 2026-09-26 ### Fixed - Mobile devices stopped receiving updates at the first changed attachment. Before applying a remote change the plugin read the file on disk as text to check for unsaved edits, even when no editor had it open. iOS refuses to read a PDF as text, so every sync cycle failed there and the device stayed offline. The disk is now read only when an editor has the file open. ## [1.5.4], 2026-09-26 ### Fixed - A connected device no longer downloads every file in the vault each time Obsidian starts. The startup identity check fetched the full contents of all files, PDFs included, before the first pull; on a phone that restarted before it finished, updates from other devices never arrived. The check now downloads nothing unless the vault holds a local file sync does not know yet. ### Changed - The release script publishes the full README to the distribution repository instead of replacing it with the short plugin README. ## [1.5.3], 2026-09-25 ### Changed - The test suite runs about 17 seconds faster. Three slow tests now prove the same behaviour with less work: the refresh-token replay cases are listed explicitly instead of sampled 1000 times, the binary payload ceiling is checked with a 1 MiB attachment plus an explicit bound for the 25 MB file cap, and the 1.5 MB encryption round trip no longer fills its payload through libsodium's slow random generator. No change to plugin or server behaviour. ## [1.5.2], 2026-09-22 ### Changed - Installation and update guidance now consistently directs users to the Obsidian Community directory. The plugin no longer documents BRAT as a distribution path. ## [1.4.10], 2026-09-14 ### Fixed - The adjacent-line merge shipped in 1.4.9 could drop a line. It checked whether the other side had touched the line the walk was standing on, but not the rest of the span a multi-line change covers, so a change overlapping on its second line passed the check and was then skipped over. CI's property battery found it after 437 random cases, having lost the text "foo" from ancestor "b\n", local "# h\n- a", remote "b\nfoo". Overlap is now tested across the whole span, and such a region falls back to a conflict copy. ## [1.4.9], 2026-09-14 ### Fixed - Two people editing neighbouring lines no longer produce a conflict copy. The merge groups changes that sit within one unchanged line of each other into a single region, and any region both sides had touched failed outright, even when each individual line had been rewritten by only one of them. That is the commonest shape of a shared vault: consecutive table rows, list items, or a heading and the paragraph under it. Such a region is now resolved line by line, and only a line claimed by both sides still becomes a conflict copy. The recovery is deliberately narrow. It applies when every change in the region replaces ancestor lines one for one; an insertion, a deletion, or a change in line count still falls back to a conflict copy, because line ownership alone cannot say where the new text belongs. A deletion next to someone else's edit stays a conflict for the same reason: combining them would silently drop a line the other person was working around, and nothing in the text says whether they meant to keep it. ## [1.4.8], 2026-09-11 ### Fixed - A remote delete could take an unsent local edit with it. The delete path checked only that the revision owned the file, never that the copy on disk still matched the last synced version, so a note edited while Obsidian was closed could be removed with no conflict copy left behind. It now compares the content first and writes a conflict copy when they differ, the same guard the rename path already had. - Renaming onto an occupied path destroyed the original. The destination was checked only after the source had been deleted, so the operation correctly reported a conflict while the file the user had been working on was already gone. The check now runs first. - The two choice cards on the disconnected screen overlapped, the second sitting on top of the first card's description, with text running past the pane edge. ### Added - The People tab reads the member list from the server, so everyone in a vault sees the same people. An offline device keeps showing the list it last knew rather than emptying the panel. - Activity names the author of a remote change. The author travels with the revision from the server, so it is never inferred: a change from someone the roster does not know reads as a remote edit rather than the wrong name. ## [1.4.7], 2026-09-05 ### Fixed - A request could hang indefinitely. Every outgoing call is now bounded by a timeout, so a stalled network leaves the pane responsive instead of pinned on a spinner that never resolves. ## [1.4.6], 2026-09-05 ### Fixed - A tap could leave the pane looking unchanged until the next repaint. Every tap path now repaints immediately. ## [1.4.5], 2026-09-04 ### Performance - Only the activity rows that will actually be drawn are formatted, so a long history no longer costs work nobody sees. ## [1.4.4], 2026-09-04 ### Performance - The rendered activity list is capped, bounding the work a busy vault can put on a phone. ## [1.4.3], 2026-09-04 ### Performance - Base64 encoding runs in chunks rather than byte by byte, which is what made attachment sync slow on mobile. ## [1.4.2], 2026-09-04 ### Performance - The conflict scan is cached between vault changes, and repaints are coalesced while taps stay immediate. ## [1.4.1], 2026-09-04 ### Fixed - The scroll rule no longer depends on `:has()`, which older mobile webviews do not support; it uses a class instead. ## [1.4.0], 2026-09-04, Mobile support ### Added - Mobile support: the pane fills the screen on a phone, with touch targets and safe areas sized for it (`isDesktopOnly: false`). ### Fixed - Migration comments restored and applied checksums frozen on the server. ### Internal - The plugin UI reached its 250-line-per-module ceiling, closing UI-03. ## [1.3.0], 2026-09-03 ### Added - The push channel resumes when the app returns to the foreground, so a phone that was backgrounded picks changes straight back up. ## [1.2.3], 2026-08-31 ### Fixed - Unloading or reloading the plugin cancels the work still in flight. A sync retry timer scheduled moments earlier could fire after unload, and a connection attempt awaiting onboarding I/O ran to completion regardless of whether the plugin was still there to receive it. Retry timers are now cancellable and released on teardown, and the connect drive takes an `AbortSignal` that `onunload` and `disconnect` both trigger. ### Internal - The generated plugin class list no longer collects CSS custom properties. `list-plugin-classes.mjs` greps identifier-shaped strings, so `--havemind-first-run-pad` read as a class the moment a test named it, and eleven tokens had already entered the list the same way. The list feeds `stylesheet-conflicts.test.ts`, whose whole job is to fail on a rule for a class nothing renders, so padding it with tokens weakened that check. ## [1.2.2], 2026-08-31 ### Fixed - The first-run screens no longer run into the edges of the pane. The chooser and the host path mount their blocks directly on the view, which had given up its own padding so the header could span the pane, so the heading, subheading, option rows and the self-hosting link sat flush against the frame while the hints between them stayed inset, leaving the text on two left edges in a 300px sidebar. - The first-run screens can be scrolled. Scrolling and the 16px of air below the last line come from the tab body on every connected screen, and these screens render none, so in a pane shorter than its content the overflow could not be reached at all and the host path's "I've done this, connect" button was out of reach. ### Added - A browser preview of the first-run screens (`npm run dev:preview`), which renders the shipping section renderers and stylesheet outside Obsidian so width- and height-dependent layout defects are visible before release. ## [1.2.1], 2026-08-27 ### Fixed - The panel's keyboard listener is removed when the plugin unloads. - Release metadata is checked before publishing, preventing a mismatch between the plugin package, the manifests and `versions.json`. - The local test deployment builds once, installs the same artefacts into both test vaults and verifies their hashes. ## [1.2.0], 2026-08-24, Panel Redesign & Connection Reliability ### Added - Redesigned Havemind sidebar with Status, Activity, People, and Connect tabs; the Connect tab brings sync, recovery, server, and disconnect controls into one place. - Owner invitation composer has an explicit Close action, so it never traps the user in the create-connection screen. ### Fixed - The sidebar tab uses the Havemind hexagon in Obsidian's top bar. - Server context appears after the overflow-menu actions rather than splitting them. - Copy invitation confirms success only after clipboard access succeeds and provides a manual-copy fallback when it does not. - Unexpected connect-flow errors are reported in the panel rather than leaving an unhandled asynchronous error. ## [1.1.8], 2026-08-20 ### Added - The distribution repository now attests its own release artifacts. The monorepo already attested the same bundle, but that attestation is recorded against the monorepo, someone installing from the distribution repo could not verify it there. The release people actually install is now attested against the repository it is served from. ## [1.1.7], 2026-08-18 ### Added - First release built and attested by the release workflow, so `main.js` and `styles.css` carry verifiable build provenance. ## [1.1.6], 2026-08-18 ### Added - Release artifacts carry GitHub build-provenance attestations, so `main.js` and `styles.css` can be verified as built by CI from this repository. Closes the gap where a release was trusted on account control alone. ### Documentation - README states what the plugin accesses and why: vault-wide path enumeration (needed to pair files with revisions and detect renames) and clipboard writes (the invitation Copy button; the clipboard is never read). ## [1.1.5], 2026-08-18 ### Fixed - The "Self-hosting guide" link actually opens the guide. 1.1.2 corrected the URL but the link stayed inert: a bare `` inside a plugin view does not reliably reach the browser. It now opens the URL explicitly and carries Obsidian's `external-link` class. Covered by a test that asserts the click behaviour, not just the URL value. ## [1.1.4], 2026-08-18 ### Changed - Plugin description rewritten to lead with what it does, matching the convention of the community catalogue. ## [1.1.3], 2026-08-18 ### Fixed - Selecting **Done** in the owner's Create-connection panel returns to the connection view instead of leaving the composer open. The composer takes render priority over the status row, so a connected vault previously showed no "Connected, synced" anywhere and read as if sync had dropped. ## [1.1.2], 2026-08-18 ### Fixed - The "Self-hosting guide" link in the getting-started panel opens the guide instead of doing nothing. It pointed at a repo-relative path, which Obsidian resolved against its own internal origin rather than GitHub, so the click went nowhere. ### Security - Development dependency advisories cleared (`brace-expansion`, `nanoid`, `postcss`). None of these ever shipped in the plugin bundle or the server image; the fix keeps the dependency scan clean. ### Documentation - The changelog, README and security policy describe the shipped release. They had been left describing `0.9.0` across four subsequent releases, including claiming a supported-version range that matched no published build. ## [1.1.1], 2026-08-13 ### Added - Graph view settings (`graph.json`) sync semantically: colour groups, filters and display preferences mirror between devices, while volatile view state (zoom, pan, node positions) stays local. ### Changed - `.obsidian/` appearance settings resolve by last-writer-wins instead of producing conflict copies. Configuration is not prose; a conflict artifact for a settings file was noise, not safety. ## [1.1.0], 2026-08-11 ### Added - Author overlay wired into both the editor and reading view, with a working settings tab. - Command palette actions for the plugin's commands, with keyboard and screen-reader access. ### Fixed - **Security:** the rejoin flow no longer treats knowledge of a `(membershipId, deviceId)` pair as proof of identity. Rejoining now requires a per-device secret verified server-side, closing a device-impersonation path. Devices paired before this release fail closed and must re-pair. - Owner mutation endpoints (revoke, rejoin-grant) are rate limited. - Vault storage accounting charges each distinct blob hash once, so a duplicated attachment no longer counts repeatedly against the quota. - Status semantics are honest: retrying and deferred states are distinct, timestamps are human-readable, and synced appearance changes apply immediately. - Skipped files are named with their reason in the console behind the reconcile summary notice, instead of a vague "markdown only". ### Security - Home LAN addresses redacted from the public tree; the privacy scan widened to cover RFC1918 `192.168.x.x` ranges, not only Tailscale CGNAT. ## [1.0.1], 2026-08-08 ### Fixed - The plugin no longer detaches its own leaves in `onunload`, per the Obsidian community catalogue guidelines. ## [1.0.0], 2026-08-08 First stable release. The seven-day pilot closed on 2026-08-07 with zero data loss across real two-device use from 2026-07-25. > **Still disposable vaults only.** Content is stored on the server in > plaintext; the trust boundary is the machine running the server. End-to-end > encryption remains out of scope, see the README security model. ### Added - Scheduled server backups with a restic pipeline and a verified restore drill (the explicit gate for tagging 1.0). - `.obsidian/` configuration mirroring across devices via adapter polling, scoped to an explicit appearance allowlist. - Multi-vault isolation: vault-scoped bootstrap selection, device revocation, and rejoin-grant device binding scoped to the grant's own vault. - Corrupt pairing state is detected at connect and offers a reset instead of failing opaquely. ### Fixed - **Breaking:** configuration sync is an explicit appearance allowlist. Plugin code never syncs under any circumstance, `.obsidian/plugins/` is excluded in full, enforced at two independent layers. - Backup integrity is verified (`PRAGMA integrity_check`) and prune verifies all retained snapshots; unsafe backup ids are rejected. - Repeated configuration-poll failures surface via a throttled notice rather than failing silently. ### Security - CI scrubs private infrastructure values, gates releases, and pins actions. - Dependency advisories for `fast-uri` and `find-my-way` patched. ## [0.9.0], 2026-07-24 First feature-complete build for the two-person technical alpha. Distributed as a three-file Obsidian artifact (`main.js`, `manifest.json`, `styles.css`) via GitHub Releases. > **Alpha, disposable vaults only.** The pilot payload format is plaintext and > has no end-to-end encryption. Do not connect a vault with real or sensitive > notes. ### Added - Two-way sync of Markdown notes with line-level history, and of image attachments (PNG/JPG/GIF/WebP/SVG) and PDFs up to 25 MB, synced byte-for-byte. - Append-only history with zero silent overwrites: concurrent edits land as conflict copies under `Havemind Conflicts/`, both versions preserved. - Authorship throughout: an Activity panel showing who changed what, a stable colour per author, and one-click restore of any previous revision. - Presence roster so the vault owner sees who is connected. - Rejoin without re-pairing when a device session drops. - Human-verified onboarding via a 6-digit code, with identity bound server-side at approval and never trusted from the client afterwards. - Opaque, append-only server (Fastify + SQLite): content-addressed blob store, forward-only checksummed migrations, refresh-token rotation with reuse detection, and per-device rate limiting. ### Notes - `.obsidian/` and Havemind's own device state are excluded from sync by two independent guard layers. - End-to-end encryption is a hard gate before any real vault is connected; the disposable plaintext pilot will not be upgraded in place.