axis-platform-sdk
v0.5.0
Published
Platform-side SDK for the AXIS protocol. The verifier side: when an AXIS agent shows up at your platform, verify its identity + delegation + scope and decide whether to accept, scope, or boot it. Zero runtime dependencies; runs in Node 20+, Cloudflare Wor
Maintainers
Readme
axis-platform-sdk
Let verified agents into your platform. Boot the bad ones. Free, drop-in, no account required.
AI agents are starting to show up at your platform — to post, to buy, to call your API on a human's behalf. An API key can't tell you which human is behind this agent, whether they're allowed to do this, or let you revoke one bad agent without nuking the key everyone shares.
axis-platform-sdk is the verifier at your door. When an AXIS agent shows up
and presents a token, this verifies — cryptographically, against a public
registry — who it is, who's accountable for it, and exactly what it's been
authorized to do, then lets it through or bounces it. A few lines of code.
On a Node/Express server:
import { axisGate } from 'axis-platform-sdk/express';
app.post('/comments',
axisGate({ audience: 'comments.example.com', requireScopes: ['content:comment'] }),
(req, res) => res.json({ ok: true, by: req.axis.agent_id })); // verified agent; proceedOn a Cloudflare Worker (or any fetch handler):
import { axisGate, denialResponse } from 'axis-platform-sdk';
const gate = axisGate({ audience: 'comments.example.com', requireScopes: ['content:comment'] });
export default {
async fetch(request) {
const verdict = await gate(request);
if (!verdict.accepted) return denialResponse(verdict); // 401/403 + a real reason
return Response.json({ ok: true, by: verdict.agent_id }); // verified agent; proceed
},
};That's the whole integration. No SDK account, no API key from us, no infra to run.
Why it's free, and what "no account required" means
The hard part — checking the signature, checking revocation, walking the
delegation chain to compute what the agent is actually allowed to do — is done
server-side by the public AXIS registry (registry.axisprime.ai). This SDK
is the thin, zero-dependency client that calls it and applies your policy. Your
platform makes one outbound HTTPS call and gets back a trustworthy verdict.
- Zero dependencies. Runs on Node 20+, Cloudflare Workers, and modern browsers.
- Nothing to host. No database, no key management, no service to deploy.
- No relationship with us. You verify against the public registry directly — no signup, no key. The only thing that reaches us is the verification call your server makes (the agent's token); we never see your content or your users.
- Apache-2.0. Use it, fork it, ship it.
Self-host is the whole product today. A cloud-hosted version — a hosted console, an aggregated arrival history across your platforms, and a richer policy engine — is in alpha testing, planned for release in Q3 2026, for teams who'd rather not run it themselves. You will never need it to run the self-host path. See Self-host today / cloud-hosted in alpha.
The registry dependency, and what happens if it's down
This is the one thing to understand before you deploy.
What the SDK depends on. Verification is not done locally. Every arrival makes
one outbound HTTPS call to the public AXIS registry at
https://registry.axisprime.ai — a GET /verify?token=…. The registry does
the cryptographic work: signature check, revocation check, and the delegation-chain
walk that produces the trustworthy effective_scope. The SDK is a thin client
around that call; it cannot verify an agent offline. (If you also gate on
minTier, a second call — GET /agents/{id} — fetches the operator's verification
tier.) This is a hard network dependency: no reachable registry, no verdict.
What happens if the registry is unreachable — the SDK fails closed. If the
registry can't be reached (DNS failure, network error, timeout) or returns a
non-2xx, verifyAgent returns a denial, never an acceptance:
{ accepted: false, code: 'registry_error', reason: 'Registry unreachable' } // network failure
{ accepted: false, code: 'verify_failed', reason: '…' } // non-200 from the registryThere is no fail-open path and no cached "last-known-good" fallback — an agent
that cannot be verified is not let in. In gate form this surfaces as a 503
(axisGate / denialResponse map registry_error to 503). The practical
consequence: registry availability gates your agent-accept path. If the
registry is down, verified agents are turned away with a retryable 503 — but your
platform stays up for human traffic, and no unverified agent ever slips
through. Design your denial UX for it (retry/backoff for agents, or a short
"try again" for agent traffic).
Two checks are decided locally, before any network call, so they still work
with the registry down: a missing token (no_token → 401) and an audience
mismatch (audience_mismatch / missing_aud → 403). A tokenless or
wrong-audience request is bounced regardless of registry health.
Hosted vs. self-hosted verification. The verify call always goes to a registry:
- Default (hosted, canonical). The public AXIS registry at
registry.axisprime.ai— the shared identity source. No account, nothing to run, no key from us; the only thing that reaches it is the token + your audience on each verification (the way a CA or DNS resolver sees lookups). This is the path 99% of platforms use. - Self-hosted / private registry. If you run your own AXIS registry instance,
point the SDK at it with the
registryBaseUrloption (per call, or on the gate; the starters expose it) — no code change, same/verifyand/agents/{id}contract. Only the base URL differs.
Note the distinct axis: the SDK is always self-hosted (you run the verifier in your own backend). The optional cloud console described in Self-host today / cloud-hosted in alpha is about where your arrivals/blocklist state lives — not the identity dependency. Identity verification against a registry is required on both the free self-host path and the future cloud path; the registry is the one shared dependency either way.
Gate your platform in 10 minutes
There's a step-by-step guide in QUICKSTART.md, and two complete, runnable drop-in starters — copy the one that matches your stack:
| Your stack | Starter | What it is |
| --- | --- | --- |
| Node / Express (or any Node HTTP server) | templates/node-express/ | The axisGate(...) middleware from axis-platform-sdk/express (one import, one line) + a full worked server (door policy, arrivals ledger, boot console). npm install && npm start. |
| Cloudflare Workers | templates/cloudflare-worker/ | The same, as a deployable Worker. npx wrangler dev. |
Both wrap the identical engine. You do not need to adopt Cloudflare to use AXIS — the Worker template is just one runtime we provide a starter for. If you run Node, Python, Go, or anything else, the integration is the same shape: pull the token off the request, call the verifier, act on the verdict.
The Express starter ships a smoke test you can run right now to watch the gate
turn away an unidentified agent:
$ cd templates/node-express && npm install && npm run smoke
PASS — no AIT -> 401 no_token
PASS — invalid AIT -> 403 denied
All checks passed.What a verdict gives you
verifyAgent(token, opts) (and the axisGate / middleware that wrap it) return a
single structured verdict:
// accepted:
{ accepted: true, agent_id, operator_id, effective_scope, delegation_valid, tier, expires_at }
// or denied:
{ accepted: false, code, reason, ... } // code is stable: no_token | audience_mismatch |
// agent_revoked | insufficient_scope | insufficient_tier | ...You decide the policy; the SDK enforces it:
audience— the AIT'saudmust equal you, so an agent's token for some other site can't be replayed at yours.requireScopes— checked against the trustworthyeffective_scope(the registry's chain-walked result), never the token's self-declared scope.minTier— requireemail/domain/kyc/kyb-level operator verification.blockedOperators/approvedOperators— deny-list or allow-list by operator.
And the stateful half a real verifier needs (all zero-infra by default):
- Access ledger — log every arrival, accepted or denied: "who's been using my platform."
- Runtime blocklist — boot one agent, or a whole operator, without a deploy.
- Reputation report-back — when you boot a bad actor, optionally sign a Trust Attestation and emit it onward (off by default).
Self-host today / cloud-hosted in alpha
Today this SDK is the product: you run it in your own backend, free. A cloud-hosted version is in alpha testing (planned for release in Q3 2026) for teams who'd rather not host the console and the state themselves.
| | This SDK (free, self-hosted) — available now | Cloud-hosted — alpha, Q3 2026 |
| --- | --- | --- |
| Identity verification | ✅ full (against the public registry) | same engine |
| Scope / tier / operator policy | ✅ SwitchAuthorizer (on/off gates) | + granular relationship/attribute rules |
| Arrivals + blocklist | ✅ your store (in-memory default; D1/SQLite/Postgres adapter) | hosted, aggregated, multi-tenant |
| Admin console | ✅ a reference HTML page you own | a hosted console |
| Cost / setup | free, nothing to run | a hosted product (alpha) |
The SDK is designed as the port; the cloud-hosted version is built as an adapter over the same engine, with byte-compatible arrival/block record shapes. That's a deliberate design choice so that when the cloud version ships, moving to it is a lift, not a rewrite — and you are never forced up.
Switching is a config flip, not a migration. Set AXIS_CLOUD_URL and the
reference verifier mirrors each arrival to your hosted cloud console
(fire-and-forget, best-effort — it never affects your local decision). Unset (the
default) keeps everything self-hosted; nothing leaves your box.
Product news, "update available" nudges, and security advisories show in the admin
console via a notices feed (AXIS_NOTICES_URL) — including the cloud waitlist while
it's open. You broadcast to every self-hosted deployment by editing one public JSON file
(see templates/node-express/notices.example.json)
— no redeploy, and the waitlist notice simply changes or disappears when cloud launches.
Telemetry & privacy
The reference verifier makes a small, fixed set of outbound calls, and none of them send your content, your users, or your arrival/decision records — those live only in your store, on your infrastructure:
- Identity verification to the public registry (
registry.axisprime.ai) on each arrival — inherent to verifying against a public registry (the operator sees the token + your audience + a timestamp, the way a CA or DNS resolver sees lookups). - A notices feed (
AXIS_NOTICES_URL, a public JSON) fetched for the admin console — product news, "update available" nudges, and security advisories. It's a one-way read of a public file (like an update check); nothing about your deployment is sent. Set it to''to disable. - Optional cloud mirror — only if you set
AXIS_CLOUD_URL, the arrivals you choose to mirror go to your hosted console. Off by default.
There is no separate analytics.
Adoption tiers: start small, add as you need
You don't have to do everything at once. Most platforms start at A and stop there; regulated or higher-stakes platforms add B and C.
| Tier | What you do | What it takes |
| --- | --- | --- |
| A — Identity acceptance | Accept any AXIS-verified agent the way you accept a signed-in human. Pass/fail at request time. | verifyAgent(token, { audience }) — that's it. |
| B — Access policy | Also publish your requirements at /.well-known/axis-access, so agents and operators can check before they even call you. | A small JSON doc (the starters serve it). |
| C — Scope + tier enforcement | Additionally require specific permissions and a minimum verification level. | Add requireScopes / minTier (and blockedOperators) to the gate. |
All three are the same verifyAgent call with more of its options set — moving up
a tier is adding arguments, not re-architecting. (For an upstream you can't put the
SDK inside — a legacy service, a non-Node runtime — the separate
axis-gateway reverse proxy
enforces Tier C in front of it.)
Trust model (read this)
effective_scopeis the only trustworthy scope. It's the registry's server-side chain-walk result, returned only when a valid delegation is presented. The AIT's self-declaredscopeis not trusted and is never used forrequireScopes.- A direct AIT with no valid delegation has no proven scope. Any non-empty
requireScopeswill deny it. That's intentional. - Audience matching is the platform's job. The registry guarantees
audexists; you guarantee it equals you. (The starters do this for you.)
Forward compatibility (gating signals coming later)
Today you can gate on scope, operator verification tier, and operator allow/block lists. Richer provenance signals — operator account age, signup method, prior abuse flags — are defined in the protocol but not yet exposed by the registry, so they aren't available to gate on right now.
When they ship, they arrive additively and backward-compatibly:
- The verdict object only gains fields; it never changes existing ones. Your code keeps working untouched.
- Provenance gating will be new optional
verifyAgentoptions (e.g. a minimum account age), exactly likeminTieris today. Unknown options are ignored, so an older integration is never broken by a newer registry. - You opt in when you want it: bump the SDK and set the new option. Platforms that don't care do nothing and are unaffected.
So adding provenance later requires a registry update (to populate and expose the fields) and an SDK minor (to read them) — but no breaking change and no forced migration for platforms already running. Build to Tier A/B/C now; the provenance knobs slot into the same gate when they land.
Web Bot Auth: verifying which bot, not what it may do
Web Bot Auth is an IETF-draft standard for proving which bot is making an HTTP
request: the caller signs it with an Ed25519 key using HTTP Message Signatures
(RFC 9421) and names its key directory in a Signature-Agent header. Edge
providers verify this at their own front door — which helps only sites behind
that edge. If you are not behind one, you have no verification story at all.
This SDK is that story:
import { verifyWebBotAuth } from 'axis-platform-sdk/webbotauth';
const v = await verifyWebBotAuth(request);
if (v.accepted) {
// The request really came from the holder of the key published at
// v.signature_agent. Log it, rate-limit it, serve it differently.
}What it proves, and what it does not. A valid signature identifies the
software vendor — "this really is that crawler." It says nothing about who
the agent is acting for, what it may do, or whether that permission can
be withdrawn. Those are verifyAgent's job:
| Question | Answered by |
|---|---|
| Is this really who it claims to be? | verifyWebBotAuth |
| May this party do this thing, and can I revoke it? | verifyAgent |
Neither substitutes for the other. Use webBotAuthGate to combine them:
import { webBotAuthGate } from 'axis-platform-sdk/webbotauth';
import { verifyAgent } from 'axis-platform-sdk';
const v = await webBotAuthGate(request, {
mode: 'either', // 'either' | 'axis' | 'both'
verifyAxis: (req) => verifyAgent(extractToken(req), { audience: 'my-platform' }),
});either(default) — a valid signature or a valid AIT admits. Widest door: admit well-known crawlers alongside AXIS agents.axis— AXIS only. A signature is still recorded as evidence, but never sufficient, because it carries no authority.both— require both. The right default for anything that writes.
Hardening you get for free
keyidis checked against the JWK thumbprint of the published key, so a directory cannot name one key and publish another.- A present
Signature-Agentmust itself be covered by the signature — an intermediary cannot rewrite it to redirect key discovery. - Both
Signature-Agentforms are understood — the bare String ("https://signer.example") and the Dictionary (agent2="https://signer.example"). A member declaringtype=cimdpoints at an agent card rather than a key directory, and is refused rather than fetched and misread. allowedAgents/directoryUrlpin who you will fetch keys from.expiresis required by default;maxAgeSecondscaps replay of stale signatures.- A directory outage is never a credential failure. Unreachable, timed out,
or non-2xx deny as
wba_directory_errorwithretryable: true— the same posture this SDK takes toward registry outages.
Tested against someone else's implementation
Signing and verifying with your own key proves self-consistency, not interoperability: a signature base that is wrong in a way your signer shares round-trips perfectly. So the suite also replays the Web Bot Auth test vectors published by the Cloudflare Research reference implementation, which fail if this SDK's reading of RFC 9421 diverges from anyone else's. They did fail, twice, before the reading was corrected.
npm run interop:livereplays the same vectors against real key directories on the public internet and
checks that each published kid really is the RFC 8037 thumbprint this SDK
computes — a mismatch there would make every signature from that signer fail.
It stays out of npm test because someone else's outage is not a defect here.
Only Ed25519 is verified. Web Bot Auth inherits the IANA HTTP Message Signature
algorithm registry and the drafts permit more, but Ed25519 is what production
signers publish, and narrowing to it keeps this dependency-free on WebCrypto.
Anything else is refused as wba_unsupported_alg rather than ignored.
Where it sits
agent (axis-protocol-sdk) ──presents AIT──> YOUR PLATFORM (axis-platform-sdk)
│
└── GET /verify ──> registry (does the crypto)The axis-protocol-sdk
and the AXIS Prime MCP are what an
agent uses to get and present an identity. This SDK is the other end of the
wire — the inbound identity gate. It is distinct from the operator-side
outbound gateway, and from generic AI gateways (TrueFoundry, Portkey, …), which
govern an operator's outbound LLM calls. This is the inbound
verifier.
A worked integration is documented in CASE-STUDY.md (gating comments on a news site). That's a case study — an example of using the tool, not the product itself.
API reference
verifyAgent(token, opts)— the core. Verifies against the registry and applies your policy. Returns a structured verdict.audience— your platform id. The AIT'saudmust equal it. (Matched locally: the registry only checks thataudis non-empty, not that it equals you. That check is yours.)requireScopes— checked against the trustworthyeffective_scope.minTier—email | domain | kyc | kyb(legacy alias values are accepted as input and mapped in; the SDK only ever emits canonical values).blockedOperators/approvedOperators— deny/allow lists by operator id.gateId— the gate/action id this verification is for. Carried on the verdict asgate_idand named asactionin the §7.1 deny body.registrationUrl— your advertised §7registration_url. When set, verification-remediable denials (no credential, tier too low) carry it as the §7.1verify_url, and tier denials also carryrequired_tier(the canonical target tier for the refused action) — the refused party learns the fix with zero inference.timeoutMs— registry-call timeout (default 5000 ms;0disables). A hanging, unreachable, or 5xx registry always denies asregistry_error(503, retryable) — an outage is never reported as a bad credential.registryBaseUrl— defaults tohttps://registry.axisprime.ai.
axisGate(opts)— returns(request) => Promise<verdict>; binds your policy to a request gate for Workers and anyfetch-styleRequest. Pulls the AIT fromAuthorization: Bearer <ait>,X-AXIS-Token, or?ait=. (aitGateis a deprecated alias, kept for one release.)axisGate(opts)(subpathaxis-platform-sdk/express) — the same as Express/Connect middleware:(req, res, next). On accept it setsreq.axisand callsnext(); on deny it responds with the protocol §7.1 deny body (axis_error- granular
code,missing_scopeson scope denials) —401+WWW-Authenticate: AXISfor credential failures,403for scope/verification/wrong-door,503for registry-unreachable. Imports nothing from Express (zero-dep), so it also works on Connect and barehttp.
- granular
denialResponse(verdict)— turns a denied verdict into a protocol §7.1Response(401/403/503; 401s carry theWWW-Authenticate: AXISchallenge).denialInfo(verdict)— the resolved{ status, body, headers }behinddenialResponse, if you render the deny yourself.scopeCovers(granted, required)/coversAll(granted, required[])— the AXIS scope matcher (ported verbatim from the operator-side gateway's, so operator and platform sides agree).enrich(agentId, token, opts)— fetch the agent's presentation layer (display name, tier) for a console UI.buildAccessDocument(policy, opts)/buildActionsManifest(policy, opts)— generate the/.well-known/axis-accessdocument (including the §7.2 per-actionactions[]manifest) FROM the enforcedSwitchAuthorizerpolicy, so the published document and the gate can never drift. Enforced fields (id,required_scopes,min_tier) come from the policy; advisory fields (method,path,description,auth,request,responses) come from the caller'sdetailsmap. Gates withenabled: falseare omitted; enforced operator allow/block lists are not republished.loadAccessPolicy(platformBaseUrl)— read a platform's published/.well-known/axis-accessdoor policy.decodeAitPayload(token)— read the AIT payload (claims) without verifying. For theaudcheck; never trust it for authorization.AccessLedger/MemoryLedgerStore/loggedGate(gate, ledger, opts)/recordEntry(verdict, opts)— the access ledger (who showed up).loggedGatewraps a gate so every verdict is logged.Blocklist/MemoryBlocklistStore/gatedWithBlocklist(gate, blocklist)— the runtime block list (by operator and by agent).blockOperator/blockAgent/unblock*/isAgentBlocked/blockedOperatorIds/checkVerdict.reportFlag(args, opts)/blockAndReport(blocklist, args, opts)— sign a negative Trust Attestation and send it to a reputation index (OFF by default).getPlatformKey(opts)/buildAttestation/signAttestation/verifyAttestation/MemoryKeyStore— the platform's Ed25519 key + TA build/sign/verify primitives (WebCrypto, zero-dep).
Gates as policy: SwitchAuthorizer (the free-tier engine)
Identity verification is fixed and core. The authorization decision is a
pluggable layer — the Authorizer port. SwitchAuthorizer is the free-tier
implementation: config-driven on/off gates. Its policy object is exactly what a
console's "door policy" screen edits and saves.
import { SwitchAuthorizer, denialResponse, buildAccessDocument } from 'axis-platform-sdk';
const door = new SwitchAuthorizer({
audience: 'comments.example.com',
defaultAllow: false,
registrationUrl: 'https://signup.axisprime.ai/signup', // §7 registration_url → §7.1 verify_url on denies
gates: {
'content:comment': { enabled: true, requireScopes: ['content:comment'], minTier: 'domain' },
},
});
const verdict = await door.gate('content:comment')(request);
if (!verdict.accepted) return denialResponse(verdict);
// GET /.well-known/axis-access — generated from the SAME policy the gate enforces
const wellKnown = buildAccessDocument(door.policy, {
details: { 'content:comment': { method: 'POST', path: '/comments' } },
});Flip enabled: false and the gate closes, no code change. Because the
authorizer threads the gate id and its minTier into the verdict, a §7.1 deny
body produced from it is action-aware: it names the refused action, the
canonical required_tier on tier denials, and the verify_url on-ramp when
registrationUrl is configured — a refused agent learns exactly what to fix. The port is
engine-agnostic: a paid EngineAuthorizer (Permify / OpenFGA sidecar) for
granular relationship/attribute rules drops into the same slot with the same
authorize(token, gateId, ctx) shape. The demo and free tier need no engine.
Stateful half: ledger, blocklist, reputation report-back
verifyAgent / axisGate are stateless verdict machines. A self-hosting platform
also needs state it owns: a record of who showed up, a runtime block list, and
a way to report a bad actor onward. These three modules add that, all zero-infra —
the platform runs the stores in its OWN store (default in-memory; plug in D1 /
SQLite / Postgres via a documented adapter shape).
import { axisGate, AccessLedger, loggedGate } from 'axis-platform-sdk';
const ledger = new AccessLedger(); // default in-memory store
const gate = loggedGate(axisGate({ audience }), ledger, { audience });
const verdict = await gate(request); // every verdict is logged
await ledger.recent({ limit: 25 }); // newest-first arrivals
await ledger.byOperator('axis:acme:op'); // arrivals from one operatorEach entry records { agent_id, operator_id, created_at, tier, delegation_valid,
effective_scope, gate_id, requested_action, display_name, decision, reason,
audience } — the same shape the cloud-hosted version uses for its arrivals
record, so the SDK and the cloud product share one arrival definition. decision
is auto_allow | denied |
held | approved | booted; created_at is epoch ms. Only the trustworthy
effective_scope is recorded, never the AIT's self-declared scope. A ledger write
failure never changes the verdict.
import { Blocklist, verifyAgent } from 'axis-platform-sdk';
const blocklist = new Blocklist();
await blocklist.blockAgent('axis:acme:bot', 'spammed'); // agent-level
await blocklist.blockOperator('axis:bad:op', 'whole op'); // operator-level
let verdict = await verifyAgent(token, {
audience,
blockedOperators: [...staticBlocked, ...(await blocklist.blockedOperatorIds())],
});
verdict = await blocklist.checkVerdict(verdict); // flips to denied if agent/op blockedWhen you boot an agent, you know something the network doesn't. reportFlag
builds a protocol-shaped Trust Attestation (AXIS Layer 3; SPEC §4.5), signs it
with the platform's own Ed25519 key (generated + persisted on first use via
getPlatformKey, WebCrypto), and POSTs it to a configurable reputation index.
Report-back is OFF by default — unconfigured, it's a graceful no-op. The
reputation index is a separate, future, commercial service — NOT the canonical
registry (which stays identity-only). See examples/verifier-worker.js for a
reference admin surface over the ledger + blocklist, and templates/ for the
deployable starters.
Single source of truth: the SDK is the port, the cloud-hosted version is the adapter
| SDK (this package, the port) | Cloud-hosted version (the D1-backed product adapter) |
| ----------------------------------- | ----------------------------------------------- |
| AccessLedger + recordEntry | arrivals table + recordArrival() |
| Blocklist (operator-level) | operator_blocks table + blockedOperators() |
| SwitchAuthorizer policy | door_policy table (serialized policy) |
| Blocklist agent-level (superset) | (not yet — an additive agent_blocks table) |
| reportFlag / reputation emit (new) | (not yet — the open emit half is here) |
The entry/meta shapes are deliberately byte-compatible with the cloud-hosted version's columns so there is one arrival/block record across the SDK and the cloud product — fold in, don't duplicate. A platform that needs its own store implements the documented adapter shape; the cloud-hosted version (in alpha) is the worked example of doing exactly that over Cloudflare D1.
Install
npm install axis-platform-sdkZero dependencies. Node 20+, Cloudflare Workers, modern browsers. Ships TypeScript declarations (the package is authored in plain JS).
Staying up to date
The self-host path needs no account, so we don't know who's running it (by design) and can't push you notices. Updates are pull-based:
- Versioning is semver. Patch/minor releases are backward-compatible; anything breaking is a major.
- While the SDK is pre-1.0, do NOT rely on a caret range to keep you current.
Under semver,
^0.5.0allows0.5.xbut not0.6.0— the caret pins to the minor for0.xversions. So a caret here quietly stops delivering updates, including security fixes, the moment the minor moves. Until 1.0, either pin an exact version and watch releases, or use>=0.5.0 <1.0.0if you accept that additive minors may land automatically. Caret ranges become the right advice at 1.0, when the public surface is frozen. - Watch the channel: GitHub Releases and the CHANGELOG are the source of truth for what changed. Use Dependabot or Renovate to get an automatic PR when a new version ships.
- The verification protocol is versioned too. The registry's
/verifycontract is backward-compatible within a protocol major; deprecations are announced in Releases ahead of removal.
An opt-in updates list for platform integrators (security and breaking-change notices) is planned. Until then, watching the repo is the way.
License
Apache-2.0. © Kipple Labs, Inc.
