Skip to main content
The enterprise question this answers: “can we keep the signing key in our own HSM / KMS / Vault, so AlgoVoi never holds it — without changing how the evidence verifies?” Yes. Key custody is pluggable, exactly like the storage backend. You pass a Signer; AlgoVoi calls it to sign each receipt and never touches the raw private key. And it is additive: the signed envelope is byte-for-byte the same shape regardless of where the key lives, so the free verifier, the conformance vectors, and your existing receipts all keep verifying with zero changes. Custody changes where the key sits, never the evidence format.
Included with Records Vault — BYO-key custody (HSM / KMS / Vault) ships in the bundle, no separate purchase. And the whole stack — engine, the KMS/HSM/Vault adapters, and every dependency — installs entirely from the AlgoVoi private index with no PyPI, so air-gapped deployments are first-class: one licence token, zero external network.

Custody modes

ModeWhere the private key livesUse when
LocalIn-process (the default, bundled)Air-gapped or single-node deployments
KMS-wrappedWrapped (encrypted) by a key-encryption-key in your KMS / HSM / VaultYou want external custody today — works with every provider
Native HSMNever leaves the HSM (PKCS#11)Your HSM signs the algorithm natively
Most enterprises use KMS-wrapped: the Falcon-1024 key is stored encrypted by a key-encryption-key (KEK) that lives in your KMS/HSM and never leaves it. At sign time the key is unwrapped into memory, the receipt is signed, and the buffer is zeroized — the plaintext key is never persisted. This works with any provider right now because the KMS only performs a classical unwrap, not the post-quantum signing operation.

The interface

A Signer is four methods:
class Signer(Protocol):
    def public_key(self) -> bytes: ...
    def kid(self) -> str: ...
    def sign(self, message: bytes) -> bytes: ...
    def can_sign(self) -> bool: ...
You hand a Signer to the archive, and it threads automatically through every Records Vault evidence stream (access log, timestamps, legal hold, and the rest):
from algovoi_doc_archive.archive import Archive
from algovoi_records_vault import RecordsVault

archive = Archive(signer=my_signer, storage=my_storage, encryptor=my_encryptor)
vault = RecordsVault(archive=archive)   # all streams now sign through my_signer

Local custody (default)

from algovoi_doc_archive.signer import LocalSigner

signer = LocalSigner(public_key, secret_key)   # public-key-only = read-only / audit view

KMS-wrapped custody

WrappedKeySigner holds only the wrapped key and an unwrap callable that asks your KMS/HSM/Vault to decrypt it. The KEK never leaves your provider.
from algovoi_doc_archive_kms import kms_wrap, kms_signer

wrapped = kms_wrap(falcon_secret_key, key_id="alias/algovoi")   # one-time setup
signer = kms_signer(public_key, wrapped, region_name="eu-west-2")
# encryption key too: kms_encryptor(mlkem_public_key, kms_wrap(mlkem_secret_key, key_id=...))
To set up, wrap your Falcon private key once with your provider’s KEK and store the wrapped blob; the plaintext key never has to live on disk.

Encryption keys too

The same pattern covers the ML-KEM decryption key that protects your documents at rest. Encryption needs only the public key (no custody concern); decryption unwraps the secret key through your KMS, decapsulates, then zeroizes. WrappedKeyEncryptor is the encryption counterpart of WrappedKeySigner and produces the identical blob format, so archives stay interchangeable.
from algovoi_doc_archive.archive import Archive
from algovoi_doc_archive.encryption import WrappedKeyEncryptor

encryptor = WrappedKeyEncryptor(
    mlkem_public_key,
    wrapped_mlkem_secret_key,                       # ML-KEM key wrapped by your KMS KEK
    unwrap=lambda w: kms.decrypt(CiphertextBlob=w)["Plaintext"],
)
archive = Archive(signer=my_signer, encryptor=encryptor, storage=my_storage)
With both a WrappedKeySigner and a WrappedKeyEncryptor, every key AlgoVoi uses — signing and encryption — stays in your custody, and AlgoVoi never holds either in the clear.

Verification is unchanged

A receipt signed through any custody mode verifies identically — same algorithm, same envelope, same public key. Your auditors and counterparties use the same free verifier, and your key and its custody model are published in the Product Key Registry.
python3 algovoi_verify.py receipt receipt.txt public_key.b64   # passes regardless of custody

Availability

Bring-your-own-key custody is an Enterprise capability. The provider adapters (algovoi-doc-archive-kms, -vault, -pkcs11) are licensed packages; local custody is built in. Talk to us about a pilot: pilot@algovoi.co.uk.