@dewebprotocol/malt is the supported TypeScript/JavaScript boundary for
running the MALT authentication verifier and writer in browsers. It packages a
stable API, type declarations, Worker lifecycle management, reproducible WASM
assets, and Vite integration.
MALT Core remains the normative implementation and protocol source of truth. This repository does not implement commitments, ProofLists, CID rules, or writer semantics independently in TypeScript.
The package asset binding is 0.0.5, built against the published
malt-core v0.0.11-rc.2 release at commit
a8b0f9260ff025e834a803f87161685914604d11.
malt-core.lock.json binds that tag, commit, Go module
checksums. The malt.ts-core-lock/v2 lock binds Core source only; Core WASM
assets are not required. This repository exclusively owns WASM entrypoints,
compilation, integration runners, Worker lifecycle, and distribution.
Release builds must never resolve a mutable Core branch or latest alias. A
new Core release is adopted through a lock-file change and conformance run
before a new npm package is published. Package versions remain v0.0.x until
MALT Core publishes v0.1.0.
import {
createBrowserMaltWriter,
createBrowserVerifierLease,
loadBrowserVerifier,
releaseBrowserVerifier,
verifyAuthenticationLocally
} from '@dewebprotocol/malt'
const lease = createBrowserVerifierLease()
const verifier = await loadBrowserVerifier({
lease,
runtimeURL: '/verifier/<asset-set>/wasm_exec.js',
wasmURL: '/verifier/<asset-set>/malt-verifier.wasm'
})
const checked = await verifyAuthenticationLocally({ request, result, provider: verifier })
if (!checked.valid) throw new Error(checked.error || 'MALT verification failed')
releaseBrowserVerifier(lease)
const writer = await createBrowserMaltWriter({
baseURL: '/writer/<asset-set>/'
})All Gateway results remain untrusted until verified against a root and query selected by the caller. Writer results are candidates; this package does not publish or promote trusted roots.
The package contains coordinated verifier and writer asset sets. Consumers can serve them from digest-versioned same-origin paths with the included plugin:
import {
maltWasmAssetsDirectory,
versionedWasmAssetsPlugin
} from '@dewebprotocol/malt/vite'
export default {
publicDir: maltWasmAssetsDirectory(),
plugins: [versionedWasmAssetsPlugin()]
}Only the resulting full SHA-256 paths are suitable for immutable HTTP caching.
The package does not download executable assets during postinstall.
npm ci
npm test
npm run typecheck
npm run check:assetsFor a Core release or Go/WASM ABI update, run the resource-limited workspace equivalent of:
make audit-core-release
make test-wasm
npm pack --dry-runPublishing an npm version, Git tag, or GitHub Release is intentionally separate from committing source.
verifyAuthenticationLocally({request, result, ...options}) verifies the
caller-selected Root, label steps and binding/range operation through Core.
malt.authentication/5 also authenticates early path absence. Labels are
base64-encoded opaque bytes. encodeIndexLabel(bigint) serializes Direct
indices as eight unsigned big-endian bytes, and encodeKeyLabel(Uint8Array)
serializes Direct 32-byte keys. deriveCoordinate({profile, label}) executes
Core derivation in the verifier Worker and returns a key or a bigint index.
JavaScript performs no label hashing or application path normalization.
Prefix layout 4 authenticates exact original label bytes and targets; distinct
labels at the same full coordinate are errors. Layout 1 is rejected without a
compatibility reader. Rebuild old Prefix state and its dependent parent Roots;
Positional layout 3 and Root V=0 are unchanged.
prepareAuthentication(backend, stateJSON) builds a complete candidate.
updateAuthentication(backend, candidateJSON, stateJSON) verifies a complete
base and applies the current Core writer. Inputs are UTF-8 JSON Uint8Arrays;
returned JSON follows Core's authentication candidate schema.
createAuthentication and importAuthentication return JSON {handle, root}.
Handles are opaque identities bound to this router, backend and Worker.
importAuthentication transfers a candidate's complete ArrayBuffer when
possible; pass a copy if the caller needs those bytes afterwards.
applyAuthentication takes a handle and a malt.authentication-delta/2
change set. It returns a new independent handle and leaves its base usable.
exportAuthentication explicitly exports a complete candidate. Retained
updates do not resend or export the complete base.
import { encodeIndexLabel } from '@dewebprotocol/malt'
const encode = (value: unknown) => new TextEncoder().encode(JSON.stringify(value))
const handleBytes = (handle: string) => new TextEncoder().encode(handle)
const base = JSON.parse(await writer.createAuthentication('kzg', encode(state)))
const next = JSON.parse(await writer.applyAuthentication('kzg', handleBytes(base.handle), encode({
profile: 'malt.authentication-delta/2',
changes: [{ label: encodeIndexLabel(0n), before: oldCID, after: newCID }]
})))
const candidate = JSON.parse(await writer.exportAuthentication('kzg', handleBytes(next.handle)))
await writer.discardAuthentication('kzg', handleBytes(base.handle))
await writer.closeAuthentication('kzg')Discard, close and Worker termination invalidate the associated handles. Pending operations reserve their backend, and retained state prevents backend switches until explicitly closed. Fatal runtime loss retires the Worker and cancels pending operations; a subsequent request creates a new generation.
validateAuthenticationBatch(backend, batchJSON) verifies all candidates and
returns Core's digest of the exact malt.authentication-batch/2 batch.
Candidates appear before parents and lineage successors. A bootstrap batch
includes its newly constructed base.
validateAuthenticationReceipt(backend, batchJSON, receiptJSON) checks the
malt.authentication-receipt/2 transaction ID, base, final Root, digest and
durable boundary. It returns the final Root without modifying retained state.
Receipts acknowledge persistence; they neither prove a portable state
transition nor publish a head or grant client trust. Applications own durable
journals, snapshots, transport and publication.
The current source removes Map/List, Resolve/Read, client-root, update-view and session snapshot compatibility APIs. Worker readiness requires the complete current ABI. There are no forwarding aliases or optional old-runtime exports.
The malt-core.lock.json and packaged binary assets identify the published
Core v0.0.11-rc.2 release. Its current host and batch APIs are bound to the
exact release tag, source commit, and module checksums.
Source integration checks remain separate from this published release binding.
Validate both source checkouts under the workspace CPU scope:
./scripts/test-core-source.sh /absolute/path/to/core-checkout /tmp/malt-ts-source-wasmThe output directory must be new and empty. The script creates a temporary
Go workspace, builds outside assets/, checks backend isolation, runs typed
Core conformance and retained-writer/batch tests for all committer profiles,
and exercises the real browser router against a Worker. These are development
artifacts, separate from a package release. make test-wasm applies the same
current contracts to the exact release selected by the lock.
Core provides the Go SDK, cryptographic parameter metadata, and portable
conformance/authentication-v3.json corpus. All JavaScript/WASM runners live
in this repository; tests never execute a Core-owned browser build or runner.
make test-wasm tests the pinned published Core release. For a proposed Core
source change, use the explicit non-release scripts/test-core-source.sh.
make wasm-release builds reproducible archives from a clean, exact malt-ts
commit. make test-wasm-release also checks adversarial archive fixtures. The
manifest uses malt.ts-wasm-release/v1, identifies this package and source
commit, and separately binds the exact Core dependency and corpus digest.
See WASM release assets.
maltCoreRelease exposes the Core version, commit, moduleSum, and goModSum.
The former Core WASM manifest/asset-set properties are removed. Consumers use
this package's asset checksums and build provenance for the WASM it distributes.
The package uses root-only Positional metadata: count plus an optional
opaque payload_cid. Range bounds are element indices, and non-root vectors
have no reserved metadata slot. Application JSON decoding stays in consumers.
The packaged verifier and writer assets are built from the exact audited Core
release above and pass its authentication-v3.json conformance corpus.