Adaptive surfaces, built from your own components. Every person uses your product their way.

In development · SDK alpha in our Reference App · Not yet published

import { defineManifest } from "@satin/ownux/integration";
export const manifest = defineManifest({
app: { id: "acme-projects", version: "1.4.0" },
actions: {
"task.list_assigned": READ,
"task.search": READ,
"task.get": READ,
"task.set_due_date": {
...WRITE, input: { task_ref: "task.ref", due_date: "date" },
},
},
locked: ["billing", "security", "consent"],
});

Step 1 of 3 · Declare what OwnUX may offer

L1 Declared

Every action has a risk, preconditions, a verify step and a fallback.

locked: billinglocked: securitylocked: consent

Surfaces stay off unless L1 passes.

A small API, React first. Declare actions, connect your API, render surfaces. These are the alpha’s exports.

App.tsx
import { OwnUXProvider, OwnUXSettings, OwnUXSurface } from "@satin/ownux";
<OwnUXProvider
manifest={manifest}
binding={binding}
components={myComponents}
tokens={tokens}
appName="Acme Projects"
user={{ id: currentUser.id }}
rollout={{ percentage: 25, holdout: 0.1, salt: "pilot-1", templates: "1" }}
onNativeView={() => showClassicView()}
onFallback={(reason, notifyKey) => showClassicView(reason, notifyKey)}
onAudit={(event) => auditLog.write(event)}
>
<OwnUXSurface template="task_list" />
<OwnUXSettings />
</OwnUXProvider>;
tokens.ts
// Map OwnUX's spacing and text size to your design tokens
const tokens = {
space: { sm: "6px", md: "10px", lg: "16px" },
font_size: "15px",
};
defineManifest({ app, actions, locked })
Declares each action’s input, risk, preconditions, verify step and fallback, plus locked screens. Surfaces stay off until L1 passes.
createIntegration(manifest, handlers)
Maps your list, search, get, execute and options calls to OwnUX. Only declared capabilities are offered.
<OwnUXProvider>
Runs the Action Broker in your page, loads and saves Interface DNA, health-checks your integration and adds the coexistence marker.
<OwnUXSurface template>
One surface: task_list, task_finder, task_detail or quick_actions. Locked screens never render.
useOwnUX()
ready, active, cohort, dna, preferences, update, reset, exportProfile and importProfile, for your own UI.
<OwnUXSettings />
In-product settings in plain words: spacing, layout, words, start with, input, contrast, text size and less motion.

Code outside the page, such as ownux.config.ts, imports defineManifest and createIntegration from @satin/ownux/integration (no React).

Your components

Compose surfaces from your own design system. OwnUX decides what goes where. Your components decide how it looks.

Every surface is built from 17 primitives, such as Row, Section and DatePicker. Register one of your components for each and map your tokens. Start from plainRegistry.

  • Deterministic: the same input always gives the same surface.
  • Surfaces are validated: no control without a capability behind it.
  • Content renders as text, never HTML: markup creates no element.

components={plainRegistry}

17 primitives · hover one to find it

task_list

Back to the classic view
Find a task

Today

• Fix login redirect
In progress
Today
• Review onboarding copy
In progress
Today

Blocked

Nothing is blocked.

task_detail

Fix login redirect

In progress ▾
25 Oct
Asha ▾
Due today
Done. Due date 25 Oct in Acme Projects.

Verified writes

Nothing is done until your product says so. Every change goes through the Action Broker.

Changes are prepared, confirmed if your policy asks, then committed. The broker calls your execute handler once, then reads the task back to check the new value.

  • More than one match asks “which task?” and changes nothing.
  • A task edited by someone else is never overwritten.
  • A repeated commit returns the first result: no double writes.
  • One content-free audit event per change goes to your log.

The person sets HQ-7 · Fix login redirect due on 25 Oct. Pick what happens next.

  1. prepare

    Finds HQ-7 and checks the change is allowed. Writes nothing.

  2. commit

    Re-reads HQ-7. Version 14, unchanged since the person saw it.

  3. execute

    Calls your API once, with an idempotency key.

  4. read back

    Your product shows due date 25 Oct.

Signed configuration

Kill switch: can’t be forged, replayed or forgotten. Switch changes off remotely, from a file only you can sign.

Pin your public keys and supply a function that fetches the signed file. If it switches you off, changes stop and surfaces hand back to your view. Reading keeps working.

  • Tampered, expired, older or unknown-key files are ignored.
  • Last valid file kept in browser storage, applied before render.
  • An accepted kill switch stays on past expiry until replaced.

Signed configuration

in force: serial 1792713600

Serve a file

{ "key_id": "acme-2026-a",
  "payload": "eyJmb3JtYXQiOiJvd251eC1jo…",
  "signature": "kq9X2vR7…" }
// payload: serial 1792713600, kill: []

Pick a file. Only a valid, newer file signed with a pinned key can change anything.

What the person sees Changes on

Classic viewOwnUX surface, with changes allowed

Built so AI coding tools can integrate it. The SDK ships all an assistant needs to integrate and test; you review.

A guide written for AI tools. AGENTS.md ships in the package, indexed by llms.txt: rules, the Task contract, errors and checks.

Starter files with the steps marked. ownux init writes a manifest that passes L1, and an integration with only TODO(ownux) steps left.

A fix on every failed check. Every failure prints Fix: with exactly what to change, so the loop converges.

A clear finish line. Done means Publishable: yes. Fresh AI agents have integrated unfamiliar products from the package alone.

Terminal
$ npx ownux init src/ownux --app acme-projects
✓ Wrote 5 starter files to src/ownux: AGENTS.md, manifest.ts, integration.ts, ownux.config.ts, view.tsx
$ npx ownux test --config src/ownux/ownux.config.ts
OwnUX conformance v0: acme-projects 1.4.0
✓ L1 Declared
✗ L2 Verified (test task HQ-7, as an editor and a viewer, reset 10 times)
✗ task.get: for a missing task, get threw Request failed, not ProviderError("not_found")
Fix: For a task that does not exist, or that this person cannot see, get must
throw new ProviderError("not_found", message). Map your API's not-found error to it.
Not publishable: 1 check failed (marked ✗ above).

Ask your assistant to follow src/ownux/AGENTS.md until the check passes. Review the result, especially that your API decides who may edit.

Small by design, honest about its status. The build enforces a size budget. A short list shows what the alpha lacks.

SDK size, gzipped, without React
52.8 KBSDK size, gzipped, without ReactMeasured at the alpha
Size budget for the SDK
60 KBSize budget for the SDKA budget: the size check fails the build above it
primitives you map to your components
17primitives you map to your componentsFrom Surface and Row to Notice
task capabilities in the alpha
6task capabilities in the alphaList, search, read; due date, status, assignee

In developmentThe OwnUX SDK is in development. An alpha runs in the Reference App with rollout and holdout, template pinning and local metering.

  • React only. Other frameworks come later.
  • Tasks only: the six capabilities above.
  • Nothing is sent to a server yet.
  • The package isn’t published yet.

Not yet. Every package is private while the SDK is in development. The alpha runs in our Reference App, a small task tracker built on the SDK. Design partners get access first.

Build OwnUX into your product. Design partners get it first, with help reaching L2 Verified.