Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.algovoi.co.uk/llms.txt

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

The AlgoVoi RFC 9421 signer produces signed HTTP requests conforming to RFC 9421 (HTTP Message Signatures) and RFC 9530 (Digest Fields for HTTP). It pairs with the RFC 9421 Verifier: sign on the client, verify on the server — both sides produce the same signing base, byte-reproducibly.

Python (PyPI)

pip install algovoi-rfc9421-signer

TypeScript (npm)

npm install @algovoi/rfc9421-signer

What the signer produces

A single sign_request() / signRequest() call returns three headers ready to attach to your HTTP request:
HeaderRFCPurpose
Signature-InputRFC 9421 §2.1Declares covered components and signature parameters
SignatureRFC 9421 §4Base64url-encoded Ed25519 signature over the signing base
Content-DigestRFC 9530SHA-256 digest of the request body
The signing base follows RFC 9421 §2.5: @method is case-preserved (uppercase for HTTP), and a final @signature-params line is appended. This is the format expected by algovoi-rfc9421-verifier v0.3.0+ and any other RFC 9421-compliant verifier.

Quick start

Python

import json, time
from algovoi_rfc9421_signer import sign_request

SEED = bytes.fromhex("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae3d55")
body = json.dumps({"recipient_address": "ALGO...", "network": "algorand"}, separators=(",", ":")).encode()

sig = sign_request(
    method="POST",
    authority="api.algovoi.co.uk",
    path="/compliance/screen",
    body=body,
    private_key=SEED,
    keyid="did:web:api.algovoi.co.uk",
    created=int(time.time()),
)

# Attach to your HTTP request:
headers = {
    "Content-Type": "application/json",
    "Signature-Input": sig.signature_input,
    "Signature": sig.signature,
    "Content-Digest": sig.content_digest,
    "X-Signer-Pubkey": "<your-ed25519-public-key-hex>",
}

TypeScript

import { signRequest } from "@algovoi/rfc9421-signer";

const body = Buffer.from(
  JSON.stringify({ recipient_address: "ALGO...", network: "algorand" })
);

const sig = await signRequest({
  method: "POST",
  authority: "api.algovoi.co.uk",
  path: "/compliance/screen",
  body,
  privateKey: SEED_HEX,
  keyid: "did:web:api.algovoi.co.uk",
  created: Math.floor(Date.now() / 1000),
});

const headers = {
  "Content-Type": "application/json",
  "Signature-Input": sig.signatureInput,
  Signature: sig.signature,
  "Content-Digest": sig.contentDigest,
  "X-Signer-Pubkey": "<your-ed25519-public-key-hex>",
};

Signing a /compliance/screen request

The AlgoVoi gateway verifies inbound request signatures on /compliance/screen when Signature-Input, Signature, and X-Signer-Pubkey are present. The response includes request_signature_verified: true when the signature checks out:
import httpx

with httpx.Client(timeout=30) as client:
    resp = client.post(
        "https://api.algovoi.co.uk/compliance/screen",
        content=body,
        headers=headers,
    )

data = resp.json()
print(data["request_signature_verified"])  # True
print(data["request_signature_keyid"])     # "did:web:api.algovoi.co.uk"
print(data["verdict"])                     # "allow" | "flag" | "block"
Unsigned requests still work — request_signature_verified is null when the signature headers are absent.

Covered components

By default, sign_request() covers: @method, @authority, @path, content-digest, created. This matches the coverage used in the conformance vector set and the AlgoVoi gateway’s verifier expectations.

Cross-implementation parity

Python uses PyNaCl; TypeScript uses @noble/ed25519. Both derive the same public key from the same Ed25519 seed and produce byte-identical signatures for the same signing base. Test seed: 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae3d55 Derived public key: 700e2ce7c4b674427eab27ba820bcf6f0faebe68e09fe8564292114e41dc6a41 This keypair is used throughout the signer and verifier test suites for byte-reproducible fixture generation.

See also