Skip to content

Repository files navigation

Ripstop

Ripstop Web SDK

CILicense: MIT

Remote config, update walls and maintenance mode for web apps. Signed at the edge, verified in the browser, 6.5 KB gzipped.

Installation

@ripstop/web is not on npm yet. Until it is, install straight from GitHub. The package builds itself on install:

npm install github:ripstop-dev/ripstop-web

Once it is published this becomes the npm install @ripstop/web you expected.

Quick start

import{Ripstop}from'@ripstop/web';constripstop=awaitRipstop.init({apiKey: 'rs_pub_your_key',appVersion: __APP_VERSION__,// whatever your build injects});constdecision=awaitripstop.check();switch(decision.type){case'maintenance':
// You are down on purpose. decision.endsAt is display-only.break;case'force':
// This tab is too old to keep talking to your API.location.reload();break;case'soft':
// A nudge. ripstop.snooze() records it and re-evaluates.break;case'none':
// Carry on.break;}

appVersion is explicit here and always will be. The Swift and Flutter SDKs read the installed version from the bundle, but a browser has no bundle and no installed version to read. The only thing that knows which build is running is the build itself, so it has to hand the string over. Every bundler can: Vite's define, webpack's DefinePlugin, or an env var read at build time.

Pass a semantic version, not a commit SHA. The rules are ordered as semantic versions, and anything that is not one is treated as no opinion, which lets the app run rather than walling it, but also means your rules do nothing.

What force means on the web

On mobile it means "this binary is stale, go to the store". On the web there is no store: a reload gets the latest code. So force here almost always means a tab that has been open since before you shipped a breaking change, and the right response is usually location.reload().

That is also why the web SDK ships no prebuilt walls. On mobile, a full-screen update wall is the same shape in every app. On the web it is a banner, a modal, or a route, entirely yours. You get the decision and the copy you wrote in the panel; the markup is your business.

Remote config

Values ride in the same signed payload as the rules, so reading one costs no extra request and cannot be out of step with them.

constcheckout=ripstop.value('checkout_enabled',true);constlimit=ripstop.value('upload_limit',10);

Always pass a fallback. On a first load with no network there is no payload yet. That is the fail-open path working as intended.

API

Default
apiKeyrequiredYour app's public SDK key. Safe to ship
appVersionrequiredThe build you are running; rules evaluate against it
localeenWhich wall copy to resolve; falls back to en per key
minFetchInterval6 hoursHow long a payload is fresh enough to skip the network
timeoutMs5000Fetch budget. After that, cache
storagelocalStorageSwap for MemoryStorage, sessionStorage, your own
signingKeyspinnedOverride for self-hosted deployments and tests
fetchImplglobalInject your own for tests or a proxy

What it does when things break

SituationWhat your app does
Network unavailableUses the last signed payload from localStorage
No network, no cachenone, so your app runs unrestricted
Edge returns 5xx, or times outCache, then normal
Signature doesn't verifyDiscarded. A forged payload can never wall your app
localStorage edited in devtoolsRe-verified on read, so it grants nothing
Wall up, then network lostThe wall stays, until a fresh signed payload clears it

The cache is re-verified every time it is read. This matters more on the web than anywhere else: localStorage is two keystrokes away in devtools, so a cache that were trusted would make every wall a polite request.

SSR

Ripstop.init runs anywhere fetch exists. Without localStorage it falls back to in-memory storage automatically, so a server render gets a decision and simply doesn't persist a cache.

Conformance

Every Ripstop SDK runs the same vectors.json: version ordering, evaluation order, message fallback, snooze accounting, the fail-open state machine. npm test runs it here. If this package and the reference implementation ever disagree about a single comparison, CI goes red.

Full docs: ripstop.dev/docs/web

License

MIT

About

Ripstop SDK for the web. Required updates, update nudges, maintenance mode and remote config.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages