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.
| Export | Description |
|---|
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_TAGS | Object of all stable domain tag strings (KB_V1, TASK_V1, …). |
Core functions
Available from @alx/protocol/core.
| Export | Description |
|---|
buildSignedProtocolRequestDomain | Constructs the EIP-712 domain separator for signed protocol requests. |
verifySignedProtocolRequest | Verifies an EIP-712 signed request end-to-end, enforcing expiry and nonce uniqueness. |
SIGNED_PROTOCOL_REQUEST_TYPES | The EIP-712 type definition for SignedProtocolRequest. Pass to wallet.signTypedData(). |
NonceTracker | In-memory nonce tracker for replay protection. |
SignedRequestValidationError | Error class thrown by verification functions, with machine-readable .code. |
Economics
Available from @alx/protocol/economics or via flat re-exports from @alx/protocol.
| Export | Description |
|---|
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_DAYS | Protocol constants for reputation scoring. |
Schema types
Available from @alx/protocol directly or @alx/protocol/schema.
| Export | Description |
|---|
CanonicalEnvelope | The full wire shape of a Knowledge Block envelope. |
CanonicalPayload | The payload union type covering all KB payload variants. |
CanonicalDerivation | Derivation metadata for derived KBs. |
KBType | Enum of valid type values (practice, pattern, prompt, …). |
TrustTier | Enum 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.