Signing
ALX supports optional cryptographic signatures over Block identities and protocol requests. Signing does not modify contentHash, blockHash, or declared parent relationships. Core Block creation, validation, and Graph verification do not require signatures.
Signing Model
ALX supports two signing mechanisms:
| Mechanism | Signs | Replay controls |
|---|---|---|
| Block Attestation | A specific blockHash, as durable evidence | None: no nonce and no expiry |
| Protocol Request | An application action, payload hash, chain, and domain | Nonce and expiry |
Block Attestations
A Block Attestation contains five fields:
| Field | Description |
|---|---|
blockHash | Identity of the attested Block. |
attester | Public identifier of the signer. |
algorithm | Signing suite: ed25519, p256, or eip712. |
timestamp | Signer-supplied Unix timestamp authenticated by the signature. |
signature | Cryptographic signature over the attestation digest. |
Attestation Digest
The signer signs a 32-byte digest of four attestation fields. \0 is one zero byte (0x00), and || is byte concatenation. Changing algorithm, attester, blockHash, or timestamp without a new signature invalidates the attestation.
attestationDigest = keccak256(
UTF8("ALX-ATTESTATION-V1\0") ||
canonicalize({ algorithm, attester, blockHash, timestamp })
)
Signing Suites
Ed25519 and P-256 are the portable suites: every implementation that claims the signing extension produces and verifies both identically, as the signing extension vectors require. The JavaScript SDK also supports an eip712 suite. The eip712 suite is outside portable signing-extension conformance and has no cross-language vectors.
| Suite | Signing rule |
|---|---|
ed25519 | Pure Ed25519 as defined by RFC 8032, with the 32-byte digest as the message. Public keys are 32 bytes and signatures are 64 bytes, each encoded as lowercase 0x-prefixed hexadecimal. |
p256 | ECDSA over NIST P-256, with the 32-byte digest supplied directly as the prehash. Public keys use the 65-byte uncompressed SEC1 encoding. Signatures use the 64-byte compact encoding of r followed by s, with a low s value. |
Signed Blocks
A SignedBlock envelope contains exactly one complete Block in block and at least one attestation in attestations. The envelope also accepts a singular attestation field for compatibility; new envelopes use attestations. The envelope is not a core Block and has no protocol identity: adding or removing attestations leaves contentHash and blockHash unchanged, and a core Block that carries an attestation field fails Block validation.
The JavaScript SDK provides:
createSignedBlock(content, parentHashes, signer, timestamp?)creates a Block and returns it in aSignedBlockenvelope with one attestation.addAttestation(signedBlock, signer)appends an attestation.validateSignedBlock(signedBlock, verifier)recomputes the enclosed Block's hashes and verifies every attestation. AMapfrom algorithm to verifier verifies an envelope that mixes suites.
The Ed25519 suite loads the optional peer dependency @noble/ed25519 (npm install @noble/ed25519). The following script creates and verifies a signed Block:
import {createSignedBlock, validateSignedBlock, Ed25519Signer, Ed25519Verifier} from '@alx-protocol/sdk';
// A 32-byte Ed25519 private key; keep a real key in a key store.
const privateKey = crypto.getRandomValues(new Uint8Array(32));
const signer = new Ed25519Signer(privateKey);
const signed = await createSignedBlock({type: 'review', status: 'approved'}, [], signer);
const result = await validateSignedBlock(signed, new Ed25519Verifier());
console.log(signed.attestations[0].algorithm, result.ok); // ed25519 true
Protocol Requests
The JavaScript SDK signs EIP-712 ProtocolRequest messages for application operations. A Protocol Request binds a signer to an action, payload hash, chain, domain, nonce, and expiry. verifySignedProtocolPayload() checks the signature, expected signer, replay window, and previously used nonces. Protocol Requests are not part of portable signing-extension conformance, and no conformance vectors cover them.
Protocol Request Fields
The JavaScript package signs under the EIP-712 domain name ALXProtocol, version 1, exported as PROTOCOL_SIGNING_DOMAIN_NAME and PROTOCOL_SIGNING_DOMAIN_VERSION. Verification binds to the exact domain, chain ID, verifying contract, signer, nonce state, and expiry of the target deployment; changing any of them invalidates the signature. A ProtocolRequest message has seven fields:
| Field | Type | Meaning |
|---|---|---|
actor | address | The signing account |
nonce | string | A single-use value |
expiry | uint256 | The expiry time in Unix seconds; validateReplayWindow() reports expired once it passes |
chainId | uint256 | The chain the request targets |
domain | string | An application string, distinct from the EIP-712 signing domain |
action | string | The requested operation |
payloadHash | bytes32 | The hash of the request payload |
Signature Verification
Block Attestation verification recomputes the attestation digest and verifies the signature with the suite named by algorithm. Protocol Request verification reconstructs the EIP-712 typed data and domain, recovers the signer, and evaluates the nonce and expiry.
Signature verification establishes that a key signed a specific message. Signature verification does not establish authorship, ownership, authorization, approval, legal identity, or content accuracy. Trust Boundary lists each claim.
When to Use Signing
Use Block Attestations for durable cryptographic evidence associated with a Block identity. Use Protocol Requests for signed operations that need request-specific authentication, nonce handling, expiry, or action binding.