authentication and signature
version 0.1 · updated 2026-04-16 · status: published
scope
This document defines how creators authenticate manifests in the Aevia protocol and how verifiers validate them. Authentication uses EIP-712 typed-data signing over the canonical hash of the manifest (RFC-1). The signing key is an Ethereum secp256k1 private key operated directly by the creator or via a Privy embedded wallet.
MUST, SHOULD, MAY follow RFC 2119.
key material
- curve: secp256k1 (same as Ethereum).
- public key format: EIP-55 Ethereum address (20 bytes, checksum casing).
- signature format: compact 0x{r}{s}{v}, 65 total bytes (32+32+1), with v ∈ {27, 28}.
- creators MAY hold the private key directly (software wallet, hardware wallet) or delegate via Privy (§7).
EIP-712 domain separator
The Aevia EIP-712 domain is fixed:
{
"name": "Aevia",
"version": "1",
"chainId": <chain ID of the target network>,
"verifyingContract": <ContentRegistry address on that chain>
}chainId MUST be the chain ID of the target network (Base Sepolia = 84532, Base Mainnet = 8453). verifyingContract MUST be the Content Registry address deployed on that chain.
The domain separator MUST be included in the EIP-712 digest. Manifests signed with the wrong domain MUST be rejected.
typed data structure
The primary signed type is Manifest:
Manifest(
bytes32 manifestHash,
address creator,
uint64 createdAt
)- manifestHash = SHA-256 of the canonical encoding of the manifest with cid and signature fields removed (RFC-1 §5).
- creator = signer address (MUST match manifest.creator).
- createdAt = Unix timestamp in seconds (MUST match parse of manifest.created_at).
signature construction
To produce the signature, the creator:
- computes manifestHash per RFC-1 §5;
- builds the Manifest struct (§4);
- computes domainSeparator = hashStruct(EIP712Domain);
- computes digest = keccak256(0x1901 || domainSeparator || hashStruct(Manifest));
- signs with secp256k1: signature = sign(privateKey, digest).
The resulting signature MUST be placed in manifest.signature in hex-encoded form with the 0x prefix.
verification
Verification is deterministic and offline. Given a manifest M, a verifier MUST:
- extract M.signature;
- compute manifestHash (RFC-1 §5);
- reconstruct the EIP-712 digest (§5.4);
- recover the signer: recovered = ecrecover(digest, M.signature);
- verify that recovered == M.creator;
- (when relevant) verify that createdAt typed-data matches the parse of M.created_at.
Any mismatch MUST result in rejection. No network round-trip is required.
Privy integration
Privy embedded wallet operates as a delegated custody layer: the user authenticates via email/social login, Privy generates and custodies secp256k1 keys under MPC, and exposes a signing API. From the Aevia protocol perspective, a Privy key is indistinguishable from a local software key — the resulting address is registered in the Content Registry normally and the EIP-712 signature is valid under §6.
Clients using Privy MUST request typed-data signing via signTypedData, not personal_sign. Clients SHOULD display the typed-data fields to the user before confirmation, per EIP-712 best practice.
security considerations
- cross-chain replay: the domain separator includes chainId and verifyingContract, preventing signature reuse between Base Sepolia and Mainnet.
- secp256k1 malleability: verifiers MUST use low-s ECDSA (EIP-2) to avoid malleable equivalent signatures.
- key loss: loss of wallet access means loss of the ability to sign future manifests. Previous manifests remain valid since the signature is already in the Registry.
- Privy social recovery: Privy MAY rotate keys via social recovery; each rotation produces a new address, which MUST be explicitly acknowledged as the same creator identity via cross-signature.
references
- IETF RFC 2119 — Key words for RFCs
- EIP-712 — Typed structured data hashing and signing
- EIP-55 — Mixed-case checksum address encoding
- EIP-2 — Homestead hard-fork (low-s signatures)
- RFC-1 — Manifest Schema
- RFC-2 — Content Addressing
- Privy Documentation