Skip to main content

Inside the ULDA chain

A client keeps a private rolling origin. It publishes a witness, then proves that a later witness follows the previously accepted state.

Three ideas​

Origin is the producer's private state: a window of random blocks and the metadata needed to continue its chain. Keep the full origin secret.

Witness is the public representation derived from an origin. A verifier keeps the last accepted witness.

Step-up moves the origin forward: remove the oldest block, retain the others, and append a fresh random block. Signing the new origin produces its next witness.

Follow a five-block chain​

This illustration uses mode S with N = 5. Select a stage to follow one adjacent update.

Start with a private window.

Five random blocks form the origin. Keep the full origin on the client.

0
1
2
3
4
The numbers are labels, not the secret values.

For the initial window [0, 1, 2, 3, 4], the witness is:

[0, H(1), H²(2), H³(3), H⁴(4)]

After step-up, the new window is [1, 2, 3, 4, 5] and its witness is:

[1, H(2), H²(3), H³(4), H⁴(5)]

One additional hash on each of the first four received blocks brings it back to the corresponding stored commitment. The old leading block drops out of the comparison. The newly appended tail has no predecessor commitment; later transitions constrain it.

The complete Core package also includes metadata. Applications should use the Core API rather than reconstruct verification from this illustration.

Try the Core API​

Use Node.js 22 or later, or a modern browser with Web Crypto. The package is ESM-only; the Core 1.0.x release does not include TypeScript declarations.

npm install ulda-core@1.0.1

Save this as chain.mjs and run node chain.mjs:

chain.mjs
import UldaSign from 'ulda-core';

const core = new UldaSign();

// Keep the origin private.
let origin = core.New();
const previousWitness = await core.sign(origin);

// stepUp returns a new origin; it does not mutate the old one.
origin = core.stepUp(origin);
const candidateWitness = await core.sign(origin);

const valid = await core.verify(previousWitness, candidateWitness);
console.log(valid); // true

These four methods are the supported public API: New, stepUp, sign and verify. Despite its name, sign() derives a witness; it does not accept or sign an arbitrary message.

Keep verification in order​

Always pass the accepted older witness first, followed by the candidate:

verify(previousWitness, candidateWitness)

Verification is stateless. The integrating application must retain its accepted witness and replace it atomically only after successful verification. Well-formed invalid transitions return false; malformed packages or unavailable hash implementations can throw.

Calling stepUp() repeatedly on the same old origin can create competing branches. The application must coordinate writers and select which transition to accept.

Choose a mode​

ModeAccepted forward gapUse
S · SkippableAt least 1 and less than NVerify a bounded skip over intermediate witnesses.
X · Adjacent-onlyExactly 1Require every adjacent transition.

Core defaults to S. With N = 5, it can verify a gap of one through four; a gap of five is outside the window. The Auth SDK uses X by default for content and always for the master, managing the write sequence around it.

The illustration shows S construction. Mode X has a different witness construction; see the manifest for the exact rules. Non-default X profiles require separate review in the current release.

What a witness proves​

A valid transition establishes the required hash-linked overlap at an allowed future position. It does not establish a user's identity, authenticate the first witness, encrypt a payload, or bind an arbitrary payload to the witness.

The SDK supplies client-side authenticated encryption and resource management around Core. The server coordinates stored resource updates. An independent Core integration must supply its own trust establishment, input validation, payload protection and state management.

Continue with the Core API and modes and package formats. Review the source integration requirements before building directly on Core.