Release safely. Measure honestly. Roll out gradually, keep a holdout, switch off anytime.

Built in the SDK alpha: rollout, holdout, kill switch. Hosted configuration is planned.

rollout={{ percentage: 25, holdout: 0.1, salt: "pilot-1" }}

Hover a person to see their group

adapted
15
holdout
13
off
72

Raise the percentage: people only ever join.

The SDK's own cohort function, running in your browser on 100 example user IDs.

Signed configuration

A remote off switch nobody can forge. Flags only: it switches reviewed behaviour off or on, and never adds any.

An Ed25519-signed envelope of key_id, payload and signature. OwnUX checks it against public keys pinned in your build before reading anything, and ignores whatever it cannot trust.

  • Ignored: altered, expired, not yet valid, unknown key, or stale.
  • Stored file applies before render; slow servers never delay it.

config.signed.json

Waiting for a file. Serial 1792729800 is in force, with nothing switched off.

  1. Envelope is { key_id, payload, signature }
  2. key_id is pinned in your build
  3. Ed25519 signature matches the payload
  4. Payload is a valid ownux-config/1
  5. Issued already, not expired
  6. Serial not older than the one in force

When something breaks, changes stop. Reads continue. A matching kill entry blocks changes; surfaces hand back to your own view.

  1. 1

    Sign a flags file

    In development, ownux config sign with --kill <id>. Production files use an offline key, never these commands.

  2. 2

    OwnUX picks it up

    Your load function fetches it on start, on focus and every refresh_ms (5 minutes by default).

  3. 3

    Changes stop, reads continue

    Surfaces hand over through onFallback("kill_switch", notifyKey). Tell the person once per notifyKey, not on every render.

  4. 4

    It stays off

    It never lapses by itself: expiry and restarts don't end it; only a newer valid file does.

Terminal, development signing
$ pnpm ownux config keygen ~/.ownux/dev-key.json --id dev-test
$ pnpm ownux config sign flags.json --key ~/.ownux/dev-key.json --kill reference-app
✓ Signed serial 1792729800 with dev-test: switches off reference-app; valid until 2026-11-22T04:30:00Z.

keygen refuses a path inside your project and never overwrites a file. Serials come from the clock, so newer files win.

App.tsx
<OwnUXProvider
user={{ id: currentUser.id }}
rollout={{ percentage: 25, holdout: 0.1, salt: "pilot-1", templates: "1" }}
config={{ keys: PINNED_KEYS, load: () => fetch(CONFIG_URL).then((r) => r.text()) }}
onFallback={(reason, notifyKey) => showClassicView(reason, notifyKey)}
/* … */
/>

Without config, nothing is ever switched off remotely. Only files signed by a key pinned in your build are accepted.

Staged rollout, without surprises. Cohorts come from a fixed hash of salt and ID, so people stay put.

Holdout first. Taken first, the holdout never sees OwnUX, not even the switch.

Raising only adds. Of everyone else, percentage get OwnUX. Raising it never removes anyone.

A new salt reshuffles. Change the salt for fresh groups. Keep it to keep everyone in place.

Pinned layouts. templates pins the template version. SDK upgrades never move controls until you promote the new one.

Off in your own code. killed: true turns OwnUX off for everyone from your code, with no signed file needed.

No user, no OwnUX. Without a signed-in user, no stable group is possible, so nobody gets OwnUX.

Measure against people who didn't get it. How a pilot is meant to be measured, and what exists today.

  1. 1

    Agree the metric first. Each design-partner pilot fixes one primary metric in advance, such as 7-day activation, and a minimum detectable effect.

  2. 2

    Hold some people out. The SDK assigns a holdout, who keep your product as it is: by default 15% of new users, and you can change it.

  3. 3

    Report the whole result. Planned: a readout with confidence intervals, never with selected anecdotes.

  4. 4

    Count people, not page views. One adapted user per person, product and UTC month, with the person's ID hashed with your product's ID (SHA-256).

In developmentCohorts, holdout and template pinning, built in the SDK alpha and running in the Reference App.

In developmentSigned configuration and the kill switch, built in the runtime and the SDK.

In developmentLocal metering. Signals stay in the browser and are sent nowhere during the alpha.

PlannedHosted configuration and production signing keys. Not set up yet.

PlannedHoldout measurement with design partners, and a measurement dashboard in the developer portal.

PlannedRollout and holdout controls in the portal. Until then, you set them in your own code.

Rollout questions, answered.

You do, in your own code, with the rollout prop. Until the developer portal exists, there is no Satin dashboard between you and your users.

Run your first holdout with us. 12-week pilots with a holdout and an agreed metric.