Vendor keys
The first policy is the only one that pack will ever sign
Creating a vendor key is the one step in minidauth that costs money and cannot be undone. The first policy you sign on that key is the only policy its authorizer pack will ever sign, and the pack is spent whether or not the signature succeeded. Two paid keys went in the bin before we understood the second half of that sentence.
What the pack is
When a vendor key is finalised, the network mints an authorizer pack for it. The pack is what
lets a brand new key do anything at all: there are no policies yet, so there is nothing else
that could authorise a signing request. It binds the vendor rotating key point that was
active at the moment of finalisation, and it authorises a Policy:1 sign.
It authorises exactly one of them. When a node processes a request carrying that pack it revokes its own entry as it goes, deliberately, so that everything afterwards has to run through the policy the request just deployed. As a design that makes sense, and the trouble is only that the revocation is unconditional in ways which are not obvious from outside.
Three ways to lose a key
1. Scope the first policy too narrowly
The pack signs attestation units as well as policies, and every token your users get is built
from attestation units. So the policy that replaces the pack has to cover both
AttestationUnit:1 and Policy:1, for example with the
any wildcard. Miss one side and the key survives in a shape that is no use:
- A policy over
AttestationUnit:1alone keeps tokens working and can never deploy anything again. - A policy over
PolicyEnabledEncryption:1alone ends policy deployment on that key permanently, and there is no route back.
You cannot fix this later by deploying something better. A deployed policy can be rotated in place, but the replacement has to carry the same key id, the same contract and the same models, so rotation is a lever for changing parameters and expiry, not for narrowing or widening reach. Whatever the first policy can authorise, it can authorise for the life of the key.
2. Retry
This is the one that cost us a key. The revocation is per node, and it happens when that node processes the request, not when the cohort produces a signature. A first attempt that misses the threshold for any transient reason, a few nodes slow or unreachable, a tunnel blip, still burns every node that did respond. The retry then burns more of the pool that is still live, and the attempt after that burns more, until fewer nodes hold a valid entry than the threshold needs and the sign is permanently impossible.
Nothing local tells you this is happening. The policy list stays empty the whole time,
because no policy ever got deployed, which looks exactly like the failure you are retrying.
On one key the fan-out ended at nine nodes answering and eleven returning
This authorizer has not been allowed, against a threshold of
fourteen. The key was dead and the console still showed an empty policy list and a plausible
error.
Never retry a first policy deployment. Check the whole cohort is reachable before the one attempt you get. A missed threshold on the first try already means minting a new key.
3. Send something only some of the nodes reject
The second key died differently and more instructively. The policy referenced a contract, and
we had left uploadContract out of the spec, so the contract source never
travelled with the request. Two of the nodes validate the contract and rejected it with
Policy referenced a contract which doesn't exist. The other twelve
do not validate it, co-signed happily, and revoked their entries on the way past.
Twelve is below the threshold of fourteen, so no policy was deployed, and the pack was spent regardless: the next attempt came back with eight nodes still holding a valid entry and twelve reporting it revoked. A request that is malformed in a way only part of the cohort checks for is still a request the rest of the cohort will burn itself on.
The rotation trap, for completeness
There is a fourth way, and it does not involve deploying anything. The pack binds the vendor rotating key point that was live at finalisation, and a rotation is not re-issued into it. So rotating before the first policy is signed leaves you signing with a key the pack does not recognise, and the node says:
VRK:1: VRK Authorization Flow failed because VRK signature of this request could not be verified.
The message is accurate and says nothing whatsoever about rotation, which cost us an afternoon of looking in the wrong place. There is a cheap way to detect it in advance: both the pack and the active vendor rotating key carry a 32 byte Ed25519 point, and those two points are identical until a rotation happens, so comparing them before you send anything tells you whether the attempt can work at all. Signing the first policy is a capability you have before your first rotation and not after it.
What to do instead
-
Rehearse on a key you do not mind losing. A free tier key on the live
network is created with
{"licensingTier":"free","email":"..."}and finalises immediately with no payment step. It is not limited in the ways that matter here: policy deployment, voucher decryption and the whole tideless flow work on it. The paid tier buys licensing and quota, not capability. Run your exact policy spec through a free tier key first, and only then spend a paid one. -
Make the first request byte complete. For a bootstrap covering
AttestationUnit:1andPolicy:1over a contract registered from source, that means"uploadContract": truein the spec so the source ships with the request. -
Set the policy version the network speaks. The bindings build version 4
and the released network understands 3, so
MC_POLICY_VERSION=3. Without it the deployment fails withError with signing model: Policy:1, and the actual reason is in a response body the bindings discard. - Check the cohort is up. Ask every node in the cohort whether it is active before you send the one request that matters.
-
Grant somebody the admin role first. minidauth refuses the first policy
deployment until a person holds
governance-admin, because there is no way back from deploying with nobody able to govern the result.
Why it is like this
All four failures come from the same property, which is that there is no administrator anywhere who can undo a decision the network has already made, including on our behalf. That property is the entire reason to use any of this: a key that can be rescued by an appeal to a support desk is also a key that can be taken by anyone who can convince a support desk.
What it costs is that the first few minutes of a key's life carry more weight than everything that follows, and the tooling does not yet make that obvious enough to save you. Read Running minidauth end to end before your first call, not after it.