> ## 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.

# RFC 9421 HTTP Signatures Signer

> AlgoVoi authored reference signer for RFC 9421 HTTP Message Signatures and RFC 9530 Digest Fields. Python and TypeScript, byte for byte parity, Ed25519.

The AlgoVoi RFC 9421 signer produces signed HTTP requests conforming to
[RFC 9421 (HTTP Message Signatures)](https://www.rfc-editor.org/rfc/rfc9421)
and
[RFC 9530 (Digest Fields for HTTP)](https://www.rfc-editor.org/rfc/rfc9530).

It pairs with the [RFC 9421 Verifier](/rfc9421-verifier): sign on the client,
verify on the server — both sides produce the same signing base, byte-reproducibly.

That signing base is the **RFC 9421 §2.5 signing-base substrate** (`rfc9421_proxy_chain_v1`) — one half
of AlgoVoi's authored L1 alongside the [JCS canonicalisation substrate](/canonicalisation-substrate),
dated in `draft-hopley-x402-rfc9421-binding`. Build L2 freely; the one ask is to **attribute the L1**
(keep the NOTICE, import by hash).

<CardGroup cols={2}>
  <Card title="Python (PyPI)" icon="python" href="https://pypi.org/project/algovoi-rfc9421-signer/">
    `pip install algovoi-rfc9421-signer`
  </Card>

  <Card title="TypeScript (npm)" icon="js" href="https://www.npmjs.com/package/@algovoi/rfc9421-signer">
    `npm install @algovoi/rfc9421-signer`
  </Card>
</CardGroup>

## What the signer produces

A single `sign_request()` / `signRequest()` call returns three headers ready to attach
to your HTTP request:

| Header            | RFC           | Purpose                                                   |
| ----------------- | ------------- | --------------------------------------------------------- |
| `Signature-Input` | RFC 9421 §2.1 | Declares covered components and signature parameters      |
| `Signature`       | RFC 9421 §4   | Base64url-encoded Ed25519 signature over the signing base |
| `Content-Digest`  | RFC 9530      | SHA-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

```python theme={null}
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

```typescript theme={null}
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:

```python theme={null}
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 default carries `created` as a covered component, matching the legacy `rfc9421_proxy_chain_v0` coverage and the AlgoVoi gateway's verifier expectations. Strict RFC 9421 treats `created` as a *signature parameter*, not a covered component; the RFC 9421 §2.5-conformant `rfc9421_proxy_chain_v1` set therefore covers only `@method`, `@authority`, `@path`, `content-digest`, with `created` carried as a parameter. Pass an explicit `covered_components` list to emit the conformant coverage. See the [conformance vectors](/conformance-vectors) for both.

## 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

* [RFC 9421 Verifier](/rfc9421-verifier) — companion verifier package
* [Canonicalisation substrate](/canonicalisation-substrate) — JCS RFC 8785 discipline
* [Conformance vectors](/conformance-vectors) — cross-implementation vector corpus
* [Compliance gate](/compliance-gate-v1) — gateway compliance enforcement
