Skip to content

Changelog

All notable changes to Glassy are documented here.

Recent Updates

October 30, 2026 — v2.58.0: the application server has no route handlers left, and three silent defects come back into the light

The server's route layer is finished. server/index.js went from 4,928 lines and 68 route handlers to 2,872 lines and 0 handlers, with every route in one of 73 modules. Handlers that owned boot state took that state with them: the liveness probes stay above the rate limiters (a throttled probe reports an outage the detector then believes), settings are read through a getter instead of a value frozen at mount, and the SSE manager is injected so there is still exactly one registry.

Three defects the split exposed, none of which a unit test could have caught.

  • A voice recording could not be archived, deleted, or fetched. POST /api/recordings minted ids as Date.now()-base36, while the validator accepted only numeric or UUID ids — so archive, unarchive, restore, delete and audio all answered 400 for the id the API had just issued. The validator now accepts exactly the three shapes the API produces and nothing else.
  • A folder that was not yours answered 200. Both the rename and delete handlers read result.changes off an un-awaited promise, so the 404 branches were dead and a delete that matched nothing reported success. They await now, and all five paths have tests.
  • The memory-ledger repair could never match a voice row. The ledger's key is voice_recording; the store's is voice_transcript. The mapping is now explicit, with a negative test proving a note cannot be repaired from a document's vector.

The memory ledger stops reporting failures whose vectors already exist. The reconciler now reconciles the ledger against the store at the start of every pass, instead of only counting what the backfill selected. On the public instance failed fell 157 → 2 (23 repaired on the first pass), and the runbook's invariant — failed rows that already have a vector — reads 0.

A preview deploy can no longer migrate the Clear appliance's live database. glassydev mounts the directory Clear writes; dev.sh now compares this tree's migrations with Clear's applied set and refuses to build when any are unapplied, naming both safe orders and an explicit override.

Released October 30 and deployed to the public cloud instance on October 31 — glassy.fyi and app.glassy.fyi both serve 2.58.0. The Clear appliance remains on v2.57.0 until its own cutover.

October 30, 2026 — v2.57.0: the memory lane recovers, a note save stops selling AI, and shared facts stop drifting

A note save no longer raises the AI paywall. Saving a note fired a background re-embed; an account with no AI lane answered 402, and the client opened the upgrade modal for any 402 — so a Pro account was sold Pro. The re-embed is background work now, a background 402 never opens the modal, and a paid account sees its own status instead of a $17/mo card.

The memory lane can recover itself. One ledger key scheme, one success word, an attempts counter with a retry cap, a boot repair for rows with no account, and failures recorded where the status is read. Your own provider key now serves embeddings per user (its own model and namespace); the instance lane runs only while ENABLE_FIRST_PARTY_EMBEDDINGS=true.

Client and server stop disagreeing. canvas and vimeo notes save, the tag field refuses the 21st tag, the upload ceiling is the one users actually meet, tier policy lives in one file, and the bookmark backfill stops throwing every hour.

A document is what its author wrote. Documents record content_format; Markdown stays Markdown, HTML stays HTML, and reads negotiate.

Deployed to glassydev, prod, Clear and the public instance on October 30 — the fleet reconciles at 2.57.0.

October 29, 2026 — v2.56.0: agent content survives its editors, Trash keeps its 30-day promise, and agent keys cannot read admin surfaces

Agent-written Markdown no longer dies in the Docs editor. Documents are stored as HTML, but the editor opened the raw string and the first keystroke autosaved the flattened result. The editor now converts once on load, and both MCP document tools say what format is stored.

The notes editor keeps what the renderer shows — tables, H4–H6 and GFM checklists survive a save.

Trash shows the real deletion date and keeps its 30-day promise: a daily sweep permanently deletes older trash, and users.trash_auto_empty can turn it off.

Permanent deletes free everything — the row, the bytes, the vector and the audio file.

Agent keys cannot read admin surfaces. Both admin gates refuse a named agent key, not just the path.

October 28, 2026 — v2.55.0: the no-restart Pair flow actually works end to end

The Settings-only Pair flow now starts and verifies without a restart. Saving the encrypted Pair token starts the scheduler through the real production route, and Sync now resolves that same Settings credential before the GLASSY_SYNC_TOKEN recovery fallback. A self-hosted receiving instance can now mint its own sync token from Settings → Cloud Sync, which is the surface the Pair walkthrough needs.

The first handshake promotes the Settings placeholder instead of creating a second blank peer row. URL, encrypted token, enabled types, direction, and conflict policy stay on the same row; already-split v2.54 rows are repaired on the next handshake.

Guest Pair purchases become claimable only after paid settlement on the instance that created the checkout. Open or unpaid sessions and foreign instance stamps are refused; asynchronous payments fulfil through checkout.session.async_payment_succeeded. The founding 2,000 cap is now an atomic database assignment rather than a race-prone check-then-write.

Also: the S2S cycle budget covers handshake, ack, state/reset, and media; expired limiter windows are pruned; an oversized cycle can roll a new id immediately; and the Lane A convergence cursor recovers when every pending id sits behind its frontier.

October 27, 2026 — v2.54.0: the Pair is $79, Pro yearly includes it, and the founding 2,000

The Zero Reliance Pair is now $79 (new sales; $65 grants are grandfathered and never revoked). The Pair is included while a plan is active with Pro yearly, Pro lifetime (grandfathered), and any Clear plan — never with Pro monthly or a trial. The first 2,000 Clear members (any Clear plan) receive a permanent Pair grant and a numbered Founding badge; Clear itself is not capped and continues to sell. GET /api/capabilities now publishes clear.founding ({ cap, claimed, remaining, soldOut }) on the Clear instance, and the marketing pages read that number instead of hard-coding a cohort. The site now carries real app frames and new OG cards.

October 27, 2026 — v2.53.0: the Pair token is a setting, migrations take a snapshot, backups have a witness

The last "edit .env and restart" step in the Pair walkthrough is gone. Migration 0132 adds an encrypted sync_peers.token_encrypted; the Cloud Sync panel gains a write-only Pair token field that reports presence only and never echoes the value. The scheduler resolves the credential per cycle — persisted token first, GLASSY_SYNC_TOKEN as the bootstrap/recovery path — so pasting or rotating a token applies from the next cycle without a restart, and getSyncHealth reports credentialSource.

A migration pass can no longer begin without a recovery point. Every pass with pending work takes one consistent VACUUM INTO snapshot from the live connection, named for the last pending migration, encrypted when a backup key is set, retained to three. A snapshot failure is loud and non-blocking; a routine boot with nothing pending takes none.

A backup nobody checks became a check. scripts/check-backup-freshness.js ages the newest nightly notes-* backup with the fleet's 0/1/2 exit contract — an event backup cannot fake nightly freshness — and /api/health reports backup so an appliance can see its own state.

Also: the Lane A convergence sweep stores a per-model cursor, so a truncated page advances instead of re-trying the same rows; and every file whose SQL interpolates a table name must be registered in server/config/dynamicTableRegistry.js — the ${table} blind spot schema:check cannot see.

October 26, 2026 — v2.52.0: the appliance's owner can use the appliance's own AI

A self-host lockout, found by review rather than by an incident. requireAiAccess had no self-host branch: on an appliance the owner holds clear_lifetime but no credits, so every generation route, the embedding family, auto-tagging, semantic search, and Keep's AI endpoints answered 402 INSUFFICIENT_CREDITS against a Store that is not mounted there. The gate now short-circuits on the appliance after the BYOK branch, so your own keys still hydrate. Pinned by aiMetering.selfhost.test.js.

Checkout could charge for a free feature. Keep and Calc are core entitlements since D4, but the one-time checkout still sold them to old bookmarks and scripts. It now refuses with ALREADY_INCLUDED (409) instead of charging.

Returning from checkout landed nowhere. Success and cancel URLs were bare paths on a hash-routed SPA. One-time and Pro URLs are hash-formatted now, and the Store and Settings views consume the query once — toast, strip, fresh entitlement pull.

October 25, 2026 — v2.51.1: a one-time purchase no longer fulfils on the other instance

A security patch found by a live probe. Clear and the public app share one Stripe account with a webhook endpoint each, and both receive every event. Because the instances number users separately, a $65 Pair sale on one instance could grant the entitlement to whichever user on the other instance held the same numeric id — and overwrite their customer id. One-time checkouts now carry metadata.instance_id, and a foreign-stamped session is refused before any write, customer-id update included. A stamp that is absent is still honoured, so sessions in flight are not dropped. charge.refunded remains unscoped by design limitation — the event carries no checkout session to read a stamp from.

Also: DOMPurify ^3.3.1 → ^3.4.17 for GHSA-cg2g-xh64-v4q7, blocked by the Trivy CRITICAL gate.

October 25, 2026 — v2.51.0: Memory Lane A — the client owns its embeddings

The embeddings index is now per-instance, and the owner's browser does the work. Migration 0131 rebuilds embedding_source_pins keyed per (source_type, model), so the server-generated Gemini/768 namespace and a client-uploaded MiniLM/384 namespace pin independently. POST /api/embeddings/upload accepts client vectors with three-way provenance validation and a per-vector ownership probe (403 EMBEDDING_NOT_OWNED); POST /api/embeddings/namespace-status is the converging indicator's source of truth; DELETE /api/embeddings/namespace is the scoped re-index escape hatch.

The search seam never cross-compares namespaces. A client query vector is validated, never regenerated or accrued, and candidates filter on model and dimensions together.

Arrivals that land with no client present are marked pending_client and converge on the owner's next visit. ENABLE_FIRST_PARTY_EMBEDDINGS makes the server-side lane per-instance retireable; the shim stays on until writers converge.

October 24, 2026 — v2.50.0: Zero Reliance, fully private — the $65 perpetual Pair

The Pair is a real SKU. zero_reliance_pair_v1 at 6500 cents resolves strictly from the server-side pricing map; checkout.session.completed grants zero_reliance_pair on any account, free included, with no tier gate. A refund unwinds a sale; the founding terms protect grants from term changes, never from money back.

The peer is decoupled from the membership oracle. Settings → Cloud Sync → Syncs to persists the peer on the same sync_peers row as the other pairing settings (migration 0130), then GLASSY_SYNC_PEER_URL is the fallback and the old verification variable is backward compatibility only. The scheduler resolves per cycle, so a Settings change applies without a restart.

The setup flow is gated, the runtime is not. Cloud Settings → Zero Reliance Pair carries the founding terms verbatim and the three-step setup, gated on the entitlement or the status-aware active-Clear bundle. The pane never renders on the appliance; the appliance's half is the "Syncs to" field. deploy/selfhost/PAIRING.md is the walkthrough. The runtime pair checks nothing — that absence is the design. We sell setup, support, and reconfiguration, never operation.

October 24, 2026 — v2.49.0: Agents on the go — MCP ships on cloud in single-key mode

The 2026-09-02 "no MCP on hosted instances, ever" posture is superseded in writing (docs/decisions/2026-10-24-mcp-on-cloud.md). A cloud instance that sets ENABLE_MCP_SERVER=true mounts /mcp with the per-user gky_mcp_* key lane, tier budgets (120 calls/hour free, 1,200/hour paid), and the CLOUD_GATED_TOOL_NAMES withhold enforced server-side. The cloud serves 37 of the 42 tools.

Named per-agent keys stay appliance-only. POST /api/mcp-keys still answers 403 FEATURE_NOT_AVAILABLE on cloud, and the manifest reports agentIdentity.mode as single-key rather than named-keys. The Second Brain corpus pane is declared selfHostOnly: on cloud the corpus is operator-run, so users do not get backfill controls against the operator's embedding budget.

The agent brief gains a two-origins section: appliance is the full-power lane (42 tools, named keys, no budget); cloud is the on-the-go lane (37 tools, single key, tier-rated). GET /api/capabilities is how an agent tells which one it is talking to.

October 24, 2026 — v2.48.0: Self-host for every account, the protected sync lane, and Free as a limited-storage Pro account

Self-hosting is open to every Glassy account (D3). All three paid walls are deleted and guard-pinned: pairing tokens mint for free accounts, verify-selfhost verifies any account (the token is the anchor; tier still travels in the signed payload), and the sync lane accepts any paired account. Appliances still 404 the cloud-only routes.

Free is a limited-storage Pro account (D4). The Voice Studio gate and PRO_INCLUDED_ENTITLEMENTS are gone: Keep, Calc, Voice, and ICS calendars are plain features of the software. The free per-file cap rises to the paid value (25 MB); the total quota is the single honest meter. Workspaces get Pro parity. The one deliberate free/paid divide left is publishing — canShareIPublic requires an active payment.

The sync protection kit (D5). The peer-apply path enforces storage headroom (SYNC_STORAGE_FULL), a 1 MB per-item cap (SYNC_ITEM_TOO_LARGE), the canonical tag envelope (INVALID_TAGS), a 500-row batch cap (413 SYNC_BATCH_TOO_LARGE), and trial-grade cycle limits — 2 cycles/hour free, 60/hour paid. /api/capabilities publishes a syncLane block derived from the enforcing middleware; appliances report protected: false.

The installer tells the truth: "requires a free Glassy account," plus a lapse-degradation curve stating exactly what stops when a membership lapses and that a configured Pair keeps running.

October 24, 2026 — v2.47.0: The AI is yours — first-party generation retired, packs-only lane, BYOK default

The monthly cloud-AI allowance is retired (D1). No subscription implies an AI allowance. Pre-funded AI packs are the only first-party lane, purchasable by every account, free included. /api/capabilities publishes the AI posture: ai.firstPartyInference, ai.byok.providerTypes, ai.onDevice. Generation routes on a pure-BYOK instance answer 409 FIRST_PARTY_AI_DISABLED — a posture, never a 500.

BYOK is the default cloud lane (ENABLE_BYOK / VITE_ENABLE_BYOK default true; the server re-enforces every key write). BYOK OpenAI now serves image generation and Whisper transcription, and bookmark summaries route through the task router with per-user key resolution.

The upgrade prompt became "Connect your AI": the free on-device lane first, BYOK first-class, packs open to everyone. Every 402/409 names the free alternatives instead of "upgrade for cloud AI".

October 17, 2026 — v2.46.1: the live pass keeps its promise

The #191 fix's live verification on the deployed 2.46.0 build measured the offset's CSS var unresolved at the modal: the scrim mounts outside the layout's var subtree, so its padding stayed 0px and the rail still clipped the modal at 1150/900/768px. The layout now publishes --sidebar-width on :root (tracking the collapsed/expanded choice), so every out-of-tree offset surface — the note modal's scrim first — resolves it; the guard now binds the resolution, not just the class string. Verified live after deploy: padding 104px at the collapsed rail and the modal clearing at 1280/1150/1024/900/768px; Focus Mode hides the rail and drops the offset.

October 16, 2026 — v2.46.0: the field agents' fixes ship, and memory goes seamless

The two field-agent reports are fixed, and synced content is now memory on arrival. The Cloud Sync "Direction" control governs cycles for real (#190 — the engine reads the persisted sync_peers.direction, no longer clobbers it on conflict, and inherits the operator's saved choice on first pairing), the open-note modal clears the nav rail at every viewport (#191 — the scrim participates in the sidebar offset outside Focus Mode), and the KB's knowledge-graph panel no longer clips and truncates its own data at the fixed sidebar width (#194 — permanent 2×2 stats, stacked lists, recoverable hub titles with tooltips, plus a guard so no viewport-keyed utility can ship inside the fixed panel again). The KB search error card classifies failures: permission errors say what they are and offer no retry button; only transient errors offer "Try again."

Seamless memory (#101 completion): synced content builds and refreshes memory on arrival — peer-arriving edits re-index with force (no more stale corpus chunks until a manual reindex), synced voice transcripts build memory on arrival, peer deletes release their corpus chunks, and an hourly reconciler (ENABLE_CORPUS_RECONCILER, default on, opt out only with a strict embedding-API budget) converges anything a write path missed. The recorded decision: per-instance brains that converge — the embeddings index itself is never synced.

Self-host fixes: the Cloud Sync panel speaks per side — direction renders only on the appliance (where cycles run), each side's type toggles + conflict policy govern that side's apply, and the receiver now honors its own persisted types/policy when applying pushes (the cloud's controls became real — no wire change; direction stays initiator-only by recorded design). The in-app cookie consent banner removed — a phantom consent (it asked permission for analytics that don't exist and nothing consumed its stored decision; the standing trigger recorded: the first non-essential tracker on any surface ships together with a real consent mechanism, never after).


October 10, 2026 — v2.45.0: the field agents' findings ship

Every lane a field agent reported, verified and fixed in one release. The Knowledge Base no longer crashes on open (#180 — the graph hooks unwrapped the wrong response shape, and the test's mock agreed with the bug), glassy_search shows one source once (#188 — the text leg's voice label and the semantic leg's voice_transcript label beat the RRF dedup key; the merge now normalizes to the store's label), and search's archive contract is one rule on both legs: archived sources are excluded by default with include_archived: true as the opt-in, mirroring the list tools (#182). Failed MCP tool calls now carry a machine-readable structuredContent — error class plus offending field and bound — beside the prose (#184), so agents can branch on failure class instead of regexing the message. /api/capabilities publishes the canvas link-out contract (renderers.canvas.linkOut) drift-checked against the enforcing source (#186), and glassy_summarize's optional maxLength is reachable: the protocol delivers prompt arguments as strings, and the schema now coerces before enforcing the 20–500 bounds (#179).

Self-host fixes: glassy_obsidian_proxy works again — it now performs the full MCP session lifecycle (initialize → Mcp-Session-Id → sessioned tools/call, one re-initialize on expiry) over the direct and bridge transports (#183), and the agent-as-vault-provider lane no longer aborts by construction: VAULT_DISPATCH_TIMEOUT_MS (120s) sizes a conforming tool-using turn, caller budgets are honoured with a floor, and the write lane's idempotency is pinned (#185). Deploy note for VM installs: the pull-first deploy script falls back to building when the GHCR pull fails (SKIP_PULL=1), for hosts where the blob CDN stalls.


October 7, 2026 — v2.44.0: the agent hands off LIVE content, and the review loop closes

Three lanes that made canvas + chat surfaces second-class now work. A read-only render lane serves one note's sanitized, rendered HTML frameable and script-free, so a chat transcript can show the LIVE note instead of a mirror-file copy (GET /widget/render/<token>, owner-minted revocable tokens, #177). A canvas image lane re-serves first-party /uploads images with CORP: cross-origin, ending the base64-copy workaround (#174). A canvas link-out posts a verified postMessage from the canvas to the host, gated on CANVAS_EMBED_ORIGINS (#175). capabilities.navigation.deepLinks publishes the parameterized routes (#/notes/:id, #/present/:kind/:id, #/w/:slug/:id, #176), and the Knowledge Base gains a human-facing vault-graph panel (stats, top hubs, orphans — #145).

The review loop closes. glassy_report_review_outcome lets the agent record what it DID with the owner's answer, so the queue reads as a closed control loop (#143 ask 4) — the MCP surface is now 42 tools (37 on cloud) — and glassy_request_review takes an ISO-8601 deadline the card shows and flags overdue (#143 ask 3).


October 5, 2026 — v2.43.0: the agent surface stops lying

The twelve-issue developer-agent capability audit (#160–#171) resolved in three passes. Parity + contract: MCP writes now cover the full organisational field set REST writes (#163/#169/#171, pinned by a new mcpFieldParityGuard), glassy_delete_document gives agents a document delete lane unified through documentService.permanentDelete on both REST and MCP (#160), agent keys are named mcp_keys (#161), and resume-verification guidance was corrected (#162). Integrity + routing: the app's navigation map is published SSOT as navigation in /api/capabilities (#164/#167), mediaIntegrity was rewritten around a MEDIA_SOURCES registry (including the previously missed notes.background_image cover lane, #165), a disabled Obsidian period now answers 200 {exists:false, periodEnabled:false} instead of a client-error poll (#166), enumerated note fields are validated with a new VALUE_COERCED warning (#170), and the modal canvas fills its container (#168). The MCP surface grew to 41 tools (37 on cloud). Every write response now names what it ignored or changed — the warnings[] honesty channel an agent's cron can alert on.


October 3, 2026 — v2.42.0: Point the vault at an agent, and a dispatch that resumes

If you run a filesystem vault without Obsidian, you can now route vault reads and writes through an agent. The vault tools (glassy_vault_read, glassy_vault_append) previously needed the Obsidian stack, which left a plain-filesystem appliance with no supported path — the workaround was to invent an MCP shim behind obsidian_url that fabricated tool names nothing defined. Now you mark one Agent Gateway connection as the vault provider (Settings → Agent connections → Set vault provider), and the vault client dispatches to that agent — the one that actually holds the files — mapping its reply back to the vault shape. Only one connection can be the provider at a time, and that "at most one" rule is enforced on the server, not just in the pane, so a direct API write cannot make the routing ambiguous. glassy_obsidian_proxy still requires the full Obsidian stack, by design. GET /api/capabilities reports agentIdentity.agentVaultProvider so your agent can check instead of guessing.

A dispatch hands back a resume handle that actually resumes. On a Hermes gateway the OpenAI-compatible lane carries two session headers, and until now we only surfaced the first. The sessionId key scopes the gateway's long-term memory, but continuing a conversation needs the separate conversation id the gateway returns in the X-Hermes-Session-Id response header — which we were discarding. Passing only the key back did not resume, and made iteration more expensive than starting fresh (5.3× the tokens by the second turn in a field measurement). sendTask now returns that id as resumeId, and you pass both sessionId and resumeId on the next dispatch. OpenClaw (which resumes on the sessionId it already carries) and Antigravity (stateless) return resumeId: null. Documented on the adapter contract and in the agent brief.

Sync now says when it lost something. A pull row that failed to apply three times was silently force-acked: the content was gone from cloud, and every surface still read healthy — deadLetter was 0 because health only ever counted push failures. The apply path now logs the reason, the loss is kept as a cumulative pullDeadLetter count shown in Settings → Cloud Sync, and the recovery path (a cloud-side re-snapshot followed by sync-data/import) is documented. Sync is a state channel, not a restore path — reinstalling does not pull old content back.

Smaller fixes. A fully-local appliance's health screen no longer reports "gemini" as the embedding provider when Ollama produced the vectors — it reports what actually did. Clear members can mint the self-host pairing token their appliance needs (Settings → Self-hosting was hidden on Clear instances), and the sidebar's Agent Gateway and Agent Review items now open when clicked. The deploy's own smoke checks were repaired: two probes called a route the server never served, and the guard meant to catch that had been asserting the same phantom path.

October 3, 2026 — v2.41.0: Your agent can call the API without borrowing your password

A named agent key now works on the REST API, read-only. Until this release every script that wanted GET /api/notes had to log in as you — your email and password — to obtain a session token. That put your password in every integration's environment, made the script's reads look like yours, and broke all of them at once whenever a reinstall rotated the signing secret. The same gky_mcp_… key you mint under Settings → Connections & data → AI tools (MCP) now authenticates GET /api/* directly:

curl -s http://localhost:8080/api/notes -H "Authorization: Bearer gky_mcp_..."

It is reads only, deliberately. Any POST/PUT/PATCH/DELETE carrying a key is refused with 403 AGENT_KEY_READ_ONLY before the key is even checked, so a leaked key cannot change anything — its reach is exactly what you can already read. The key stays in the account it was minted under (an X-Account-Id header will not move it), a key in a URL query string is never accepted, and the forced-password-change wall, the inactivity lock and the account PIN lock all still apply to it. GET /api/capabilities reports agentIdentity.restReadLanes so a script can check instead of probing. Writes still require an owner session.

A fresh install no longer ships the bug-report widget turned on. It was visible on a brand-new appliance nobody had configured, because the default came from a database migration run at first boot rather than from the code that appeared to govern it. New installs start with it off; an admin turns it on under Admin. If you installed earlier and want it off, the toggle is yours — a stored "on" cannot be told apart from a deliberate choice, so nothing overwrites it. Where it is on, an individual can still hide it for themselves, and that choice now definitively wins over the admin flag.

Also in this release:

  • Knowledge Base results scroll in their own region again. A long result list used to push the whole page, carrying the search box and source filters off screen. The same fix repaired six other full-height views — Feed, Canvas, Present, Upgrade, the Agent Gateway and GlassyKeep.
  • The appliance account menu offers Extension instead of a dead Store. There is no store on a self-host box, and the browser-extension download had no entry point at all.
  • Multi-turn agent dispatch has a session handle that works. POST /api/agents/:id/task used to return the provider's per-call completion id in its sessionId field, so passing that back started a new conversation every turn and re-paid the agent's full context. It returns the real session key now, with the provider's id under metadata.completionId. Dispatch options are also validated: an unusable timeout or sessionId comes back as a warnings[] entry naming the key instead of silently aborting every request.
  • An agent asking you to approve something can say what each choice commits to. A review request may carry a separate context field and a one-line consequence per choice, rendered inside its own button so it cannot be read against the wrong one.
  • The first-boot password wall is documented for scripts. On a fresh install the seeded owner must change its password before the API serves anything — but login itself is not behind that wall, so a script saw a successful login followed by 403 on every call and reasonably concluded the install was broken. GIVE-THIS-TO-YOUR-AI-AGENT.md now gives the exact sequence.

September 30, 2026 — v2.40.7: The surfaces now describe themselves accurately

  • New: a chrome-free presentation route, #/present/<kind>/<id>. It renders one note, canvas or document with no sidebar, header or widgets — for putting a single artifact in front of someone without the workspace around it. Esc exits. It is owner-only by design (the unauthenticated surface remains #/w/<slug>/<noteId>), it reuses the existing renderers rather than adding one, and /api/capabilities publishes the route and its valid kinds so an agent can build the URL instead of guessing it.
  • Canvas notes render in the Public Window. A canvas opened through #/w/<slug>/<noteId> used to fetch successfully and then display nothing, because the window had a renderer for every note type except that one. On an appliance the Public Window is forced members-only, so this is visible to signed-in members rather than to anonymous visitors.
  • The capability manifest advertised an embedding-reindex route that has never existed. It said /api/kb/reindex, which answers 404 on every method; the real route is POST /api/kb/backfill/reindex. For an agent, a 404 on a repair path is indistinguishable from "this instance has no reindex". The value is now derived from the constant the route registers with, so the two cannot part silently.
  • glassy_note_update's is_public description names both presentation surfaces and states the appliance behaviour, so an agent deciding whether to publish a note is told who will actually be able to see it.

September 27, 2026 — v2.40.6: A rule that is enforced is not a rule that is displayed

The theme of this release is the difference between a product that has a rule and a product that only shows one.

password_must_change now gates the API. Until now the flag drove a client-side wall: an account that had not satisfied it was told to change its password, and a token that ignored the instruction kept full access to /api/*. The middleware now refuses with 403 PASSWORD_CHANGE_REQUIRED, exempting only POST /api/auth/change-password — so the account that must change its password still can, and nothing else works. Setting your own password in the admin panel now clears the flag too; previously that route was the only place in the server that cleared it, so rotating a password in the panel rotated it and left the account permanently flagged — harmless while the flag was decorative, an API lockout once it was not. An admin resetting someone else still leaves the flag set, on purpose: that is the flow the flag was introduced for.

A release cannot publish while its own commits reference issues nobody closed. A new CI gate reads the issue numbers out of a release's commit range and refuses a versioned tag whose work is still open. It was written because twenty-one shipped issues were sitting open, and the recurring "the application is broken" reports were being diagnosed against the app when the actual defect was the tracker. The gate has a narrow allowlist, and an allowlist entry whose issue has since closed fails the build — a ratchet, not a mute button.

The self-host verification rig states its own provenance. It now prints its md5 and git describe and warns when it is not at a release tag, because two issues in this tracker were filed from a stale checkout whose verdict described nobody's shipped product.

September 27, 2026 — Companion v2.20.1: error classification reads the status code

Companion v2.20.1 — a small correctness release: MCP key-fetch errors are now classified by HTTP status (err.status === 403) rather than by matching words in the error message (includes('403'), includes('not enabled'), includes('forbidden')), which misfired in both directions. No behaviour change beyond more accurate error copy. Pairs with glassy-dash v2.40.5. GitHub release v2.20.1.

September 26, 2026 — Companion v2.20.0: the manifest is the single source of truth

Companion v2.20.0 — the extension stops probing the server per-feature and reads the one document the server publishes about itself (/api/capabilities, cached 30s, stale-on-error, fail-closed). Fixes the MCP settings pane being unreachable on every server (v2.19.0 gated on a /mcp/status field no release ever emitted), the Vault tab rendering on cloud, a background poll logging users out, named-key config-snippet dead-ends, and #vault deep links stranding the popup. Pairs with glassy-dash v2.40.5. GitHub release v2.20.0.

September 26, 2026 — v2.40.5: The surface tells the truth about what this instance can do

Eighteen commits, one rule: the self-host build carries the capability, the cloud build carries the restriction — now enforced in code, reported by the manifest, and pinned by CI on both sides instead of being true in one representation and false in another.

The three review tools (glassy_request_review, glassy_get_review, glassy_withdraw_review) are gated on cloud, because the approval queue that answers them is appliance-only: a cloud agent could file a request, poll it, and wait forever on a queue nobody could open. Cloud therefore serves 36 of the 40 MCP tools and names the four it withholds in cloudGatedTools, so an agent can tell "this instance cannot do that" from "I guessed the name wrong". /mcp/tools and /mcp/status stopped reporting every loaded module and now report this instance, because loading a module is not the same claim as serving it.

September 25, 2026 — v2.40.4: Membership signatures stop being a secret everyone has

A security release with no behaviour change you will notice. It closes issue #126 properly rather than documenting around it.

Your appliance periodically asks the cloud "is this account a member?" and caches the answer. The cache entry carries a signature so that tampering with the file on disk cannot turn an expired membership into a valid one. That signature used to be HMAC-SHA256 under a symmetric key — and because the appliance has to verify the signature, the key had to be inside the appliance image, which anyone can pull without authenticating. A secret every caller can read cannot gate anything.

Signatures are now Ed25519. The cloud signs with a private key that never leaves it; the image ships only the public half, which is committed in the Dockerfile in plain sight. A public key in a public image is not an exposure — that is the whole point, and it is why rotating the old key would not have helped (the replacement would ship in the next public image and be exposed identically).

Upgrading cannot break your appliance. Investigating this found that the old mechanism was inert as well as insecure: the build argument defaulted to the literal string unset, so the cloud signed with "unset" while published appliances verified against the real secret. The two never agreed, signed caches validated nowhere, and every appliance has always re-verified on the 24-hour TTL. You are already on that path, so nothing regresses — and if your cache holds an old-format signature, it is recognised by shape, logged once as expected, and re-verified.

For operators: GLASSY_VERIFY_CLOUD_URL still needs to point at the cloud instance that holds your account, but there is no longer any key to match or configure. The SELFHOST_SIGNING_KEY build argument is gone.

September 25, 2026 — v2.40.3: A security release — the appliance image can now take OS patches

No application behaviour changes in this release. It exists because the container image was structurally unable to receive an operating-system security update, and two CRITICAL GnuTLS vulnerabilities had fixes published that no rebuild would ever pick up (CVE-2026-33845, a denial of service via a DTLS zero-length fragment, and CVE-2026-42010, an authentication bypass via a NUL character in a username).

The cause is worth knowing if you build your own images. The Debian base already ships libgnutls30, and apt-get install curl ca-certificates does not upgrade a package that is merely present — so the vulnerable version survived every build. Docker then caches that layer by its instruction text, so on any machine with a warm build cache it survived even a corrected instruction.

The runtime image now runs an explicit apt-get upgrade behind a date-stamped cache key, so OS patches land within a day of publication instead of never.

Practical exposure was low: libgnutls30 reaches this image through curl, a diagnostic tool, while the Node runtime uses OpenSSL. Upgrade at your normal pace — but do upgrade, because a scanner that stays red on your appliance is a scanner you will eventually stop reading.

September 25, 2026 — v2.40.2: An appliance serves its own pages, and a limit means what it says

If your self-hosted Glassy shows a completely blank page, this is the release that fixes it. Every earlier build decided "is this request from my own site?" from a hardcoded list of allowed origins. A production browser bundle always sends an Origin header, so that list was the only thing consulted — and it contained localhost:3000 and 127.0.0.1:3001 but not the address you were actually using. The appliance was answering 403 to requests for its own JavaScript and CSS, so the browser rendered an empty page. A custom APP_PORT, a LAN address, a Tailscale hostname or your own domain each triggered it. The default install was never affected, which is exactly why it went unnoticed through eleven releases: localhost:3000 was allowed by accident rather than by design.

Sameness is now decided from the Host header the browser derives from the URL it is fetching, so an appliance always serves its own bundle — and still refuses every other site, including one that tries to spoof X-Forwarded-Host.

Also in this release:

  • An appliance no longer crash-loops on first boot. Every cluster worker ran the default-admin seed as "count the users, then insert", so two workers could both see an empty database, both insert, and the loser's unique-constraint error took the whole boot down. The unique index is now the arbiter. A related boot bug silently skipped the remaining column migrations when two workers added the same column at once.
  • Rate limits mean what they advertise. glassy_notify says "max 10 per minute" and the login limiter says 20 failed attempts per 15 minutes, but both windows lived in per-process memory — measured on a live appliance, the 21st notification was the first refused (correct) while 40 failed logins got through against a promise of
  • Both windows are now shared across workers and survive a restart.
  • A disabled corpus tells you the truth. A registered-but-disabled external corpus used to report "not registered — register it by config", sending you to do the thing you had already done. It now names the real reason and how to re-enable it.
  • The MCP surface no longer advertises what it is not serving. If the MCP router fails to attach at boot, POST /mcp answers 503 MCP_NOT_MOUNTED with a reason and /api/capabilities reports mounted: false beside the tool count, instead of quietly claiming 40 tools while serving the single-page app's HTML.
  • Deep links work again. A shadowed variable made every id-carrying route (#/notes/<id>, #/canvas/<id>) unmatchable, silently falling back to the list.
  • Search reports a degraded lane honestly rather than returning nothing without explanation, and embedding-width mismatches name the source and the fix.

Includes everything from v2.40.1, which was never published as an image.

September 24, 2026 — v2.40.0: Your agent becomes a principal

The release that makes Glassy a workspace shared by you and your agents rather than a notes app an agent happens to call.

  • Named agent keys. Issue one MCP key per agent (Settings → Connections & data → AI tools (MCP)) instead of one shared key for everything. Each is pinned to a name, so authorship, the activity feed, agent memory and notifications all say which agent did the work. Previously identity was a label the client declared about itself in a handshake; now it is verified.
  • Your agents can remember. glassy_remember and glassy_recall store durable memories as ordinary notes — searchable, graph-linked, and private per agent. One agent's memory is never returned to another.
  • An approval loop. An agent asks you a question with 2–10 offered choices, optionally pointing at the note or document it means; you answer with a click on the new Agent Review page. A "changes requested" style answer requires a reason, so the agent can act on it rather than guess. Notes carry their state as a badge on the card. See Agent Review and Approvals.
  • An awareness lane. Agents ping you with glassy_notify (10/minute by design) and post end-of-session digests. Unanswered questions escalate to a warning at 24h and 72h — each threshold once, and escalation never answers on your behalf.
  • Agent activity. One feed over what your agents wrote and edited, plus the questions they asked and the answers you gave, with a server-generated summary that counts the whole window regardless of pagination.
  • Bulk vault ingest. POST /api/ingest walks an Obsidian vault and indexes it — resumable (a crash leaves a claim the next run reaps), idempotent (a re-run over an unchanged vault is a no-op and costs nothing), and honest (an unreadable file is recorded and named, never silently dropped). Proven at 5,000 files.
  • A canvas page type. A first-class page rendered in its own viewer and full-screen at #/canvas/<id>, under an origin-scoped embed policy — the one surface allowed to embed live content, so its external origins are allowlisted and a blocked canvas names the refused origins rather than half-rendering. See Content Types.
  • Brain depth. Embedding widths are pinned per content source with loud, actionable errors instead of silent search degradation; re-indexing can be scoped to one source; glassy_search can federate across extra endpoints you register by config; and ranking now respects your note graph, so well-linked notes rank up.
  • Memory builds itself wherever content lands. Cloud Sync writes fire the corpus indexer, which maintains full-text search and graph edges — so one agent on two machines gets one brain, not two divergent ones.

Migrations 0114–0122 apply automatically on first start after upgrading. The new agent surfaces are appliance-only; the hosted service deliberately withholds them. Check curl -s <your-instance>/api/capabilities.

Includes everything from v2.39.0, which was prepared but deliberately not launched as its own release.

September 22, 2026 — Companion v2.19.0: the companion is a principal

Companion v2.19.0 — pairs with glassy-dash v2.40.0 (the agent-identity release). The extension speaks the /api/capabilities manifest with a fail-closed client gate, can mint a named agent key (pinned identity "Companion") on self-host named-keys appliances, shows an unread agent-notification badge (self-host only), and surfaces degraded-lane diagnostics in the KB tab. GitHub release v2.19.0.

September 22, 2026 — v2.38.1: An instance describes what it can actually do

A completion patch for v2.38.0's capability manifest. /api/capabilities now reports six per-surface renderers — note, window, keep, writing, ai-assistant-preview, help-article — each with its allowlist, the elements it drops at render, and how it drops them (silent-drop versus prompt-only). The point is the distinction an agent cannot guess: whether a rejected element fails loudly at write time or simply vanishes when the page renders. It also names the two near-identical document routes whose shapes differ (POST /api/documents creates; POST /api/ext/documents captures a URL-shaped page and is not a create).

Every value is read from the module that enforces it, and drift guards parse the browser source so a documented mirror cannot quietly stop matching what the app renders.

September 22, 2026 — v2.38.0: Agents can read and write documents, and ask you questions

  • Documents are reachable. Long-form content — journals, plans, drafts — was invisible to the agent loop while every note was readable and writable. Four new tools: glassy_list_documents, glassy_read_document, glassy_create_document, glassy_update_document. Writes go through the same service the UI uses, so authorship, the canonical tag policy and the storage cap behave as they do for notes. Tool surface 29 → 33.
  • The ask-and-answer loop (glassy_request_review / glassy_get_review / glassy_withdraw_review) with a click-to-answer owner view. Answers are enforced against the choices actually offered, so an agent always receives a value it can act on.
  • needs_review on notes — an agent that stops before finishing raises a flag you see and clear; it rides every note response and the sync wire.
  • Per-note deep links — #/notes/<id> opens that note. A missing or foreign id says so and lands on the list, never a blank screen.

September 22, 2026 — v2.37.0: Trust on both sides of the aisle

v2.36 made the write path honest; this release does the same for reading and for memory.

  • Search works again. Every GET /api/search query had been returning 500 since beta.40 — eight SQL ESCAPE clauses collapsed to an empty escape character that SQLite rejects at prepare time. The test suite never caught it because those route tests mock the database and never execute SQL; a real-SQL test now does.
  • A save that omits the title no longer erases it. PUT /api/notes/:id defaulted title to an empty string when the body left the key out — the one column without keep-existing semantics — and answered 200.
  • Delete is honestly idempotent. Re-deleting an already-trashed note is a quiet success rather than an error, so a documented retry loop no longer treats a no-op as a failure. A genuinely unknown id is still an error.
  • Trashed content is no longer findable. A document in the trash could be indexed into both the semantic and keyword indexes permanently.
  • One memory system. Documents ride the same corpus lane as notes and bookmarks (embeddings, full-text search and the graph), and the legacy readers migrated with them.
  • The graph sees every content type. A note's [[wikilink]] to another note or document now becomes an edge, alongside the vault-path edges that were previously the only kind. The seven graph tools accept node keys (note:<id>, document:<id>, bookmark:<id>) as well as bare vault paths; existing callers are unchanged.
  • Every write response says what render will strip. A warnings field is always present — empty when clean, naming the tag when the sanitizer will remove it — so "stored successfully but will not display" is no longer a surprise.

September 21, 2026 — v2.36.0-beta.42: A correct authenticator code is no longer rejected

If 2FA has ever told you a code was invalid when you were certain you typed it correctly, and it worked a few seconds later — that was a real bug, and it is now fixed.

  • Codes are accepted one 30-second step either side of the current one. Glassy previously checked the exact current step only, so whenever the boundary fell between you reading six digits off your phone and the server checking them, login and every 2FA verification returned "Invalid verification code" for a code that was correct. There was nothing you could do differently except wait and try again. In practice a code now works for about 90 seconds instead of 30.
  • This is the standard behaviour, not a loosening. ±1 step is what RFC 6238 recommends and what every Google Authenticator-compatible service accepts, so it is already what your authenticator app assumes. A code from two steps ago is still rejected — the window is bounded, and the bound is pinned by a test so it cannot be widened by accident.
  • The trade-off, stated plainly. A code stays valid longer, so there is no replay protection inside that ~90-second span. That is the standard TOTP trade-off; the mitigation is throttling the verification endpoints, not narrowing the window back to something that rejects honest users.
  • See Two-Factor Authentication for the full sign-in behaviour and a troubleshooting order that starts with the things that are actually likely.

September 20, 2026 — v2.36.0-beta.41: One write path for notes

An integrity release. Nothing looks different, and five things stopped being wrong — several of them in ways you would have felt without being able to name.

  • Newly created notes now appear on your other devices. A new note was written with no "last updated" timestamp at all, which made it invisible to incremental sync and sorted it last in every recently-updated list. Two further write paths stored the timestamp in a second, space-separated format that sorts as older than the ISO format used everywhere else — so those notes looked stale on the day they were written. All three were found by querying the data rather than reading the code, and all three now write one consistent format.
  • Tags keep their words. The AI auto-tag lane was stripping every non-alphanumeric character: Résumé came back as rsum, release notes as releasenotes. It now uses the same canonical policy as everything else — lowercased and trimmed, with accents, spaces and punctuation preserved. Bookmark tags written through MCP adopted the same policy and the same 64-character limit their schema always advertised (the code enforced 50). See Content Types.
  • Deleting a shared note does what you meant. If you are a collaborator rather than the owner, Delete now removes the note from your workspace and leaves the owner's copy untouched. It used to return an error telling you to use a different screen. Restoring from the bin is owner-only and now says so honestly instead of returning a success message that changed nothing.
  • Agent edits are attributed. A note changed through the MCP server records MCP agent as its last editor, so an AI-made change is no longer indistinguishable from your own.
  • Storage is checked before a write, not after. Capture, the browser extension's note and document saves, and the notes API now refuse up front when you are out of headroom, rather than accepting the write and putting you over quota silently. Bookmarks are deliberately exempt — they cost no storage.
  • A privilege escalation in the legacy import path is closed. POST /api/notes/import accepted an "announce to everyone" flag from any caller, so a non-admin could publish a note to every user on the instance. It is now administrator-only, matching the normal create path.
  • A success counter that counted nothing. Auto-tag reported updated: 1 per attempt, so tagging a note that did not exist — or one in another account — still reported success. It now counts rows actually changed.
  • Two build guards keep it this way. One fails the build if a raw note write reappears anywhere; the other fails if authorship stamping moves or is applied with the wrong account. Both caught miscounts by their own author on first run, which is rather the point.

September 17, 2026 — v2.36.0-beta.40: Cloud Sync media transfer + custom-backgrounds sync

The sync lane now moves files, not just rows — and the appliance tells the truth about anything it can't:

  • Media transfer (beta.40): note images and custom-background renditions now sync to your appliance alongside their metadata (voice audio already did). The appliance diffs a manifest against its own disk and fetches only what's missing, retrying every cycle; older origins are handled gracefully.
  • Custom backgrounds are a synced type: rows sync through the outbox like everything else; the three image renditions arrive via the media step above.
  • A visible media-health signal: Settings → Cloud Sync now warns (amber) when synced rows reference files this instance doesn't have, split by voice/images — instead of green health over broken placeholders. If a count doesn't drain, the origin genuinely no longer has those bytes, and the panel says so.
  • Agent Gateway on your LAN: self-hosted appliances now accept LAN and Tailscale agent base URLs (http://192.168.x.x:8642, http://box.tailnet.ts.net:8642) — previously only localhost was allowed. Cloud instances keep the strict allowlist.
  • BYOK polish: the Validate button shows the real error instead of "invalid: undefined"; the configure button opens a prefilled edit dialog (it was dead UI); and the Ollama model dropdown lists the models actually installed on your instance (live /api/tags, 30s cache) instead of a stale hardcoded list.
  • MCP key banner fixed: the Agent Connections banner read a field the API never returned, so it always warned "No MCP key found" even when one existed.
  • Missing uploads answer 404: a request for an upload that doesn't exist returns JSON 404 instead of the app shell (which silently broke every existence check).

September 17, 2026 — v2.36.0-beta.37: Embeddings lane integrity

Local (Ollama) embeddings got honest about their limits:

  • Long notes embed instead of failing: the single-note path now chunks like the corpus path (tokenizer-verified chunks, mean-pooled into one vector) — notes over ~5,300 characters no longer die with an opaque 502.
  • Chunking counts real tokens (shipped tiktoken encoder) against the model's actual context length (probed from Ollama) instead of a 4-characters-per-token guess.
  • fp16-aware device probing makes the WASM/q8 fallback reachable, so local embeddings work on machines without WebGPU fp16 support.
  • Corruption can't enter the index silently: a write-time width guard rejects mismatched embedding dimensions with a loud 409, and search responses carry in-band lane diagnostics that distinguish "no results" from "the lane is broken".

September 16, 2026 — v2.36.0-beta.36: Public Window owner-view + sync health honesty

  • Owners can view their own Public Window while signed in on a private instance (previously the privacy gate blocked the owner too); anonymous visitors still get 401.
  • Sync health is owner-scoped: on multi-user instances your pending-changes count and peer list show your queues only — a token holder no longer sees other members' hundreds of pending rows as their own, and peer emails no longer leak cross-tenant. Dead-lettered changes became recoverable (Retry re-arms them).

September 16, 2026 — v2.36.0-beta.35: Inactivity sweep kill-switch

The DISABLE_INACTIVITY_SWEEP kill-switch is now actually enforced (node-cron v4 compatibility) — appliances that set it no longer run the sweeper at all.

September 15, 2026 — v2.36.0-beta.34: MCP schema-drift fix (#58) + docs accuracy (#59, #60)

The three notes MCP tools work on a stock appliance again, and every MCP-facing doc now matches the live surface:

  • #58 (P1): glassy_read_note, glassy_list_notes, and glassy_get_recent queried a notes.created_at column that has never existed (the notes table's creation column is timestamp) — all three failed with SQLITE_ERROR: no such column: created_at on every fresh appliance, inside 200 tool results. Fixed on the real column, including the list_notes pagination cursor; the glassy://notes/{id} and glassy://recent/{sourceType} resources (which delegate to the tools) are fixed with them. The root cause — MCP test fixtures declaring a column production never had — is fixed, and the schema-contract scanner now scans the .mjs tools layer so this drift class is gate-caught.
  • #59: the Obsidian direct path is no longer documented as out-of-the-box — the Local REST API plugin binds 127.0.0.1 by default, which no container can reach. The docs now teach the three real requirements (plugin bound to 0.0.0.0, a host firewall rule for the container network, server config via PATCH /api/users/profile) and no longer contradict themselves about the hidden Settings → Obsidian panel.
  • #60 + docs-site accuracy sweep: the agent brief's resource count is now honest (3 listed + 6 URI templates), the MCP key location reads Settings → Connections & data, and this site's own enums are corrected — glassy_search.sources (bookmarks · notes · documents · vault · voice), glassy_get_recent.source_type (bookmarks, notes, all), and glassy_note_create.type (text, checklist, draw, youtube, music, rss, spotify, vimeo — the stale rich entry from before the #34 canonical-type wave is gone), plus the missing glassy_daily_briefing prompt row.

September 14, 2026 — v2.36.0-beta.33: Production follow-ups (issues #43–#57)

Full investigation of the fifteen issues filed against the beta.32 appliance and the beta.31 social-graph work, plus a sweep of every touched surface for similar conditions. Fourteen closed in code; companion-side items are documented on #53:

  • Relayed upstream 401s no longer destroy healthy sessions (#56, root cause of "the app keeps logging me out"). One wrong Obsidian plugin key (or a bridge race falling back to an unreachable direct path) used to log you out of the whole app: auth-shaped upstream failures now map to 502 errors, the auth middleware tags its own 401s with AUTH_* codes, and the client force-logs-out only on those — wrong PIN/password/MFA 401s surface as plain errors.
  • In-flight vault reads survive the bridge's ~5s SSE reconnect churn (#53). Idempotent (GET/HEAD) requests park for an 8s grace window and are adopted + re-sent verbatim by the fresh connection; non-idempotent writes keep fail-fast. The stale-JWT ticket loop after a JWT_SECRET rotation is shed server-side (5 consecutive 401s from one source answer 429 + Retry-After).
  • The editor no longer dies mid-edit on forced logout (#57), and the Vault sidebar icon persists when Obsidian is enabled (#52) — now gated on the user's Obsidian settings, with an amber dot hinting disconnection.
  • One-call reindex (#43) — POST /api/kb/backfill/reindex {confirm:true} clears the vector/ledger tables together and starts the backfill immediately; surfaced as a two-click Rebuild index button in Settings → Second Brain.
  • Opt-in Web Push on self-host (#45). The "push depends on FCM/APNs" premise was wrong for browser PWA push; setting VAPID_PUBLIC_KEY + VAPID_PRIVATE_KEY now mounts /api/push/* and reveals the settings panel (feature-detected). The appliance stays push-off by default.
  • One tag policy on every write surface (#48) — capture and extension endpoints adopt the notes 64/20 contract with 422 INVALID_TAGS offender lists; BYOK maxRetries is wired (#44); the MCP per-catch audit is complete (#49); the People discovery strip respects the collaboration_discoverable opt-out; the cookie-consent toast no longer appears on self-hosted instances (#55); the Cloudflare purge covers the app origin's PWA shell (#51); the dead ENABLE_KB_UI server knob is deleted (#46); DB_FILE legacy aliases are deprecating (#47); the agent onboarding brief lives in the main repo and is linked (#54).
  • The syncService suite is hermetic (#50) — closing the deploy-gate flake family behind this release.

September 14, 2026 — v2.36.0-beta.32: Self-host audit remediation (issues #23–#36)

Independent re-verification of every open self-host issue against beta.31 — most reported bugs were already fixed; this release closed what genuinely remained and hardened the process that let a correct fix be silently deleted:

  • Note tags are validated, not silently stored-or-dropped (#23 §2.3). The notes API normalises tags (trim, lowercase, de-duplicate) and rejects invalid ones with 422 INVALID_TAGS naming every offender, its reason, and the limits — REST and AI writes now follow one rule, and the success response echoes what was persisted.
  • Embeddings no longer reuse the chat model (#35). An explicit OLLAMA_EMBEDDING_MODEL, else a single cached /api/tags probe auto-selects an embedding-capable model already installed — deliberately never falling back to chat. A missing model yields an actionable error naming a pullable tag.
  • Embedding dimension drift is observable instead of silent (unreported sweep finding) — counted, aggregated into a single warning naming the remedy, and reported by /api/monitoring/ready → embeddingHealth.
  • Obsidian periodic notes always send a date (#31). The Today strip no longer fails on the plugin's dateless weekly/monthly defaults (the plugin's generic "Invalid token" read like an auth failure), and the query key no longer serves yesterday's periodic note from cache indefinitely.
  • The app shell is served network-first (#36). Workbox no longer precaches index.html — a browser holding the pre-upgrade shell previously kept enforcing the previous deploy's CSP until the tab was reopened.
  • Installer README corrected to six required fields including GLASSY_TAG (now asserted in CI for both READMEs); MAINTAINING.md compose/version-pin corrections; push notifications documented as intentionally unavailable on the appliance at this release (/api/push/* unmounted — see beta.33 for the opt-in change).

September 13, 2026 — v2.36.0-beta.31: Following — a real social graph

  • Following is now a real social graph — user_follows relation (migration 0106), follow/unfollow by slug, per-window follower counts (public number), a new-Follower notification to the person followed, and a "Glassy people" discovery strip (GET /api/feeds/discover/people, opt-in: slug claimed + ≥1 public note).
  • The Following surface leads with a People tab (avatars + window links) and keeps external RSS in a Publications tab; the aggregated stream unions followed people with external RSS, resolved origin-free.
  • Fixed: following the same person from two glassy origins no longer creates duplicate follows or a stale "Follow" button (follow state derives from the graph, not an origin-embedded feed URL), and following a person no longer consumes the free-tier RSS publication quota.
  • Review hardening: follower-count routes are guarded against private instances (a spoofed crawler UA is not a credential — anonymous callers get 401 PRIVATE_INSTANCE); the new-Follower notification is actually delivered client-side; migration 0106's backfill runs in its own transaction so a mid-loop SQLITE_BUSY cannot leave user_feeds half-migrated; the follower identity list endpoint was removed rather than left dead (counts stay public; the identity list is deliberately not exposed).

September 12, 2026 — v2.36.0-beta.30: Instance integrity & appliance hardening

Security + integrity pass over the three deployment identities (hosted SaaS, Clear, self-hosted appliance):

  • Guarded /w/:slug social-meta routes against private instances. The OG routes served note titles, 200 characters of note bodies, bios and avatar URLs to any request with a crawler User-Agent — no JWT, no private-mode check — so members-only window content was readable by anyone spoofing Googlebot. The guard now runs before the User-Agent branch.
  • The self-hosted appliance always boots. Membership verification is informational: a failed check starts the appliance in degraded mode (reported via GET /api/instance → membershipState) and nothing else changes; the live verification call is skipped while inside the persisted backoff window. Previously a first boot with no internet refused to start at all.
  • Fresh-install 500s fixed: the GDPR export queried a tags table no migration has ever created, and the Stripe checkout pending-setup INSERT wrote a users.updated_at column no fresh install has ever had — both found after fixing the schema-contract scanner's apostrophe mis-read (it had been silently skipping most SQL statements). stripe_processed_events moved into migration 0105.
  • Client tier policy mirrors the server self-host bypass — DB tier drift no longer blanks the Public Window guide or hides the whole settings editor while the server still allows publishing; sub-account caps hold the 5-workspace ceiling on the appliance through the tier-policy SSOT.
  • Privacy & consistency: the collaborator search masks email addresses (a***@domain hints, 2-character minimum enforced server-side); the presence cookie no longer emits a foreign Domain=.glassy.fyi on self-hosted origins; public-window share links, og:url and RSS autodiscovery resolve from the running origin instead of hardcoded https://glassy.fyi; admin user-management routes return 404 on the appliance and the admin panel no longer fires success toasts for changes the server silently discards.

September 11, 2026 — v2.36.0-beta.29: Note-type integrity (issue #34) + beta.28 self-host wave

Root-cause fix for 0Reliance/glassy#34 plus the pattern-class sweep it triggered, shipped together with the beta.28 self-host experience wave (previously tagged but never fleet-deployed):

  • One canonical note-type universe — shared/noteTypes.json is now the single source of truth consumed by the server (notes API, Obsidian import, MCP tools, sync adapters, public window) and the client (note Modal, Composer, display helpers). Six drifted inline definitions are gone.
  • The note Modal is fail-safe — unknown note types render the text editor, never the drawing workspace, and saving preserves their content verbatim (previously a save silently replaced the content of unknown-type and vimeo notes with empty drawing data). 0Reliance/glassy#34
  • Obsidian import hardening — frontmatter type values normalize to the canonical allowlist (custom vault types like project-doc become text), frontmatter tags are no longer silently dropped, and checklist items get the app's canonical {id, text, done} shape.
  • MCP — glassy_note_create no longer advertises the non-canonical rich type; sync adapters normalize drifted peer types at the import boundary; the notes API no longer accepts render-unsupported legacy types.
  • Public window — malformed JSON columns no longer leak internal DB fields into public payloads, and note types are normalized at the public boundary.
  • Migration 0104 — existing rows repaired (drawing → draw, unknown → text, content untouched) and the notes table rebuilt with a CHECK(type IN …) constraint so invalid types can never persist. Verified loss-free at boot on all three production databases.
  • beta.28 wave (details in the app CHANGELOG): instance-identity self-heal + fail-closed build flag, local-AI CDN allowlist, Ollama BYOK correctness, MCP-row settings crash fix, create-doc fix, Cloud Sync "off" state, deployment-aware SSRF, bridge SSE heartbeat (first ping immediate, 20s → 5s), Docker build-flag self-verification — pairs with Companion v2.18.1 below.

September 10, 2026 — Companion v2.18.1: bridge reconnect hardening

Companion v2.18.1 pairs with the glassy-dash server-side heartbeat fix (first ping sent immediately, heartbeat interval 20s → 5s) that ships in v2.36.0-beta.28, closing the ~5-second bridge-SSE subscribe-churn cycle on self-host (0Reliance/glassy#25-F2): the offscreen-document EventSource could drop before the server's first heartbeat arrived, and the extension's onerror handler reconnected only after a 5-second delay. The reconnect delay drops 5000ms → 1000ms (initial value and the onopen reset) so the bridge recovers within a second when a drop still happens. Companions ≤ v2.18.0 keep the 5-second delay. GitHub release v2.18.1.

September 8, 2026 — v2.36.0-beta.25: Public window typography parity

Public pages now render exactly like the in-app editor (release train includes beta.24, below):

  • Full formatting on public pages — note permalinks, the article reader, and every card layout share the .note-content typography layer (paragraph spacing, headings, lists, code, tables, callouts) that the editor uses; inert prose classes are gone app-wide, including the Obsidian vault previews and the AI Writing Assistant preview.
  • Editor-faithful media and links — public sanitisation keeps editor utility classes (rounded images, spaced embeds, link colors), strips tracking pixels, and renders task checkboxes read-only.
  • Faster windows — list payloads cap each note/article's HTML at 4,000 characters (permalinks still serve full content), RSS descriptions ship clean plain text, and reading times are computed server-side.
  • Fixes — Vimeo notes no longer render as raw text on permalinks; the article reader is a proper modal dialog (Escape, focus trap, focus restore).

September 8, 2026 — v2.36.0-beta.24: Calendar quality audit

Defect sweep over the beta.22 calendar work: multi-member CalDAV sync no longer destroys sibling events, UPDATE write-backs actually reach CalDAV, the hourly sweep pushes local changes (migration 0100), temporal extraction resolves in the viewer's timezone, suggestion chips confirm/dismiss individually, and an async-DB contract sweep fixed un-awaited database calls across the server.

September 7, 2026 — v2.36.0-beta.23: Two-way sync, real push, capture wiring

The Temporal Spine completes its foundation (follow-ups round; fleet rollout pending):

  • CalDAV two-way sync — connect a calendar as a full CalDAV collection: incremental sync-token REPORTs, remote events adopted, and locally-created events written back (create/edit/delete propagate as <event-id>.ics). Locally-owned events win on conflict, our own pushes are never re-adopted, credentials are encrypted at rest, and Pro gates it like ICS. The Today calendar's "+ Add feed" now offers an ICS/CalDAV toggle.
  • Real push_reminder delivery — rule reminders fire actual Web Push notifications (plus the in-app live update), with expired-endpoint pruning. Self-host: drop a VAPID key pair into the compose env. iOS needs the installed PWA (≥ 16.4).
  • Temporal capture auto-wiring — note saves and voice-to-note conversions now propose calendar events via the confirm chips (best-effort, deduped per note, never blocks the save).
  • Playwright runner fixed — E2E_BASE_URL override + a top-level per-test timeout (the old value sat where Playwright ignores it) + unauthenticated-context registration; formal e2e verified 2/2 on the deploy VM.

September 2, 2026 — v2.36.0-beta.22: Temporal Spine calendar + AI time management

The calendar becomes the workspace's temporal spine (branch feat/temporal-spine-calendar, released 2026-09-07 to all instances):

  • Calendar foundation — local calendars + events (migrations 0096/0097), a Today cockpit module replacing the old read-only strip (Now/Next hero, week/month/agenda, layer toggles, drag-a-note-to-day scheduling), and Pro-gated ICS feed subscriptions with hourly ETag-conditional sync and recurring-event expansion. See the new Calendar page.
  • MCP time tools (15 → 20) — glassy_get_schedule, confirm-gated glassy_create_event, glassy_update_event, glassy_delete_event, Pro glassy_find_free_slots, plus the glassy_daily_briefing prompt and glassy://calendar/today resource.
  • Event-triggered Agent Gateway dispatch — events can carry an agent task; a minute-tick cron fires it at event start with at-most-once delivery via a pre-write ledger, behind ENABLE_CALENDAR_AUTOMATION (default off; pre-set on the self-hosted appliance).
  • Rule time triggers — on_due/on_overdue with create_event, dispatch_agent, and push_reminder actions.
  • AI temporal capture — proposed events from date/time heuristics with one-click confirmation chips (propose-only, never a silent write).
  • Briefing calendar context — the morning briefing now includes meetings, conflicts, and the first free block.
  • Extension API — /api/ext/calendar/today and /api/ext/calendar/events for the Companion surface.

See the glassy-dash CHANGELOG for full details.

August 31, 2026 — v2.36.0-beta.19: multi-account sessions, stable background uploads, publisher API fixes

Three-stream release deployed to all instances (14090502):

  • Seamless multi-account sessions (family-computer) — a "Continue as…" remembered-account chooser on the login screen (device-local, max 6, most-recent-first), full user-session purge on login/logout (no more "clear the cache to switch accounts"), and per-user query-cache namespacing with identity verification so one user can never rehydrate another user's data.
  • Premium & stable custom background uploads — real metadata (names, timestamps, dimensions) instead of filesystem-only listings; atomic processing so a partially-processed image can never appear selectable-but-broken; phone photos now upload right-side-up (EXIF orientation); re-uploading an identical image is free (content-hash dedup instead of charging storage quota again).
  • Publishing API fixes (from agent-publisher feedback, issue #23) — POST /api/notes now honors is_public at creation time (previously silently dropped, requiring a follow-up PATCH); free-tier attempts get a clear 403 instead of silence. POST /api/upload accepts image or file as the multipart field name, not just images. Public-window inline image previews load reliably for fresh visitors.

Companion v2.18.0 is unchanged — no extension update is required for this release. GitHub release v2.36.0-beta.19.

August 27, 2026 — Companion v2.18.0: bridge transport v2 (vault writes over the extension bridge)

Companion v2.18.0 closes the Obsidian glass-pane follow-up with glassy-dash. The SSE bridge now carries raw markdown bodies and request headers (If-Match, Content-Type, Accept) and relays upstream response headers (ETag) back to the server, so vault WRITES (tap-to-toggle checkboxes, add-under-heading, daily-note append, push-to-vault) work when the extension is the only path to Obsidian on containerized self-host. Also fixes raw-body corruption in the extension's direct capture-to-vault push. Servers gate transport v2 on the extension's advertised version; companions <= 2.17.1 keep prior behavior. Extension suite 189/189 (5 new obsidianFetch tests). GitHub release v2.18.0.

August 11, 2026 — Companion v2.17.1: save & sync reliability (customer release-gate hardening) + dash server companions

Companion v2.17.1 — product of a full release-gate code review of the extension save page, save/sync, and connectivity paths (extension + server). GitHub release v2.17.1 with .zip + .xpi (SHA-256 download-verified). Extension suite 184/184 (14 new regression tests); dash server suite 1018/1018.

  • Capture-rule pre-population fixed — GET /api/capture-rules responds with a { rules: [...] } envelope that the popup never unwrapped (Array.isArray() on the wrapper object is never true), so Smart Save auto-fill (preset, collection, tags, public candidate) had been silently inert since v2.2.0. New tested toRulesArray() normalizer.
  • 5xx-resilient sessions — verifyToken() previously cleared the stored session on ANY non-2xx from /api/ext/me; a backend deploy blip logged every user out. Auth now clears only on a real 401.
  • Offline-queue flush semantics — policy drops now report ok:true so the service worker actually removes them (previously retried 5× then lost); auth failures pause the flush and keep all items queued until re-login (previously dropped after 5 attempts).
  • Popup saves queue offline (parity with background saves) with a new amber Queued for Sync toast; Settings server-URL validation shows inline errors; host_permissions gained https://*.glassy.fyi/*; auth/login/ping timeouts; byte-accurate response cap; single-write quota trim; save-all-tabs stops at the 60 req/min limit.
  • Dash server companions (commits 6b8d4973 + f891a84c): Cross-Origin-Resource-Policy: cross-origin for /uploads/captures/* so the extension can preview saved capture screenshots cross-origin (other /uploads/* paths keep same-origin); 4xx API errors now return their real message in production (the CORS 403 body previously claimed "Internal server error"); companion version pins bumped app-wide (ExtensionView, helpContent, docs) with the cross-repo version-sync gate green.

August 7, 2026 — glassy-dash v2.36.0-beta.13: brand-asset CORP override + Companion v2.17.0

glassy-dash v2.36.0-beta.13 — brand-asset CORP override + AI guardrails + version sync hardening: see CHANGELOG.

  • brandAssetCorp middleware — overrides Helmet's default same-origin CORP to cross-origin on brand-asset paths only (/favicon*, /og-*.{png,jpg,jpeg,webp}, /pwa-*, /apple-touch-icon*, /logo*, /wordmark*, /icon-*). This allows the Glassy Capture browser extension (running in a chrome-extension:// origin, which is never same-origin with any web host) to load og:image and favicon previews in the SaveCard visual header. User-content uploads (/uploads/*), API routes, and hashed JS/CSS bundles keep Helmet's strict same-origin default — the privacy boundary on user data is unchanged.
  • Companion v2.17.0 — save-card image preview reliability — og:image now uses object-fit: cover (was a tiny thumbnail on non-16:9 images). Container background is the brand gradient (was solid black). Favicon <img> is conditional on non-empty URL with a ▣ placeholder. CSP img-src widened to mirror connect-src (http://localhost:, http://127.0.0.1:, http://*) so dev/self-host capture image previews load.
  • CSP quote regression fix — v2.17.0 initially shipped with a malformed CSP (style-src 'self' 'unsafe-inline — missing closing quote on unsafe-inline) that prevented the extension from loading. Fixed in-place at commit 9666c57; tag v2.17.0 force-moved.
  • AI guardrails hardening (commit c7a8f65) — all 4 AI providers (Gemini, Ollama, OpenAI, Anthropic) now inject GLASSY_GUARDRAILS between the knowledge block and tool defs, isolating bookmark content as data and preventing indirect prompt injection. GENERATE_IMAGE tool sanitizer gap closed. CREATE_NOTE server-side validation added (title 200, content 20000, tags 20×64, reject unknown keys).
  • Keep FK validation (commit 4323ff4) — collection_id FK validation + body type coercion prevents 500s on malformed Keep API requests.
  • Committed: glassy-dash 0364d39, companion 9666c57.

August 4, 2026 — glassy-dash v2.36.0-beta.12: Cloud Sync (Phase 0 + Phase 1)

glassy-dash v2.36.0-beta.12 — Cloud Sync shipped: see CHANGELOG and Cloud Sync docs.

  • Cross-instance two-way sync — sync notes, documents, bookmarks, voice, conversations, folders, highlights, collections, pinned tags, and note versions between Glassy instances (e.g., self-hosted appliance ↔ cloud Pro account). 11 synced types; embeddings/FTS/presence/AI-usage excluded.
  • Phase 0 (manual export/import) — GET /api/sync-data/export and POST /api/sync-data/import with JWT auth. Stable-id types sync by id; INTEGER-PK types use natural-key upsert or dedup. Identity remap via pairing token rewrites user_id + account_id.
  • Phase 1 (automated delta channel) — S2S delta sync via GLASSY_SYNC_TOKEN auth. sync_outbox table + triggers, per-type enable/disable + direction + conflict policy controls, automated sync scheduler with configurable interval, sync-health dashboard + sync-now button in Settings → Cloud Sync. Voice audio binary transfer via S2S fetch. 3-way merge preservation snapshots before LWW overwrite.
  • Settings UI — Settings → Cloud Sync card with per-content-type toggles, conflict policy, health dashboard, and sync-now button.
  • Committed: glassy-dash dd536c5c (Phase 0 PR #14) + feat/cloud-sync-phase0 branch (Phase 1, 17 commits).

July 29, 2026 — MCP layer completion + full stack enablement

MCP server now exposes 15 tools, 3 prompts, 6 resources — the original 8-tool vision is complete. See glassy-dash CHANGELOG for full details.

  • 5 new MCP tools — glassy_list_notes (paginated, filterable), glassy_read_note (full note by ID), glassy_get_tags (unified tag cloud), glassy_get_folders (collections + folders), glassy_capture_voice (voice memo with transcribed text, corpus-indexed).
  • MCP spec compliance — human-readable title on all 15 tools, destructiveHint on delete tools, openWorldHint on all tools, 3 new resources (glassy://notes/{id}, glassy://tags, glassy://folders).
  • SSE streaming fix — Obsidian proxy now handles text/event-stream responses per the MCP Streamable HTTP spec.
  • Companion MCP token exchange — new McpConnectionSection.jsx in the extension Settings panel. Fetches MCP key + copy-paste config snippet for Claude Desktop, Cursor, Windsurf. Checks server MCP status before showing UI.
  • Full MCP stack enablement — self-host compose now sets all 6 runtime flags to true (was 2 of 6). Dockerfile + GHCR workflow bake in all 5 build-time UI flags. Without the full stack, users could see MCP settings but couldn't generate keys or get search results.
  • 51 MCP tests pass (was 32), 170 companion tests pass.
  • Committed: glassy-dash c8b501a + 6e01d8b, companion fba8657, glassy-docs 5dc1742.

July 29, 2026 — glassy-dash v2.35.0-beta.11: Obsidian Bridge race condition fix

glassy-dash v2.35.0-beta.11 — Bridge registry race condition fix: see CHANGELOG.

  • Root cause of "cycling every ~60s" found and fixed — obsidianBridgeRegistry.js had a race condition. When the extension reconnected, the server closed the old SSE and registered the new one, but the old SSE's asynchronous req.on('close') handler fired after the new connection was registered and blindly deleted it via unregisterConnection(userId). The registry then showed disconnected even though the new SSE was alive, so the bridge reconnected ~60s later, repeating the cycle.
  • Fix — registerConnection() now returns a unique connectionId (UUID); unregisterConnection(userId, connectionId) only removes the connection if the IDs match. The SSE route captures the connectionId and passes it to its close/error handlers. 4 new tests cover the race, the return value, the legacy path, and the no-op case.
  • Companion v2.15.0 heartbeat fix — the SW-side heartbeat monitor moved from setInterval (dies on service-worker eviction) to chrome.alarms (survives eviction and wakes the SW on fire). The offscreen doc sends a heartbeat every 15s; the 30s BRIDGE_RECONNECT_ALARM calls checkHeartbeat() and recreates the offscreen doc after 2+ missed heartbeats. Alarm period reduced 2min → 30s for faster recovery.
  • What it is NOT — not a Chrome MV3 offscreen-eviction issue. The WORKERS offscreen reason has no built-in lifetime restriction. The original diagnosis blamed the client; the real bug was a server-side async close-handler race.

July 27, 2026 — Companion v2.15.0: Obsidian Vault Companion + glassy-dash v2.35.0-beta.9

Glassy Capture extension v2.15.0 — Obsidian Vault Companion: see release notes at https://github.com/0Reliance/glassy-companion/releases/tag/v2.15.0.

  • New Vault tab — browse your Obsidian vault from the extension popup. File tree with breadcrumb navigation, rendered HTML preview with backlinks/tags metadata, "Open in Obsidian" button.
  • Quick Note + Daily Note — "Notes" sub-tab inside the Vault tab. Append to today's daily note (Shift+Enter), or create new notes pushed to the vault as .md files.
  • Capture Controls — auto-push toggle + configurable clips folder in Settings → Obsidian Bridge. "Related in your vault" panel on save (debounced vault search).
  • Tag Bridge — vault tags merged into autocomplete with source badges. Nested Obsidian tags (type/reference) now preserved (was silently destroyed). Server sanitizeTags aligned.
  • Settings flyout — permanent "Browser Extension" link added → #/extension page.

glassy-dash v2.35.0-beta.9 — Obsidian Bridge SSE Auth Stabilization: see CHANGELOG.

  • SSE ticket double-consumption fix — /api/ext and /api/ext/obsidian-bridge routers ran auth twice, consuming the one-time SSE ticket on the first run. Reordered mounts + next('router') guard.
  • Plugin version parsing fix — parsePluginVersion now reads versions.self (plugin) not versions.obsidian (app).
  • Tag sanitization fix — sanitizeTags now allows / for nested Obsidian tags.

glassy-dash v2.35.0-beta.10: see CHANGELOG.

  • Companion bumped v2.14.0 → v2.15.0 — ExtensionView.jsx + helpContent.js updated to point at the v2.15.0 release assets (the Vault Companion).
  • Tag sanitization allows nested Obsidian tags — sanitizeTags in extensionRoutes.js and captureRoutes.js now allows / in tag names ([^a-z0-9-/_] instead of [^a-z0-9-_]). Aligns with the companion's normalizeTag fix — nested tags like type/reference were silently corrupted to typereference on persist.
  • Settings flyout — permanent "Browser Extension" link added → #/extension page (was only reachable via Store / landing page / direct URL).

July 24, 2026 — Companion v2.14.0: Obsidian Bridge deep-fix + glassy-dash v2.35.0-beta.8

Glassy Capture extension v2.14.0 — Obsidian Bridge deep-fix: see release notes at https://github.com/0Reliance/glassy-companion/releases/tag/v2.14.0.

  • localhost host_permissions broadened — the old manifest only declared Obsidian ports (27123/27124); the offscreen doc SSE to a localhost self-host Glassy server was silently blocked by Chrome. Now declares http(s)://127.0.0.1/* and http(s)://localhost/* so any localhost Glassy server works.
  • Offscreen doc existence verified via chrome.runtime.getContexts() — stale cached flag caused silent bridge death after Chrome tore down the offscreen doc under memory pressure.
  • onSuspend no longer false-flips status — the offscreen doc owns the SSE and isn't evicted on SW suspend; flipping status caused false-negative "Disconnected" in the popup.
  • peekToken() prevents silent auth loss — getToken() called clearAuth() on JWT expiry, silently nuking the token; the offscreen doc can't re-authenticate, so the bridge died with no feedback.
  • Permission denial feedback — popup shows a warning banner when localhost host permissions are denied.
  • SSE auth migrated to one-time ticket — POST /api/ext/obsidian-bridge/ticket issues tickets; JWT no longer leaks into server logs.
  • Server-side bridge-first routing on self-host — 20 obsidian.js route guards + aiContext.js + MCP proxy all bailed before checking the bridge, making it dead code when obsidian_token is NULL (self-host). Now uses loadObsidianSettings() helper.
  • Cross-repo version sync gate — scripts/verify-companion-version-sync.js added to dev.sh deploy pre-flight to prevent the "app links to old extension" release integrity failure.
  • Gem brand assets unified across all surfaces (favicons, PWA icons, component SVGs).

July 22, 2026 — Companion v2.13.0: Obsidian Bridge MV3 reliability

Glassy Capture extension v2.13.0 — Obsidian Bridge MV3 reliability fix: see release notes at https://github.com/0Reliance/glassy-companion/releases/tag/v2.13.0.

  • SSE EventSource moved to a persistent offscreen document — Chrome MV3 evicts service workers after ~30s of inactivity, silently killing the EventSource that powers the Obsidian Bridge. The offscreen document is persistent and never evicted, so the bridge stays alive indefinitely while the browser is open. A connectSSEInServiceWorker() legacy fallback is retained for Firefox versions that lack the offscreen Documents API.
  • chrome.runtime.onSuspend handler — flips the bridge status to connected:false when the SW is evicted, so the popup never shows a stale "connected" indicator.
  • Test Connection now tests the full bridge loop — both the SSE bridge to the server and the direct extension→Obsidian fetch. A green result confirms both legs; a yellow warning surfaces if the SSE bridge is offline while the direct fetch works.
  • Settings sync to server — on SSE onopen, the extension pushes its Obsidian URL to the server's users.obsidian_url column, eliminating drift between the web-app DB and extension storage. The Obsidian API token never leaves the extension.
  • Membership gate removed from popup — the extension no longer does any client-side entitlement gating for glassy_keep. The server remains the single gatekeeper via requireKeep. This removes the "Unlock GlassyKeep" upsell wall for self-hosted admins (whose clear_lifetime tier is resolved server-side, not in entitlements_json).
  • Softer entitlement toast — a 403 from the server now reads "Save unavailable — Saving from the extension requires a Pro plan. Manage your items in your Glassy workspace." instead of "Upgrade required."

July 23, 2026 — Docs refresh pass

The docs surface (docs.glassy.fyi) is brought in line with shipped product state:

  • Companion page fully rewritten to document v2.13.0 (installation across Chrome / Firefox / Edge / Opera / Brave / Arc, the new release notes, and the migration from v2.12.0).
  • FAQ expanded from a stub ("being expanded") to a comprehensive reference covering pricing, privacy, AI, self-hosting, Companion, Obsidian, and security.
  • Thin pages fleshed out: content-types.md, import-export.md, search.md, webhooks.md, access-modes.md. Each now documents the current state of the feature instead of "Learn more on Glassy Learn" placeholders.
  • access-modes.md updated to document the third INSTANCE_ID value: self_hosted was missing from the table; the page now covers the full public / clear / self_hosted matrix with the recommended combinations.
  • obsidian-vault.md v3.0+ typo fixed to v2.13.0+ (Companion never shipped a v3.x line).
  • last-reviewed frontmatter refreshed across all docs pages to today.

July 8, 2026 — Companion v2.12.0: Unified Save Card & Obsidian Bridge

Glassy Capture extension v2.12.0 — Unified Save Card & Obsidian Bridge: see release notes at https://github.com/0Reliance/glassy-companion/releases/tag/v2.12.0.

  • Unified Save Card — Quick Save and Smart Save are now merged into a single progressive-disclosure card. Type chips (Article, Video, Repo, Bookmark) re-run the page interpreter on change so metadata is always fresh before save. One card, one flow, less friction.
  • Obsidian Bridge — the extension now acts as a local proxy between the Glassy server and Obsidian Local REST API. The server pushes proxy requests to the extension via SSE; the extension calls Obsidian on 127.0.0.1:27124 and returns the result. This bypasses WSL2/Docker networking entirely. Also pushes captures to the vault as markdown files with YAML frontmatter.
  • Cloud → self-host switching — changing the server URL in extension settings now detects the change, prompts re-authentication, reconnects the Obsidian bridge SSE, and invalidates caches.
  • Optional host permissions — the extension declares optional_host_permissions for 127.0.0.1:27123/27124 so users can opt into localhost access for the Obsidian bridge without widening the base permission set.

July 8, 2026 — Obsidian Bridge: browser extension as local proxy

Dashboard + Companion Extension — Obsidian connectivity overhaul:

This release solves the fundamental WSL2/Docker networking problem: when Glassy runs in a container on Windows, the container cannot reach Obsidian on the Windows host's 127.0.0.1. The browser extension, running on the Windows host, can.

  • Browser extension Obsidian Bridge — the Glassy Capture extension now acts as a local proxy between the server and Obsidian. The server pushes proxy requests to the extension via SSE; the extension calls Obsidian on 127.0.0.1:27124 and returns the result. This bypasses WSL2/Docker networking entirely.
  • Extension settings UI — new "Obsidian Bridge" section in extension settings with URL input, API key, test connection, and connection status indicator.
  • Server bridge registry — in-memory registry tracking connected extensions per user; all Obsidian route handlers and the AI context path check the bridge first and fall back to the direct server→Obsidian path when the bridge is offline.
  • Configurable Obsidian network allowlist — new OBSIDIAN_NETWORK_ALLOWLIST env var lets self-hosted operators add internal IPs/hostnames (LAN, Tailscale, WSL2 bridge) to the Obsidian SSRF allowlist without a code change. Self-signed TLS is now tolerated for these hosts too.
  • Environment-aware diagnostics — the Obsidian diagnostics endpoint now detects WSL2/Docker deployment context and gives specific, actionable recommendations ("Install the browser extension and enable the Obsidian Bridge") instead of generic "try HTTP" messages.
  • Push-to-Obsidian from extension — captures saved via the extension can now be pushed directly to the Obsidian vault as markdown files, without a server round-trip.
  • Cloud → self-host switching — changing the server URL in extension settings now detects the change, prompts re-authentication, reconnects the Obsidian bridge SSE, and invalidates caches.
  • Core app UI bridge status — the Settings → Obsidian panel now shows "Browser extension bridge active" when connected, or "Install the Glassy Capture extension" recommendation when not connected.
  • Optional host permissions — the extension declares optional_host_permissions for 127.0.0.1:27123/27124 so users can opt into localhost access for the Obsidian bridge without widening the base permission set.

July 7, 2026 — Self-Hosted Launch Gating (beta.3) + Brand-Asset Rebuild

Dashboard v2.35.0-beta.3 — Self-Hosted Launch Gating: - Transparent sign-in panel on the appliance — explains that sign-in is temporary, tied to Kickstarter goals, and that the appliance is local-first. Links to the self-host README "Why sign in?" section. - Persistent "Local only — nothing leaves this device." badge in the authenticated chrome (LocalOnlyBadge component, isLocalDeployment()-gated). - Marketing-route short-circuit — anonymous visitors to #/about, #/security, #/guide, #/neurodiversity, #/obsidian, #/writers, #/students, #/extension, #/download on self_hosted are routed to login before any flash, not after. - LandingView fallthrough replaced on self_hosted — no marketing page exists on the appliance. - Compose env hygiene — identity vars (INSTANCE_ID, INSTANCE_ACCESS_MODE_DEFAULT) hardcoded as the privacy safeguard; operational vars (DEPLOYMENT_LOCALITY, ENABLE_MCP_SERVER, ENABLE_AGENT_GATEWAY, OBSIDIAN_HOST_OVERRIDE) now .env-overridable. - Ollama URL fix in .env.example — was confusingly localhost; now host.docker.internal.

Glassy Capture extension v2.11.2 — Self-Hosted CSP & Local Network Support: see release notes at https://github.com/0Reliance/glassy-companion/releases/tag/v2.11.2.

Marketing (glassy-www) — Brand-Asset Container Rebuild: - glassy-www-prod and glassy-learn-prod rebuilt with brand-asset ingest (cfc1a91/997480f); new logo.png (md5 07a1cc64…), og-default.png, unified favicon.svg, pwa-512.png, apple-touch-icon.png. - app.glassy.fyi already serves the new assets (baked into the f8fa465 promotion 2026-07-05). - One stale Cloudflare URL remaining: https://glassy.fyi/favicon.svg (founder dashboard purge).

July 6, 2026 — Self-Hosted Blueprint (Phases A–D) + Ecosystem Cleanup

Dashboard v2.35.0-beta.1 / -beta.2 — Self-Hosted Blueprint Phases A–D: - INSTANCE_ID=self_hosted — third runtime identity (distinct from public and clear). - Owner auto-unlock — seeded admin granted clear_lifetime on first boot when isSelfHostedInstance(). All existing entitlement gates (Keep, Calc, themes, Voice Studio, KB, MCP, Agent Gateway) pass through isClearMember() so no tierPolicy.js change needed. - Registration lockdown (defense-in-depth) — authRoutes.js /register returns hard 403 REGISTRATION_DISABLED as the first check; loadAdminSettings() forces allowNewAccounts=false and instanceAccessMode='private' on every boot; admin PATCH /api/admin/settings silently ignores mutations to those fields on self_hosted. - Server-side service gating — on self_hosted: /api/commerce/*, /api/stripe/*, /api/push/* not mounted (404); Sentry not initialised; sendEmail() no-op; deductAiCredits() no-op. - BYOK-only AI on the appliance — GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY not loaded from env; BYOK via Settings → API Keys and local Ollama work; no-provider path returns 402 { code: 'NO_AI_PROVIDER' } with setup guidance. - Frontend commerce hiding — 9 components migrated isClearInstance() → isBundledInstance(); App.jsx redirects #/register → #/login, #/store → #/settings, #/upgrade → #/settings on self_hosted. - AiUpgradeModal BYOK guidance — no upgrade/credits copy; shows "Add your own API key in Settings → API Keys" instead. - Self-host compose overlays — docker-compose.ollama.yml, docker-compose.https.yml (Caddy), docker-compose.watchtower.yml. - docs/SELF_HOSTED_DEPLOYMENT.md written — account management, sub-accounts, account recovery, AI providers (BYOK + Ollama), Obsidian sync, backup/restore, upgrading, security, monitoring. - Backup consolidation — server/migrations/backup.js honours BACKUP_ENCRYPTION_KEY (AES-256-GCM), BACKUP_DIR, BACKUP_RETENTION_DAYS; server/utils/backup.js fixes for .db.enc listing + cleanup double-delete.

Ecosystem Cleanup (11/11 in-scope recommendations closed): - Brand sweep — "AI-powered" and "unlimited" removed from all customer-facing surfaces (4 glassy-dash files, 4 glassy-www blog posts, 2 blog excerpts, 2 pricing surfaces). - Clear reframe — PricingTeaser reordered (community-first, Docker as a perk); FAQSection self-host rewritten; Agent Connections UI copy updated. - Bootstrap path hardened — server/utils/spaBootstrap.js extracted; refactored server/index.js. 13 new tests (was zero coverage on this CSP-critical path). - adminSettings getter closure + Stripe webhook idempotency — both now have regression tests. - Pagefind scoped to main> across all 54 glassy-www pages. - Stripe POZI account retired (dormant) — 2 test subs cancelled on acct_1RDYEPGV8GF9abap; duplicate products deactivated (prod_USsO1H1NvsB2H1, prod_USsOcvWV0YjEAk); redundant disabled webhook endpoint deleted; stray pozi100 glassy-dash-prod container (running old sk_live_51RD…) stopped; Clear test accounts (id 25, 26) removed from ~/.GLASSYDASH_CLEAR/notes.db via FK cascade. - Resend email verified — glassy.fyi sender verified; live test email delivered (id 6a4a4b74-89ce-4b04-9de7-11e14f762df0); EMAIL_FROM=Glassy <[email protected]>; [email protected]. - Test suite: 800 → 821 pass (+21 self-host gating).

July 5, 2026 — Brand-Asset Harmonization, Blog Imagery & LMS Screenshots

Brand-Asset Harmonization: - Unified lowercase "glassy" brand logo across glassy-www, glassy-learn, glassy-dash. - Unified favicon.svg and PWA icons (pwa-512.png, apple-touch-icon.png). - High-DPI Open Graph banners (og-default.png, og-dashboard.png) regenerated with clean lowercase "glassy" typography on dark aurora backgrounds. - Source committed 2026-07-05 to glassy-www (cfc1a91) and glassy-learn (997480f). Container rebuild followed 2026-07-07 (see above).

Supportive Blog Imagery (glassy-www): Premium frosted-glass screenshot panels inserted into 7 blog articles: - The inbox-zero approach to knowledge management (Today view / dashboard) - Glassy for students: lecture capture to exam prep (Voice Studio) - The research capture loop: save, index, query, cite (Knowledge Base) - MCP server showdown: Glassy vs Obsidian vs Notion (Notes Grid) - Obsidian in the browser (Synced Docs View) - Your notes are not training data (Local AI Assistant) - Sensory-friendly UIs (Theme presets)

LMS Screenshots (glassy-learn): Refreshed in getting-started and capture lessons to show the correct dashboard views, note card libraries, and settings options.

July 4, 2026 — Code-Review-Driven Cleanup (11 recommendations closed)

All 11 in-scope recommendations from the June 13 evaluation stabilization plan closed: 1. Brand sweep closed (AI-powered + unlimited removed). 2. Clear reframe in glassy-dash landing + Agent Connections UI. 3. spaBootstrap.js extracted with 13 new tests. 4. adminSettings getter closure locked with 2 regression tests. 5. Stripe webhook stripe_customer_id idempotency locked with 1 regression test. 6. Pagefind scoped to main> across all 54 glassy-www pages. 7. Bootstrap path integration tested. 8. MCP server module tests fixed (5 of 14 timeouts resolved). 9. Pre-deploy migration validator caught missing name export on migration 0083. 10. CJS→ESM conversion completed for the 5 tool modules (.mjs). 11. Real production runtime bug (top-level await in CJS) caught during live deploy and fixed with async IIFE.

Server test suite: 757 → 800 pass.

June 29, 2026 — Beta finalization: self-hosting, GHCR, download page

Dashboard v2.34.2 (unreleased) — Beta Finalization & Distribution: - GHCR image publishing — Docker images published to ghcr.io/0reliance/glassy-dash:<tag> on git tag push. Compose files now default to the GHCR image. - Self-hosted Docker recipe — one-command docker compose for self-hosters with live Obsidian sync, Ollama, and localhost integrations. Includes DEPLOYMENT_LOCALITY=local, APP_URL, ENABLE_MCP_SERVER, and OBSIDIAN_HOST_OVERRIDE. - #/download page — user-facing page with three install paths: PWA, Glassy Capture extension, and self-hosted Docker. - Deployment locality signal — DEPLOYMENT_LOCALITY env var (cloud|local) exposed to the client. Obsidian settings show a cloud-limitation banner when running on cloud, with a link to #/download. - Obsidian settings visible on all instances — the Obsidian section is no longer hidden on SaaS; instead, a banner explains the localhost limitation. Reverses the June 28 "hidden on SaaS" entry. - MCP enabled in self-host — ENABLE_MCP_SERVER=true in self-host compose; VITE_ENABLE_MCP_SERVER build arg added to Dockerfile and GHCR workflow. - Optional env vars documented — GEMINI_API_KEY, RESEND_API_KEY, EMAIL_FROM, OLLAMA_BASE_URL documented in self-host guide. - Release strategy ADR — single branch, tag-based releases, deferred branching. See docs/decisions/2026-06-29-release-strategy.md. - CI fix: buildx driver — docker/setup-buildx-action changed to driver: docker so the locally-built glassy-deps image is visible to the build-push step. - Repo + GHCR private during beta — both the GitHub repo and the GHCR Docker image are private until final release. Self-hosters need GitHub access + docker login ghcr.io. All quick start snippets updated with the login step and a beta privacy notice.

June 28, 2026 — Agent Gateway, security hardening, settings panel review

Dashboard v2.34.2 (unreleased) — Agent Gateway + Security + Settings: - Agent Gateway — dispatch tasks to AI agent frameworks (OpenClaw, Hermes, Antigravity) from a central workspace. Pluggable adapter system with status cards, dispatch panel, activity feed, and MCP config snippets. SSRF-protected, user-scoped (migration 0086), feature-flagged. 62 tests. - Infrastructure security audit — SSH hardening (no root, no password auth), fail2ban, UFW firewall, npm audit fix (41→18 vulnerabilities, 0 critical/high), apt upgrades on both production hosts. - Hosted security hardening — user isolation for agent_connections (user_id scoping), async execFile, feature-flag defence-in-depth guard, Hermes CLI argument injection guard. - SaaS/self-hosted settings consistency — local-only agent frameworks hidden on SaaS, Ollama local option hidden on SaaS, music section language made deployment-agnostic. (Note: Obsidian section was hidden on SaaS on June 28, then reversed on June 29 — see above.) - Settings panel review pass — 15 fixes: YouTube embed autoplay/playlist/thumbnail, pagehide keepalive flush, SectionShell light-mode footer, YouTubeInput debounce, accessibility (role=switch + aria-checked), stale backgrounds cleanup, announcements toggle moved, parseStartTime ?start= support, MusicSettings dead code removed. - React + TanStack audit — 14 fixes across 9 files. - npm audit fix — resolved vitest (critical), vite (high), ws (high), undici (high), uuid (moderate). 750 server tests pass.

June 25, 2026 — Account switch refactor & auth cache

Dashboard v2.34.2 — Account Switching Optimization: - Auth middleware caching — in-process short-TTL caches for user (15s) and account/primary (30s) lookups with hoisted prepared statements. Invalidation helpers wired into all write paths. - Account switch — memoized sorted accounts, explicit SSE teardown, theme_config in GET /accounts.

June 22, 2026 — Cloud AI audit & local embeddings

Dashboard v2.34.0 — Cloud AI Audit & Local Embeddings: - Gemini model catalog audited — migrated image generation off Imagen (shut down 2026-06-24) to Gemini native image gen. Corrected invalid model IDs. Default text model: gemini-3.5-flash. - Local embeddings wired — on-device Related Notes and Smart Tag Suggestions via 384-dim IndexedDB vector cache. Cloud fallback remains 768-dim Gemini.

June 13, 2026 — MCP SDK Upgrade & S2 Hardening, companion v2.11.0

Dashboard v2.33.0 — MCP SDK Upgrade & S2 Hardening: - Real MCP SDK integration — replaced hand-rolled JSON-RPC endpoint with the official @modelcontextprotocol/sdk v2. New server/mcp/index.js uses McpServer + NodeStreamableHTTPServerTransport (stateless) + createMcpExpressApp (DNS rebinding protection). All tools now use server.registerTool() with zod v4 schemas. - glassy_obsidian_proxy tool — transparent proxy to your Obsidian Local REST API's /mcp/ endpoint. SSRF-protected, TLS fallback, Docker host override. - 3 MCP prompts — glassy_summarize, glassy_capture_prompt, glassy_daily_brief. - 3 dynamic resources — glassy://kb/search/{query}, glassy://recent/{sourceType}, glassy://status. - completable() argument hints on enum fields across tools. - Tier-based KB rate limiting — free 30/hr, Pro 300/hr, via kbTierLimiter middleware.

Glassy Capture extension v2.11.0 — Store Readiness & Reliability Hardening: - Firefox CSP — content_security_policy added to manifest.firefox.json (required for AMO submission). - Storage quota monitoring — STORAGE_QUOTA_ALARM 6-hourly check; warns at 80%, auto-trims offline queue at 95% critical. - Bundle size optimization — vite.config.js uses manualChunks to split vendor libraries and UI components. - MCP bridge config snippets — Settings → Integrations shows copy-pasteable config snippets for Claude Desktop, Cursor, Windsurf, OpenCode, Hermes, Openclaw, Pi, and any MCP-compatible client with the live server URL. - Manifest version sync — both manifest.json and manifest.firefox.json bumped to 2.11.0 to match package.json. A check-version-sync.mjs prebuild guard is being added to prevent recurrence.

June 12, 2026 — Second Brain P1–P3, companion v2.10.0

Dashboard v2.32.0 — Second Brain P1–P3: - MCP Key Settings UI — Settings → Integrations panel for generating, displaying, copying, and regenerating MCP API keys. Shows config snippets for Claude Desktop, Cursor, Windsurf, OpenCode, Hermes, Openclaw, Pi, and other MCP-compatible clients. Lists all available MCP tools. Gated behind ENABLE_MCP_SERVER. - 4 additional MCP tools — glassy_add_capture, glassy_get_recent, glassy_obsidian_query, glassy_note_create. - Second Brain feature flags — ENABLE_MCP_SERVER, ENABLE_CORPUS_INDEXER, ENABLE_KB_QUERY, ENABLE_KB_UI, ENABLE_HYBRID_SEARCH, ENABLE_MCP_BRIDGE, ENABLE_OBSIDIAN_MCP_PROXY. All default to false.

Glassy Capture extension v2.10.0 — Knowledge Base Search: - KB tab and search view — new "KB 🧠" tab in the popup for searching the Glassy Knowledge Base (bookmarks, notes, vault files). Debounced search, source filter tabs, corpus status banner, result items with source badges and relevance scores.

June 11, 2026 — Free Tier Navigation Fix

Dashboard v2.31.0 — Sidebar Navigation Gating: - Recording and Keep nav items are now hidden from the sidebar when the user lacks the corresponding entitlement. A new Store card directs free users to the marketplace. - The Upgrade card is hidden when the Store card is shown to avoid two competing monetization CTAs.

June 9, 2026 — Structured capture reception, type-aware reader & companion v2.9.0

Dashboard v2.29.0 — Structured Capture Reception & Type-Aware Reader: - structured_data column (migration 0081) — SQLite TEXT column on bookmarks stores type-specific capture metadata as JSON (max 8 kB). - Video embed in Keep reader — captures of type video with a YouTube videoId render an embedded player (youtube-nocookie.com) instead of prose. - Repo card in Keep reader — captures of type repo render a structured header card (owner/repo, ★ stars, language pill, license, topics tags) above the README prose. - 4 content types consolidated — article, video, repo, bookmark. Old research/product captures display as article/bookmark labels.

Dashboard v2.28.0 — GlassyKeep Immersive Reader: - Full-window two-pane reading experience — rebuilt from a 672 px glass card. Ambient backdrop (OG image fills window), hero image strip, left-rail Table of Contents with IntersectionObserver scrollspy, in-reader text highlights (yellow/green/blue/pink, persisted via highlight API), reading-progress bar. - Keyboard shortcuts: Esc close; +/- font size; j/k scroll; [ toggle rail. - Font-size control (5 steps, 13–21 px). framer-motion animations respect prefers-reduced-motion.

Dashboard v2.27.0 — GlassyKeep Reader Access + Animation Polish: - Reader now accessible via a BookText icon in each bookmark card's action bar. Cards with captured content show icon in amber; without show dimmed teal. Clicking the card body on a captured bookmark also opens the reader directly. - Removed layout-reflow animations from bookmark grid (simple 120ms fade only).

Dashboard v2.26.0 — Sidebar Restructure + Pro Entitlement Gate Fixes: - Sidebar restructured into two sections: Views and Organize. GlassyKeep is now a first-class sidebar destination. - Pro entitlement gate fixes for GlassyKeep access.

Companion v2.9.0 — Structured Capture & Reliable Execution: - Two-button main bar — Save Page and Screenshot. Four first-class content types: article, video, repo, bookmark. - Screenshot via direct SW routing — captureVisibleTab no longer requires a content-script relay. Works on restricted URLs, PDFs, and stale tabs. - Structured capture pipeline — typed structuredData per content type (video: videoId/provider/channel/duration; repo: owner/repo/stars/language/topics; article: abstract/doi/language). Interpreter re-runs on type change. - ensureContentScript() fallback eliminates “Could not establish connection” errors. - Fixed: interpreter-enriched fields silently dropped; screenshot non-functional on restricted pages; Smart Save chips had no effect on scraping behavior.


June 7–8, 2026 — Workspace performance, rich image viewer, capture quality & extension release

Dashboard v2.24.0 — Rich image lightbox: - Zoom + pan image viewer replaces the basic full-screen overlay. Scroll or pinch to zoom toward the cursor (up to 6×), drag to pan when zoomed, double-click to toggle 1×↔2.5×, and use +/−/0 keyboard shortcuts. A live zoom-percent readout and a download button sit in the toolbar. Works for screenshot, element-capture, and image notes.

Dashboard v2.25.0 — Workspace load & switching speed: - ~3 second notes-appear delay eliminated. The app was firing 6 non-critical API requests at startup (documents list, documents trash/archived, voice recordings, voice trash/archived) in parallel with the critical notes list, saturating the browser's HTTP/1.1 connection pool. The /api/documents call alone could take 2.8 s, stalling everything behind it. - Non-critical queries (Documents and Voice subsystems) now defer until the browser main thread goes idle via requestIdleCallback. Data still loads promptly after first paint and is fully available for search, the Trash view, and all other features. - Account switching also benefits — the idle gate resets on each account change so the incoming account's notes list always gets priority over the connection pool.

Companion v2.7.0 — Multi-account capture & frictionless server setup: - Account picker — choose which account a capture is written to directly in the popup. Captures no longer silently land in the wrong profile when you work across multiple accounts. - Pre-login server selection — point the extension at your server (or self-hosted instance) before authenticating, removing the login-logout dance. - Unsavable-URL guard — friendly warning shown for pages that can't be saved (chrome://, localhost, private IP ranges) instead of an opaque error.

Companion v2.8.0 — Screenshot UX & capture quality: - Screenshot → immediate Smart Save — clicking the screenshot button now opens Smart Save with the image pre-loaded instead of storing silently and requiring a second popup open. - SPA / app-page quality gate — the extractor now rejects pages that yield fewer than 200 meaningful characters (navigation-heavy shells, SPA dashboards) and falls back to a clean bookmark, eliminating junk captures. - Decorative image filtering — aria-hidden, role="presentation", and sub-pixel tracking images are now skipped during extraction.


June 1, 2026 — Web capture, reliability & privacy hardening

The Glassy Capture browser extension's full capture pipeline is now live, and this update focuses on making it dependable end to end.

Web capture (Companion 2.5.0): - Save anything from the web — pages, snippets, page elements, and full screenshots flow straight into your knowledge base. Screenshots and element captures now render as proper notes so they read the way you expect. - Screenshots embed your own instance — captured images point at the server you actually use (glassy.fyi or your self-hosted instance), and an image is only saved when you save the capture, so cancelling never leaves stray files. - Nothing gets dropped offline — a failed save while offline is now reliably queued and retried instead of being lost, and the retry queue stays fast even when it's full.

Privacy & reliability (server — Dashboard 2.22.0): - Per-account screenshot storage limits keep shared capture storage fair and prevent any single account from filling it. - Obsidian import is safer and idempotent — re-importing a vault no longer duplicates notes, and malicious archives can no longer exhaust server memory. - Storage usage counting now self-heals after a transient database hiccup.


Companion v2.4.0 — May 30, 2026 — Element picker persistence & URL canonicalization

Companion 2.4.0 only (extension update):

Added: - Element picker state persists across panels — the CSS selector you used to capture a page element is remembered; re-opening the popup pre-selects the same element so you can capture the same section from updated pages without re-clicking. - URL canonicalization — urlUtils.js now normalises captured URLs: tracking parameters (utm_*, fbclid, etc.) are stripped, www. variants are unified, and hash fragments are removed before saving. Deduplication logic benefits from cleaner canonical keys. - Rule Engine: element-pick preset — you can now assign the Element Picker as the default preset for a URL pattern in the Rule Engine (Settings → Rules).

Fixed: - Screenshot capture on Firefox no longer fails silently when the tab is in a reader-mode overlay.


Companion v2.3.2 — May 29, 2026 — Offline queue hardening

Companion 2.3.2 only (extension update):

Fixed: - Queue flush is now O(n) — previously each queued item required a separate storage round-trip; large queues could time out. The flush now processes all items in parallel and commits in a single read-modify-write, so a queue of 50 items flushes as fast as a queue of 1. - No captures lost during a flush — items enqueued while a flush is in progress are now correctly preserved (the apply step re-reads storage at commit time instead of relying on a stale snapshot). - Screenshot uploads retry independently — a failed image upload no longer blocks the queued text capture from syncing.


v2.19.0 — May 11, 2026 — Remove legacy interval→plan shim

Removed: - LEGACY_INTERVAL_TO_PLAN shim — the server no longer accepts a bare interval field on subscription checkout requests. All clients must send plan directly (e.g. pro_monthly, pro_yearly). Sending only interval now returns 400 Invalid or missing subscription plan. No customer migration required — zero active users on the old path.


v2.18.1 — May 10, 2026 — P0 marketing copy pass (landing)

Changed: - Landing page copy overhaul — Hero, Problem, Neurodiversity Highlight, and Pricing Teaser sections refreshed with calm, precise, ND-affirming voice. New H1 "Keep it / Glassy.", capability-led body copy, and a micro-FAQ ("One Pro price · Your data on cancel · Cloud AI is optional"). Pricing SSOT preserved verbatim throughout.

Added: - docs/MARKETING_COPY_PASS_PLAN.md — v1 roadmap covering this P0 pass plus upcoming P1 (AudienceCards / FeatureBentoGrid / TestimonialsSection / LocalAIShowcase / FAQ), P2 (SEO/meta + footer + public/glassy-landing.html), and P3 (Jekyll docs pass).


v2.17.1 — May 10, 2026 — Pricing copy fix

Fixed: - Corrected GlassyKeep / GlassyCalc pricing copy across surfaces — both are bundled into every Pro plan (not sold separately). Any copy describing them as à la carte sidecars has been corrected.


v2.17.0 — May 10, 2026 — Pro entitlement bundle + tier cleanup

Changed: - GlassyKeep and GlassyCalc are now included in all Pro plans — both sidecars are bundled into every Pro tier (monthly, yearly, and lifetime) at no extra cost. Existing separate add-on purchases are honoured; members who paid for either sidecar retain full access and will not be charged again. - pro_quarterly soft-retired — quarterly Pro subscriptions are no longer offered to new signups. All existing quarterly members are grandfathered with no change to their access or billing cycle. - Tier constants consolidated — PRO_INCLUDED_ENTITLEMENTS, CLEAR_TIERS, and PRO_TIERS are now declared as frozen constants in the server, providing a single source of truth for entitlement logic across all tier checks.

Internal: - clear_lifetime scaffolded in the tier registry to support a future Clear lifetime offering. Not publicly listed or sold at this time.


v2.16.1 — May 9, 2026 — Clear instance separation

Added: - Clear instance gating — when Glassy is deployed as a Clear instance (INSTANCE_ID=clear), Pro upsells, marketing surfaces, and Pro-only checkout flows are hidden end-to-end. Clear plans and the GlassyCalc sidecar remain available. The public Glassy instance is unchanged. - Clear self-hosted deploy bundle — production docker-compose and operator README under deploy/clear/ for organizations running their own Clear instance. See Clear self-hosted.

Changed: - Pricing copy reconciled across landing page, in-app help, FAQ, and the AI assistant's knowledge base. Single source of truth: Pro $17/mo or $100/yr · Clear $80/mo or $300/yr · AI top-up packs $10 / $20 / $50 (100k / 200k / 500k credits, never expire). Cloud AI continues to be described as a generous monthly allotment. - Pro Lifetime is fully retired for new signups; existing Pro Lifetime members remain grandfathered forever. The GlassyCalc sidecar was available as a one-time add-on for Pro users. (Superseded: GlassyCalc is bundled into all Pro plans from v2.17.0.)


v2.16.0 — May 8, 2026 — Public Window goes Pro + Settings v2

Changed: - Public Window publishing now requires Glassy Pro. Free accounts can still view Public Windows, but claiming a slug, editing the bio/tagline/social links/banner/theme/layout/accent color now requires a Pro subscription. The Account HUD Window button still works — non-Pro users see a glassy upsell card on the Public Window page and in profile settings. - Settings panel refactor — the unified Settings UI is now built from modular sections with lazy loading and per-section dirty tracking. Behavior is preserved; the structure is now extensible so new settings can ship without bloating the panel. - Deploy hardening — the production deploy pipeline gained a concurrent-deploy lock, a fast-fail check when local main is behind origin/main, a heartbeat indicator during long builds, and an automatic build log captured to /tmp/glassy-dash-deploy-<sha>.log.

Security: - Server-side enforcement of the Public Window tier gate — the profile API now rejects writes to Public Window fields (slug, bio, tagline, social_links, public_theme, public_layout, accent_color, banner_url) from non-paid accounts with 403 PUBLIC_SHARE_NOT_ALLOWED. display_name, avatar_url, and Obsidian settings remain writable for free accounts.

Fixed: - Account HUD — Archive now opens the real notes archive view, outside-clicks reliably close the panel, and short browser viewports no longer cause the HUD to overflow above the screen. - Extension page — the authenticated #/extension route now renders inside the workspace shell (matching #/store); release links point at the published Glassy Capture v2.2.2 packages.


v2.15.2 — May 7, 2026

Added: - Instance-wide private mode — self-hosted deployments can set Admin → Instance Access to Private, hiding the entire site from logged-out visitors with deep-link stashing and post-login resume.

Fixed: - Global search now crosses all sub-accounts — results from every workspace surface in one overlay, each tagged with an account pill badge. - Account HUD: archive action now opens the real notes archive view, outside clicks reliably close the panel, and the panel scrolls internally on short screens instead of overflowing. - Authenticated #/extension route now renders inside the workspace shell (like #/store) for logged-in users.

Companion: - Glassy Capture v2.2.2 — JWT token now persists in chrome.storage.local so users stay signed in across browser restarts.


v2.15.1 — May 6, 2026

Fixed / verified: - Global page-glass controls and ephemeral per-page glass previews are live in production. - App release metadata is aligned across GitHub, the repository, the live production container, and public health output. - Production is verified at commit 9672b3eccc3c51bebfca3a4869b36615d93ca84a, with v2.15.1 marked as the latest GitHub release. - Glassy Capture v2.2.1 is published with both Chrome .zip and Firefox .xpi packages.


v2.15.0 — May 6, 2026 — Pricing Relaunch

Changed: - Glassy Pro is now $17/month or $100/year. - Glassy Clear is now available at $80/month or $300/year, bundling every current and future sidecar/theme add-on. - Lifetime is retired for new purchases; existing lifetime members remain grandfathered. - Paid users can extend cloud AI usage with $10 / $20 / $50 top-up packs that never expire. - Public pricing and AI copy now describe a generous included monthly cloud AI allotment instead of exposing the internal cap amount.


v2.14.5 — May 5, 2026

Fixed: - Outbound network calls now fail fast across AI providers, icon/RSS lookups, music proxy paths, and Obsidian integration requests, reducing idle timeout noise and hung upstream sockets. - Local AI downloads and format-polish flows are more reliable: WebLLM downloads use a dedicated cancellation worker, stale cancel state is cleared before downloads, JSON schema response formats are passed correctly, and rich-text block boundaries are preserved when generating plain-text formatting plans. - Session and account switching are safer: sliding auth refreshes use atomic expiry guards, account unlock token rotation is transactional, and AI/embedding/HUD state is reset during account switches. - Production-only React regressions were cleaned up, including the EmojiSuggestion require() crash and a tag-filter feedback loop that could trigger React error #185. - Search snippets now highlight every match, and delete confirmations clarify the 30-day auto-purge window.

Companion: - Glassy Capture v2.2.1 is the current extension line, adding Firefox packaging from v2.2.0 plus a 429 retry fix that honors Retry-After headers.


v2.14.4 — May 3, 2026

Added: - AI Assistant workspace context broker — /api/ai/ask and /api/ai/ask-stream now search notes, documents, voice transcripts, bookmarks, and help articles in the active account scope and return citation-ready context. - Obsidian Docker host bridge — containerized server can now reach the host's Obsidian Local REST API via host.docker.internal while users continue entering https://127.0.0.1:27124.

Fixed: - Obsidian search now uses the correct POST /search/simple/?query=... endpoint. - Obsidian setting now defaults to HTTPS (https://127.0.0.1:27124). - NoteCard delete confirmation no longer renders behind the card backdrop — portaled to document.body so the dim layer covers the viewport. - Account switch is faster: account-scoped query keys (including unified tags) are namespaced by active account, so switch-back uses isolated cache buckets without cross-account bleed.


v2.14.3 — May 2, 2026

Fixed: - Service Worker bad-precaching-response loops resolved — large media directories (backgrounds/**, note-backgrounds/**, avatars/**, uploads/**, images/**, guide-assets/**, wasm/**) are no longer baked into the precache manifest, eliminating the post-deploy reload-and-wipe loop. Matching NetworkFirst runtime caching keeps these assets available offline.

Added: - scripts/disable-2fa.js admin recovery script for locked-out 2FA users (referenced by the troubleshooting doc; the file is now actually present).

Docs: - Troubleshooting doc accuracy fixes: real 2FA column names (totp_enabled / totp_secret / totp_recovery_codes), real uploads path derivation, real per-tier file size limits, corrected WAL checkpoint snippet.


v2.14.2 — April 29, 2026

Improvements: - Streak meter fully rewritten — client-timezone bucketing, 90-day tracking window, edit-only days now count toward your streak, shield peek bug fixed - Note bottom toolbar polish (transparency promotion, cleaner button states) - ExtensionView scroll area fixed


v2.14.1 — April 28, 2026

Bug Fixes: - AI providers no longer crash on tool-augmented prompts (Anthropic and Ollama providers fixed) - Two flaky tests stabilised - ESLint brought to 0 errors (warnings only) - npm audit: 5 → 3 moderate vulnerabilities


v2.14.0 — April 28, 2026 — Tier Restructure

Changes: - Voice Studio is now Pro+ only — Free accounts are redirected to the store when accessing voice features - Storage caps introduced — Free: 10 MB total / 2 MB per file; Pro & Lifetime: 200 MB / 25 MB per file - Free accounts cannot create new public shares — existing public shares remain accessible - À la carte sidecars — GlassyKeep, GlassyCalc, and theme packs are now individually purchasable by Pro users; Lifetime includes all sidecars bundled - Inactivity sweeper — Free accounts inactive for 90+ days with under 1 MB of data enter a 30-day recovery window, after which data is permanently deleted. Pro and Lifetime are exempt - Lifetime subscription now clearly offers 3 years of included cloud AI, then unlimited local AI forever


v2.13.1 — April 26, 2026 — Security Hardening

Security Fixes: - Google OAuth sign-in fully restored (async DB calls were silently broken) - 2FA TOTP enrollment and recovery code regeneration now correctly persisted - AI tool responses (NAVIGATE, OPEN_NOTE, OPEN_HELP) validated against allowlists before executing - Streaming AI calls now properly abort at the provider when the browser tab closes, stopping billing - In-flight API requests now abort during account switch, preventing cross-account data writes - Cross-tab account switching detection — switching in one tab now updates other tabs automatically - Migration 0076: historical negative AI credit balances clamped to 0; future negative writes blocked


v2.13.0 — April 26, 2026

New Features: - Chrome Prompt API (Tier-0 Local AI) — Uses Gemini Nano built into Chrome for zero-download local AI; automatically preferred when no WebLLM model is loaded - Account reordering — drag and drop accounts into your preferred order - Per-account custom backgrounds — upload your own background image for each account - Live theme preview in appearance settings - PIN prompt modal — clicking a locked account now opens a modal instead of showing a toast

Bug Fixes: - AI BubbleMenu now shows a loading toast during generation (no more silent 10-second waits) - Drawing canvas v2: coordinates no longer distort on window resize, theme-default color swaps correctly on dark/light toggle, recent colors persisted across sessions - Account switch latency reduced from ~3s to ~1s


v2.12.2 — April 25, 2026

Bug Fixes: - Drawing notes now open in draw mode by default (previously required an extra click) - Mode toggle button labels corrected — shows current state (Drawing/Viewing) not the action - Per-account theme no longer dropped on rapid account switching


v2.12.1 — April 24, 2026

New Features: - Per-page glass override system — Override glassmorphism settings for individual pages without changing your global theme

Bug Fixes: - Various note background and rendering polish


v2.12.0 — April 23, 2026 — AI Access Overhaul

New Features: - Cloud AI now metered by cost, not credits — Pro users get up to $2/month of Gemini 2.5 Flash usage included - AI Usage Meter — Spending progress bar in Settings → Billing and the Platform Store - Z.AI removed — Fully replaced by Gemini 2.5 Flash as the cloud provider - Pricing rebalanced — Pro monthly: $8.99 → $7. Quarterly: $19.99, Annual: $68.99, Lifetime: $189.99


v2.11.1 — April 22, 2026

Bug Fixes: - Glassy Capture extension: SAVE_PAGE now works on SPAs, auth-gated pages, and Cloudflare-protected sites - Landing page footer links fixed - In-page anchor scroll on cross-page navigation fixed


v2.11.0 — April 21, 2026

New Features: - Account reordering — Drag and drop your accounts into any order - Per-account custom backgrounds — Each account can have its own background image - PIN prompt modal — Locked accounts show a PIN entry modal rather than a toast - Hourly unlock-token cleanup — Expired account unlock tokens are now cleaned up automatically

Security: - Rate limiters on account unlock/lock/link-verify endpoints - inactivity_timeout validated server-side (60–86400 s)


v2.10.6 — April 21, 2026

Bug Fixes: - Today page streak, reminder reconciliation, and UUID handling hardened - AI Morning Briefing citations now resolve correctly - Streak shield no longer shows a false-positive badge for inactive users


v2.10.5 — April 21, 2026

New Features: - Writers landing page (#/writers) — dedicated feature overview for writers - Students/Researchers landing page (#/students) — dedicated feature overview for students

Bug Fixes: - Trust proxy configuration fixed — real client IPs now used for rate limiting - i18n packages removed (unused; ~80KB bundle savings) - Extension API CORS hardening


v2.10.4 — April 21, 2026

Bug Fixes: - GlassyKeep share-to-public 500 error fixed (missing is_public column restored by migration 0070)


v2.10.3 — April 20, 2026

New Features: - Today page summary with migration safety for last_login_at - Version history async/await fix (resolves "fire-and-forget" bug) - SSE reliability improvements with Alt-Svc header

Improvements: - AI engine transition from json_object to json_schema for Gemini and OpenAI - glassySchema.js now source of truth for AI tool definitions - Gemma2 support for local/Ollama path

Security: - SSRF protection for Obsidian integration - JWT enforcement with algorithm pinning


v2.9.13 — April 14, 2026

New Features: - Account HUD (glassmorphic slide-up panel) - Document Icon Picker (emoji/Lucide icons) - TOTP Two-Factor Authentication - Web Push Notifications

Improvements: - 13 new slash commands - 4 callout block types (info/warning/success/error) - Progressive rendering / virtual scroll


v2.9.0 — March 2026

New Features: - Obsidian Vault Integration (full launch) - BYOK providers (OpenAI, Anthropic, Ollama, Perplexity, Groq) - Collaboration permissions model


Older Versions

For full changelog history, see the CHANGELOG.md on GitHub.


Filing Issues

Found a bug? Please report it on our GitHub Issues page with: - Version number - Steps to reproduce - Expected vs actual behavior - Browser and OS information