@authoxi/js
v0.1.3
Published
authoxi for JavaScript — the browser SDK loader, and an offline verifier for signed AI-agent authorization decisions (no account, no network call).
Maintainers
Readme
@authoxi/js
JavaScript for authoxi — the control plane that governs what an AI agent is allowed to do. Two things live here, and they don't overlap:
| Import | What it does |
|---|---|
| @authoxi/js | Browser SDK loader — human sign-in / sign-up in a web app. |
| @authoxi/js/verify | Verify a signed agent decision — offline, no account, no network call. |
| authoxi-verify (bin) | The same verification, from a terminal or CI. |
npm install @authoxi/jsVerify that a human approved an agent's action
Your AI agent did something irreversible — issued a refund, paid an invoice, merged a branch. A human approved it. Later, someone who does not trust you asks you to prove that.
A database row can't answer: it's mutable by everyone with write access, including the person you're trying to hold accountable. A signature can.
import { verifyEvent, verifyStream } from '@authoxi/js/verify';
const { ok, verified, checks } = await verifyEvent(event);
// ok — every signature holds; not one byte has changed since it was signed
// verified — a specific human signed this exact action (derived from the signature,
// never read off the event's own `verified` flag, which is just a claim)
const stream = await verifyStream(events); // signatures AND `seq` continuityThe public key is carried inside the event's own did:key, so this needs nothing but the event
you hand it. No registry. No API key. No call home. It works if authoxi is offline, acquired, or
dead — which is the point: an audit record that needs the vendor alive to validate is not an audit
record, it's a receipt.
Isomorphic — the same module runs in a browser on WebCrypto with nothing uploaded. That's what docs.authoxi.com/verify does: it hands you a real signed decision, verifies it in your browser, and then lets you edit it and watch the signatures fail.
From a terminal, or in CI
npx -p @authoxi/js authoxi-verify decision.jsonPASS evt_01JZQK8F3M4N5P6Q7R8S9T0V1W approve payment 9000.00 USD
✓ emitter signature: valid — signed by did:key:z6MkrEET…
✓ approver signature: valid — a human signed this: did:key:z6MkehRg…Exit 0 if every signature holds, 1 if any doesn't — so it fails a build on a broken audit trail:
- run: npx -p @authoxi/js authoxi-verify audit/decisions.jsonlWhat a PASS proves — and what it doesn't
Proves: the event was signed by the holder of the key inside its own did:key and hasn't been
altered since; and if an approver signature is present, a specific human approved this exact action
and cannot later deny it.
Does not prove:
- That the key belongs to who it claims. A signature binds an action to a key. Binding a key to a human is your identity process, not this library's job.
- That no event was suppressed. A signature proves an event is unaltered; it says nothing about
one you never received. That's what
seqis for —verifyStreamreports holes. Deletion, not forgery, was always the easier attack on an audit trail. - That the action executed. This records an authorization, not an effect.
We would rather you knew the limits than trusted the output.
Wire format: the loss-event/v2 open spec (CC BY 4.0).
Implement your own verifier — if it disagrees with ours, one of us has a bug and we'd like to know
which.
Browser SDK
import { loadAuthoxi } from '@authoxi/js';
const authoxi = await loadAuthoxi({ publishableKey: 'pk_live_...' });
document.querySelector('#signin').onclick = () =>
authoxi.signIn({ onAuth: ({ user, tokens }) => { /* ... */ } });Prefer a plain <script> tag if you're not using a bundler:
<script src="https://assets.authoxi.com/v0/authoxi.js"></script>
<script>
const authoxi = Authoxi.init({ publishableKey: 'pk_live_...' });
</script>Why this is a loader and not a bundle
The SDK half of this package is deliberately tiny — it does not contain authoxi.js. It injects
the <script> from https://assets.authoxi.com/v0/authoxi.js at runtime. That's the same choice
@stripe/stripe-js and react-plaid-link make: for an auth SDK, the hosted copy stays the single
source of truth, so a security fix reaches every embedder at once and nobody ships a stale bundled
copy of your login form. The SDK is served from assets.authoxi.com but calls the API at
api.authoxi.com — two separate hosts by design.
(The verifier is the opposite: it is the code, with zero dependencies and no network access, because verification you have to phone home for isn't verification.)
Content-Security-Policy
If your site sets a CSP, allowlist:
script-src https://assets.authoxi.com;
connect-src https://api.authoxi.com;
frame-src https://assets.authoxi.com; # only if you embed the widget iframesLicense
MIT — see LICENSE. The loss-event/v2 format is CC BY 4.0: free to implement, no
permission needed.
