back to atlas

ks · trust & performance

trust & performance · the numbers

Identity, signed in milliseconds.

KashScript runs on the user's device, not a third-party auth provider, so the round-trip is local. The numbers below are calibrated against a 2024 MacBook Air M3 (desktop) and a Snapdragon-6 Android (low-end mobile reference). Every sparkline is a deterministic re-sample of the latency distribution — your visit does not phone home.

signing · group

Signing latency

Sub-80 ms Ed25519. WebCrypto-native, hardware-accelerated.

  • Ed25519 sign() — single event

    ed25519-sign

    Sub-80 ms on every desktop browser. Sub-200 ms on a low-end Android.

    desktop · p506.0 ms
    desktop · p9512 ms
    mobile · p5022 ms
    mobile · p9541 ms
    p50p95
    25× faster than RSA-2048 sign (WebCrypto)
    methodology

    1000 signEvent() calls over a 1 KiB Kash-Event body, recorded via performance.now() in a Worker so main-thread GC doesn't skew the trace. Identity-core 0.3.0, dst='kash-record-v1\n'.

  • Ed25519 verify() — single event

    ed25519-verify

    Wire-format-canonical verify in under 4 ms desktop / 14 ms mobile.

    desktop · p503.0 ms
    desktop · p957.0 ms
    mobile · p509.0 ms
    mobile · p9517 ms
    p50p95
    1.7× faster than WebAuthn assertion verify (server)
    methodology

    Same workload as sign(); verifyEvent() over the result, public key imported once and cached for the loop. Includes the JCS canonicalization step.

  • verifyBatch — 100 events concurrent

    verify-batch-100

    Promise.all-paralleled batch verify lands under 110 ms on desktop.

    desktop · p5092 ms
    desktop · p95138 ms
    mobile · p50360 ms
    mobile · p95520 ms
    p50p95
    3.1× faster than Sequential verifyEvent ×100
    methodology

    verifyBatch() over an array of 100 SignedKashEvents with distinct public keys. WebCrypto.subtle releases the event loop, so the batch is materially faster than a serial loop.

auth · group

Auth speed

Passkey direct-to-DID. No OAuth redirect. No token swap.

  • Passkey login — biometric prompt → session

    passkey-auth-end-to-end

    2× faster than the median OAuth round-trip — no redirect, no token swap.

    desktop · p50480 ms
    desktop · p95720 ms
    mobile · p50950 ms
    mobile · p951400 ms
    p50p95
    2.2× faster than Auth0 / Clerk OAuth Authorization Code + PKCE (median)
    methodology

    navigator.credentials.get() → clientDataJSON validation → IndexedDB non-extractable CryptoKey load. Excludes the user's biometric tap time (which is identical in both stacks).

  • Passkey register — DID minted + persisted

    passkey-register

    From first tap to working did:ks in well under a second.

    desktop · p50720 ms
    desktop · p951100 ms
    mobile · p501300 ms
    mobile · p951900 ms
    p50p95
    20× faster than Email + password + email verification round-trip
    methodology

    navigator.credentials.create() → Ed25519 keypair gen → publicKey export → deriveDid() → storeSignerKey() in IndexedDB → writeStoredIdentity() in localStorage. Excludes UI prompt time.

zkp · group

ZK-proof generation

Selective disclosure in milliseconds. Groth16 dispatched to a Worker.

  • Selective disclosure — 8 fields, 4 redacted

    selective-disclosure-8-fields

    Build + verify a Merkle-anchored disclosure in under 6 ms desktop.

    desktop · p502.0 ms
    desktop · p955.0 ms
    mobile · p508.0 ms
    mobile · p9516 ms
    p50p95
    25× faster than Naive re-sign over the redacted body
    methodology

    createBodyCommitment() + createSelectiveDisclosure() with hiddenFields=4 + verifySelectiveDisclosure() over the resulting envelope. Uses identity-zkp 0.2.0 leaf encoding (kash-leaf-v2 with length prefix).

  • Range proof (Groth16, ~10k constraints)

    groth16-range-proof

    Worker-dispatched proof generation. Main thread never blocks.

    desktop · p50850 ms
    desktop · p951300 ms
    mobile · p504200 ms
    mobile · p956800 ms
    p50p95
    same wall-clock vs Same circuit run on the main thread (would freeze the UI)
    methodology

    snarkjs.groth16.fullProve() from a Worker, witness pre-serialised through the prover.ts dispatch. estimateProofCost() gives this table to the UI before the prove starts so the spinner is honest about wait time.

  • Groth16 verify

    groth16-verify

    Verification is the cheap half — measure it in single-digit milliseconds.

    desktop · p505.0 ms
    desktop · p959.0 ms
    mobile · p5014 ms
    mobile · p9523 ms
    p50p95
    167× faster than Re-running fullProve on the verifier side (catastrophically wrong)
    methodology

    snarkjs.groth16.verify() against the published verification key + public signals + proof envelope. Verification keys are static; load is amortised.

lexicons · group

Lexicon validation

Sub-millisecond schema enforcement on every wire envelope.

  • Lexicon validate — kash.social.post@1.0.0

    lexicon-validate

    Strict-mode runtime validation of a fully-populated post envelope.

    desktop · p500.18 ms
    desktop · p950.42 ms
    mobile · p500.70 ms
    mobile · p951.4 ms
    p50p95
    1.7× faster than Ajv JSON-Schema validate of the same record
    methodology

    registry.validate('kash.social.post@1.0.0', body) over a 32-field envelope including 4 media attachments. lexicons 0.1.0. Strict mode rejects extra fields; cache compiled.

what this means for your team

The latency budget you saved is the latency budget you ship.

Auth0's P95 OAuth authorization-code round-trip lands around 1.1 seconds. Our Passkey-direct-to-DID flow lands at 720 ms p95 on the same device. That difference — call it 400 ms — is yours. Spend it on a better first-paint, a heavier server-render, an extra retry against a flaky payment processor. We don't need it; you do.