> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parmanasystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Signing more than 4 KB with a cloud key: the commitment scheme

> How a 4096 byte limit in AWS KMS broke live refunds, and how Parmana signs larger records with a fixed 97 byte commitment that stays ordinary Ed25519 and cannot be used to downgrade small messages.

On 2026-09-20, a refund request to Parmana's production server came back with a plain `500 Internal Server Error`. It
was a synthetic test refund, not a customer's ₹15,000, but the cause was real. Every refund would have failed the same
way.

A few days earlier I had moved the signing key into AWS KMS. That was the right move: the private key is created inside
KMS and never leaves it, so nobody can copy it, me included. The server sends KMS the bytes to sign and gets a signature
back. The tests passed. Then a full refund ran through production, and KMS refused to sign.

## The limit nobody hit in testing

KMS refuses a raw Ed25519 message longer than 4096 bytes. The error said so plainly: the message must have length less
than or equal to 4096.

An Execution Trust Record is not small. It carries the request, the decision, the signed authorization it was released
under, what the connector reported and a reference to the approved policy. A full record is well over 4 KB. The earlier
KMS tests had only signed a small test record, so the limit was never reached until real traffic reached it.

Worse, the failure happened after the refund connector had already been called. An action ran, and the signed record of
it could not be made. That ordering problem was recorded as a separate gap, and closed the next day with a signed
Execution Intent stored before release. This essay is about the signing.

## Options that looked easier

The decision is written down in ADR-0010, including what was rejected:

* **Use the pre-hashed variant of Ed25519, which KMS supports for any size.** Its signatures are a different scheme, and
  every existing Ed25519 verifier, including those used by third parties, would reject them.
* **Sign Trust Records with a local key, and keep KMS for small things.** Simple, and it puts the key that signs the
  durable evidence back on a disk, which is what the move to KMS was meant to end.
* **Make the record smaller.** Connector evidence and request parameters have no fixed size. A record can always grow
  past the limit.
* **Store a marker on each record saying how it was signed.** That changes the record format, every verifier's field
  mapping and the story for old records.

## The idea: sign a short, unambiguous summary

If the message is too long, sign a fixed size commitment to it instead. In Parmana it is 97 bytes: a fixed text prefix
naming the scheme and its version, a single zero byte, and the SHA-512 digest of the whole message. Change any byte of
the message and the digest changes, so the signature no longer verifies.

The signature over those 97 bytes is an ordinary Ed25519 signature. Any Ed25519 library can check it, once it rebuilds
the commitment.

## The rules that make it safe

* **Length decides, nothing else.** A message of 4096 bytes or fewer is signed raw, exactly as before. Only a longer
  message signed through KMS uses the commitment. No marker is stored, so no record changed shape and every signature
  made before the change still verifies.
* **No downgrade.** A verifier accepts a raw signature for any message, and additionally the commitment form only for a
  message over 4096 bytes. A commitment signature over a small message is refused. Tests cover the exact boundary, 4096
  bytes and 4097 bytes.
* **No confusion between forms.** The prefix includes a zero byte, and the canonical JSON Parmana signs always begins
  with an opening brace, so a raw message can never look like a commitment.
* **Same key, same message.** Accepting both forms for a large message is needed because records signed earlier with
  local key files were signed raw. Both forms are made by the same key over the same content, so accepting either does
  not let an attacker forge anything new.

## What happened next

The fix went to production the same day. A live refund returned `200` with a Trust Record whose canonical form was 4965
bytes. It did not verify as a raw signature and did verify as the commitment, and the offline verifier accepted it
against the public key the live server publishes. Later a repeatable check signed messages of 300, 4096, 5000 and 60,000
bytes through the real key, confirmed that a signature over other data failed, and confirmed that KMS still refuses 5000
raw bytes. That check ran with a limited AWS user from a developer machine.

## What it does not do

* **A verifier must know the rule.** The TypeScript packages, the Python SDK and `@parmana/sign` implement it. A verifier
  you write yourself must accept the commitment form for messages over 4096 bytes, or it will reject large records
  signed under KMS. The rule is fully specified in ADR-0010.
* **It only makes signing possible.** It does not decide when signing happens. That is the job of the readiness check
  and the Execution Intent.
* **It is Ed25519 under AWS KMS only.** KMS signing is Ed25519 only. No other key store is implemented, and hybrid
  signing does not run with KMS at all.
* **Not every path has been seen live.** The check above used a local AWS login, not the role production uses. The
  extra cost is small, one SHA-512 and at most one extra verification, but it was not measured on its own.

## The takeaway

A limit you have not hit is a limit you have not tested. The answer was not a new algorithm but a smaller, unambiguous
thing to sign, with two rules: only for large messages, and never accepted for small ones.

**Read more:** [AWS KMS signing](/deployment/aws-kms-signing),
[Cryptography handbook](/handbook/03-cryptography),
[Choosing signatures](/essays/19-choosing-signatures).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.