ServicesVault

Multi-Signature Unlock

A multi_signature vault opens when a quorum of authorized signers has signed - no deadline involved. A hybrid vault combines both: it opens at unlock_at regardless, and a signature quorum can open it early.

How signing works

The signer allowlist and quorum are fixed at seal time in lock_config.early_unlock and are immutable afterwards. A signature is not something you compute: the server generates a deterministic HMAC signature for the authenticated caller, so authorization comes entirely from the allowlist (or platform admin). Each signer can sign exactly once, and every signature lands in the audit history as a witness_added event.

Signatures accumulate through two doors:

  • snug vault witness - records a signature, returns progress, never reveals.
  • snug vault unlock -u multi_signature -s <anything> - records the caller's signature (the payload just signals intent) and reveals in the same call if that signature completes the quorum.

Seal with a signer quorum

Signers are principal ids (the JWT subject). A 2-of-2 vault:

snug vault seal --content-type docs4-escrow \
  --data "$(echo -n 'strategy: rush B' | base64)" \
  --lock-type multi_signature --required-signatures 2 \
  --authorized-signers docs-wave,docs4-other

One holder share is minted per signer (2 here), so any single signer can later complete the key reconstruction with their own share.

Collect signatures

The first signer attempts an unlock directly. The quorum is not met, so the server records the signature and answers 202 with progress - captured live:

{
  "status": 202,
  "msg": "Accepted",
  "data": {
    "attestation_id": "vault_att_HZzAaVfGzRKcWUJJsRFH",
    "status": "partially_unlocked",
    "signatures_collected": 1,
    "signatures_required": 2,
    "message": "Additional signatures required for early unlock"
  }
}

The second signer can add theirs without attempting an unlock:

snug vault witness -a vault_att_HZzAaVfGzRKcWUJJsRFH -r "match ended"
{ "witness": "docs-wave", "signature_valid": true, "current": 2, "required": 2 }

Progress and the signer roster are visible to every authorized signer via snug vault status (unlock_progress.signers lists who signed and when).

Reveal

With the quorum met, any authorized signer unlocks by presenting a holder share:

snug vault unlock -a vault_att_HZzAaVfGzRKcWUJJsRFH -u multi_signature \
  -k "A3gHRJ1mvlOcR8583cKzPJvTDCRQ5BqWdsUQzhlsx28i"

The response is the same reveal shape as a time-lock unlock: the hex content_key, the sealed_blob to decrypt locally, and an unlock_proof whose witnesses array names every signer that contributed (["docs4-other", "docs-wave"] here, verified in the live capture).

Hybrid locks

Sealed with --lock-type hybrid --unlock-at <deadline> plus the same --required-signatures / --authorized-signers flags. Verified live with a 2030 deadline and a 1-of-2 quorum: a single unlock -u multi_signature -s intent -k <share> call revealed the content years before the deadline, with unlock_method: "multi_signature". Without signatures, a hybrid vault behaves exactly like a time lock and opens at the deadline.

Refusals

Each reproduced live:

  • A caller outside the allowlist gets 403 signer_not_authorized - from both witness and unlock -u multi_signature.
  • Signing twice gets 409 duplicate_signature ("Signature already recorded for signer: docs-wave").
  • witness on a vault sealed without early_unlock gets 403 unlock_conditions_not_met ("attestation does not support early unlock signatures").
  • Once the vault is unlocked, further witness signatures are refused; a repeat unlock call instead returns the reveal again with unlock_method: "already_unlocked".

One subtlety, also observed live: the quorum check and the key reconstruction are separate steps. If the final signature arrives with a bad or missing key share, the gate still opens (status flips to unlocked, the audit event is written) but the call fails with 400 invalid_share or 403 insufficient_shares - simply retry the unlock with a valid holder share to collect the reveal.

The exact request and response schemas are in the Vault API reference.

On this page