Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.xandrlabs.ai/llms.txt

Use this file to discover all available pages before exploring further.

The @alx/protocol package is the canonical reference implementation of ALX Protocol v1. Every other conformant implementation — Python, Rust, or otherwise — is validated against the outputs this package produces. It ships canonicalization, identity hashing, economic utilities, and Zod-validated schema types, and is the primary SDK you should reach for when building on ALX Protocol. Node.js 20 or later is required.

Installation

npm install @alx/protocol

Exports

@alx/protocol groups its exports into four areas.

Canonicalization functions

These functions implement the deterministic serialization and identity-hashing pipeline defined by the protocol specification.
ExportDescription
canonicalize(value)RFC 8785-style (JCS) deterministic JSON serialization. Throws on null or non-finite numbers.
kbHashFromEnvelope(envelope)Derives the canonical kbHash for a Knowledge Block envelope: keccak256("KB_V1" + JCS(normalizedEnvelope)).
contentHashFromCanonical(json)Raw keccak256 of an already-canonicalized JSON string (no domain prefix).
domainHashFromCanonical(tag, json)Domain-prefixed keccak256(tag + json). Used by kbHashFromEnvelope internally.
artifactHashFromBytes(bytes)keccak256 of raw artifact bytes. Use this to compute the artifactHash field before publishing.
artifactHashFromPayload(payload)Convenience wrapper: canonicalizes a CanonicalPayload and returns its keccak256.
cidV1FromCanonicalSync(json)Derives a CIDv1 from canonicalized JSON using SHA-256 (synchronous, Node crypto).
sortSources(envelope)Returns a new envelope with sources sorted deterministically (required before hashing).
buildDerivedEnvelope(input)Builds and normalizes a complete CanonicalEnvelope for a derived Knowledge Block.
compareCandidates(a, b)Consensus-critical comparator for canonical pool head selection: block → log index → kbId.
DOMAIN_TAGSObject of all stable domain tag strings (KB_V1, TASK_V1, …).

Core functions

Available from @alx/protocol/core.
ExportDescription
buildSignedProtocolRequestDomainConstructs the EIP-712 domain separator for signed protocol requests.
verifySignedProtocolRequestVerifies an EIP-712 signed request end-to-end, enforcing expiry and nonce uniqueness.
SIGNED_PROTOCOL_REQUEST_TYPESThe EIP-712 type definition for SignedProtocolRequest. Pass to wallet.signTypedData().
NonceTrackerIn-memory nonce tracker for replay protection.
SignedRequestValidationErrorError class thrown by verification functions, with machine-readable .code.

Economics

Available from @alx/protocol/economics or via flat re-exports from @alx/protocol.
ExportDescription
computePayout(base, rs, freshness)Computes the payout for a KB: base × clamp(rs) × freshness, rounded to 6 decimal places.
normalizeOnChainScore(score)Converts an on-chain integer score (0–1000) to a normalized RS float in [0.01, 3].
freshnessMultiplier(isoDate)Exponential decay multiplier based on publish date. Half-life is 30 days.
meetsTier(rs, tier)Returns true if the reputation score meets the minimum threshold for a tier (0–3).
clampRS(rs)Clamps a raw reputation score to [RS_MIN, RS_MAX] = [0.01, 3].
ledgerLeafHash(contentHash, amount)SHA-256 leaf hash for verifying settlement integrity.
RS_MIN, RS_MAX, HALF_LIFE_DAYSProtocol constants for reputation scoring.

Schema types

Available from @alx/protocol directly or @alx/protocol/schema.
ExportDescription
CanonicalEnvelopeThe full wire shape of a Knowledge Block envelope.
CanonicalPayloadThe payload union type covering all KB payload variants.
CanonicalDerivationDerivation metadata for derived KBs.
KBTypeEnum of valid type values (practice, pattern, prompt, …).
TrustTierEnum of valid tier values (open, verified, premium, restricted).

On-chain ABI

import REGISTRY_ABI from "@alx/protocol/abis/AlexandrianRegistryV2.json" assert { type: "json" };
REGISTRY_ABI is the ABI for the AlexandrianRegistryV2 contract. Import it from the /abis/ subpath and use it with ethers or viem when you need to read or write registry state directly on-chain.

Computing kbHash and artifactHash from scratch

The following example walks through the complete flow: constructing a Knowledge Block envelope, computing its artifactHash, deriving its kbHash, and confirming that the two hashes are stable across repeated calls.
import {
  canonicalize,
  kbHashFromEnvelope,
  artifactHashFromBytes,
  DOMAIN_TAGS,
} from "@alx/protocol";

// 1. Define your KB payload.
const payload = {
  type: "practice",
  rationale: "Use constant-time comparison to prevent timing attacks on tokens.",
  contexts: [],
  failureModes: [],
};

// 2. Serialize the artifact bytes and compute artifactHash.
//    In production, this would be the bytes of your stored artifact file.
const artifactBytes = new TextEncoder().encode(JSON.stringify(payload));
const artifactHash = artifactHashFromBytes(artifactBytes);

// 3. Build the canonical envelope.
const envelope = {
  type: "practice",
  domain: "software.security",
  sources: [],          // empty for a root Knowledge Block
  tier: "open",
  artifactHash,         // bind the artifact to this envelope
  payload,
};

// 4. Derive the kbHash. This is the canonical, protocol-visible identity.
const kbHash = kbHashFromEnvelope(envelope);

console.log("artifactHash:", artifactHash);
// artifactHash: 0x5e71fc830e383453429f2b703db3eb456dc4a6bfd66b2a0fc7535330ab8b168a

console.log("kbHash:", kbHash);
// kbHash: 0x5c3415ff46569330de2d0820b55a31859f2c7efde1df96ce5bb59c731a872d51

// 5. kbHash is deterministic — same envelope always yields the same hash.
console.log("stable:", kbHashFromEnvelope(envelope) === kbHash); // true
The top-level kbHash field, if present on an envelope, is stripped before hashing. Do not rely on its presence in the envelope object to recover the hash — always call kbHashFromEnvelope directly.

EIP-712 signing utilities

The @alx/protocol/core subpath exposes the full EIP-712 signing and verification surface:
import {
  buildSignedProtocolRequestDomain,
  verifySignedProtocolRequest,
  SIGNED_PROTOCOL_REQUEST_TYPES,
  NonceTracker,
} from "@alx/protocol/core";

// Build a domain bound to the Base mainnet registry contract.
const domain = buildSignedProtocolRequestDomain({
  chainId: 8453,
  verifyingContract: "0xD1F216E872a9ed4b90E364825869c2F377155B29",
});

// On a server, use a NonceTracker to prevent replay.
const tracker = new NonceTracker();

const result = verifySignedProtocolRequest({
  domain,
  request: {
    kbId: "0xabc123...",
    query: "How do I prevent timing attacks?",
    agent: "0xYourAgentAddress",
    nonce: 1,
    expiry: Math.floor(Date.now() / 1000) + 300,
    chainId: 8453,
  },
  signature: "0x...",
  expectedChainId: 8453,
  nonceTracker: tracker,
});
// result.ok === true; result.signer is the recovered address.
Always pass verifyingContract explicitly to buildSignedProtocolRequestDomain. Without it, the domain defaults to the zero address, which is safe only for off-chain verification. Any on-chain verifier must supply its deployed contract address — using the zero address on-chain allows signatures to be replayed across contracts.