ML-DSA Public Key Bytes
The half of the keypair you're allowed to hand out
- public_bytes
- private_key
- public_bytes
- public_key
A signature is only interesting if someone else can check it. That someone only needs your public key, and this node is how you get one out of a workflow in a form you can paste into a Discord message, a text file, or the metadata of a puzzle image.
It's the mirror of ML-DSA Private Key Bytes, and the two outputs are public_bytes (the serialized key) and public_key (the key object on a KEYOBJ wire) - which is exactly what ML-DSA Message Verification takes.
Where the public key comes from
key_source is a boolean that's doing more work than its label suggests. On, the label reads From Key Object: wire a KEYOBJ into the optional private_key input and the node calls public_key() on it to derive the matching public half. That's the normal path - generate a private key, derive the public one, done.
Off, the label reads From Public Bytes, and you feed the optional public_bytes input instead. This is the verifier's path: you received a raw ML-DSA public key and you need to load it.
Except here's the thing the field tooltip sort of gestures at and doesn't quite say. The input is raw key bytes, not PEM text. The node hands whatever arrives straight to the library's from_public_bytes(), which expects the bare encoded key of the exact size FIPS 204 defines - 1312 bytes for ML-DSA-44, 1952 for -65, 2592 for -87. If you took a PEM file, stripped the header lines with your eyes, and fed the base64 across, it will not work. The tooltip's advice to "refer to documentation for exact byte size for each algorithm" is the author telling you this input is fussy about length.
That's also why encoding and formatting only affect the output: PEM or DER, SubjectPublicKeyInfo or Raw. PEM + SPKI is the portable choice, and it's what you'd hand to any other ML-DSA tool. The library's own from_public_bytes only accepts the raw thing, so the node can write PEM but can't read it back - worth knowing before you plan a round trip.
sig_alg has to match, publicly
sig_alg (ML-DSA-44/65/87) must be the same parameter set the signature was made with. There's no negotiation and no auto-detect. Wire an ML-DSA-65 public key into a verification node set to ML-DSA-87 and you don't get "wrong algorithm", you get a failed load or a false verdict - same class of silent mismatch you hit when a checkpoint and its VAE come from different families, except here the failure is just a false.
So the parameter set belongs in the same sentence as the key, like a filename. If you're publishing a puzzle, publish "ML-DSA-65 public key" alongside it, because the bytes don't announce it.
Installing it
Manager → search ComfyUI ARG Toolkit → install → restart → refresh the browser. Manual install is the standard two commands:
cd ComfyUI/custom_nodes
git clone https://github.com/AzelusLightvale/ComfyUI-ARG-Toolkit
pip install -r requirements.txt
This node's whole existence depends on cryptography being new enough to have the mldsa module - the pack's requirements pin 50.0.2, so an environment where pip resolved an old cryptography elsewhere is the first thing to check if the Modern/Asymmetric category greys out or the import throws. Be aware the requirements file also pins torch and torchaudio, which this pack has no use for and which can absolutely disturb a working CUDA install if you let pip re-resolve them. ComfyUI's ecosystem has no dependency isolation - every pack's requirements land in the same interpreter - so installing into a live environment is always mildly rude. Manager does the same thing, but at least it does it consistently with the rest of your install.
How people actually use it
Two patterns. In a signing workflow you barely look at this node: private key object in, public key object out to the signature step's counterpart, and the serialized bytes travel with the artefact you're publishing. In a verification workflow it's the front door - key_source off, the published bytes wired in, public_key out to ML-DSA Message Verification.
The failure mode worth pre-empting: keep the bytes somewhere. A KEYOBJ wire lives for as long as the workflow runs, and nothing in this pack persists a key to disk. If all you have is the object and the workflow closes, the public key is gone - which is fine when you can regenerate it from the private key and not fine at all when the whole point was that the verifier never gets the private key.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| sig_alg | COMBO | ML-DSA-65 | The algorithm to use for the public key. |
| key_source | BOOLEAN | true | The source to load the public key. |
| encoding | COMBO | PEM | 3 options: PEM, DER, Raw |
| formatting | COMBO | SubjectPublicKeyInfo | 2 options: SubjectPublicKeyInfo, Raw |
| public_bytesopt | BYTESLIKE | Only applicable if key_source is `From Public Bytes`. Refer to documentation for exact byte size for each algorithm. | |
| private_keyopt | KEYOBJ | Only applicable is key_source is `From Key Object`. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| public_bytes | BYTESLIKE | — |
| public_key | KEYOBJ | — |