Tell open tabs a new version is liveNo more stale tabs.
OpenVersion is for teams that ship web apps. CI publishes each release, and every
open tab offers a reload, or reloads by itself at a safe moment when it must.
Open source · MIT ·
Works with any framework, GitHub Actions and any CI
CI · acme/shop · deploy
$npx openversion release --minor --notes-file CHANGELOG.md
Published Shop production: 1.5.0
live 1.4.2 → 1.5.0
open tabs ~40, checking every 5 minutes
notes posted to OpenAnnounce as News
next check "A new version is ready. Reload" ← nobody runs 1.4.2 for long
The problem
Deploys don't reach open tabs.
You shipped 1.5.0 at noon. Three people still have the app open from this morning,
running yesterday's JavaScript against today's server.
tab opened 08:12runs 1.4.2
GET /api/cart
import('/assets/checkout-a1f9.js')
ChunkLoadError: Loading chunk failed
tab opened yesterdayruns 1.4.1
POST /api/orders
{ "deliveryNote": … }
400 Bad Request
tab opened last weekruns 1.3.0
Bug report: "basket total is wrong"
fixed in 1.3.1, nine days ago
Status: can't reproduce
Stale chunks. Lazy-loaded files from the old build are gone, so navigation breaks.
Changed APIs. Old code sends old requests, and the server refuses them.
Fixed bugs, reported again. Support chases bugs that a reload would fix.
Features
Every tab on the live version, without nagging anyone.
A small client in your app, a CLI in your CI, and a dashboard for the moments that
need a person.
The heart of OpenVersion
CIopenversion release --minor
→ 1.5.0 is live in production
A new version is ready.1.5.0
Reload · What's new
AKA tab from this morning
✓ reloaded
Open tabs~40
1.5.0 · just reloaded
1.5.0 · up to date
1.4.2 · toast showing
1.4.2 · tab hidden
A reload, offered at the right moment
Each tab knows the version it was built as. When another one is live, it offers a
reload with the release notes, or for a hard release reloads when the tab is hidden
or on the next navigation. Never twice, so it can't loop.
preview2.0.0-rc.1
staging1.5.0
production1.4.2
production1.5.0
Every environment on its own
Production, staging and previews each have their live version, keys and history.
1.5.0 · broke checkout
1.4.2
made live
every tab reloads to 1.4.2
1.4.1
1.4.0
Roll back in one click
Make an older release live, and open tabs go back to it. The history keeps both.
Where it lives
pk_9Hq…ZtR
In the browser. Reads the live version, from your own origins only.
ov_live_••••3f9a: in a CI secret. Publishes. Shown once.
Two keys, never mixed up
The browser can only ask what's live. Only CI's deploy token can publish.
Publish from where you deploy
One CLI step after the deploy, with an idempotency key, so a retried job publishes once.
answered 30497%
One cheap request per tab
A check is one query with an ETag. Unchanged answers are an empty 304.
ChunkLoadError
Caught by the client1
Checked: 1.5.0 is live, reloaded once
vite:preloadError · failed import()
Stale chunks, recovered
A failed lazy load makes the tab check at once and reload into the new build.
~/open-version
$ docker compose up -d✔ openversion and postgres started→ open http://localhost:8080
Self-host in one command
One Docker image, configured with environment variables, with a sensible default for
every setting.
Hard refresh
After a bad deploy or a security fix, every open tab reloads once. No new release needed.
Release notes in OpenAnnounce
Each release can post its notes to OpenAnnounce as News, exactly once.
Export and import
Everything you put in comes out again, and goes back in.
How it works
Running in five minutes.
1
Run it
Start the server and add your app. Each environment gets a public key and a deploy token.
terminal
$ git clone https://github.com/openstuff-dev/open-version
$ cd open-version && docker compose up -d
2
Add the client
Tell it the version your build is. <ov-update> shows the toast.
Each team has its own apps, keys and people, kept apart from the other teams in the
database itself.
Apps and environments
One row per environment, with its live version and how many tabs have it open.
Teams
Separate teams on one server, kept apart with Postgres row-level security.
Safe by default
Tokens stored hashed and shown once, integration keys encrypted, no tracking.
Roles
Admins manage people. Developers publish and refresh. Viewers look.
Allowed origins
Only your own sites can ask an environment what's live.
Export and import
Move a team, its apps and its release history to another server.
FAQ
Questions people ask first
Is it free?
Yes. OpenVersion is open source under the MIT licence, so self-hosting costs
nothing but your server.
Will it reload a page while someone is typing?
Not for a normal release: people get a toast and reload when they choose. A hard
release or a hard refresh waits until the tab is hidden or the person navigates,
or shows a countdown with Reload now. Your app can also take over the forced
reload, for example to save a form first.
Does it work with my framework?
Yes. The client is plain JavaScript and <ov-update> is a web
component, so it works with Angular, React, Vue, Svelte or none. Give it the
version your build is, from your bundler or package.json.
Where is my data?
On your own server, in your Postgres. OpenVersion keeps your apps, releases and
a per-minute count of version checks for the last hour, with no addresses or user
data. Admins can export everything to one JSON file.
How is this different from a service worker update?
A service worker knows a new build exists only after it downloads one, and it
can't roll back or force a reload across every tab. OpenVersion knows what's live
from your CI, works without a service worker, and gives you rollback and hard
refresh. They work fine together.