How it works

Stillvault is a vendor-blind secret-release layer. The control plane routes requests and holds ciphertext; it never holds a key that can open them. Decryption happens on your enrolled phone, and the result is sealed directly to the requesting process — the vendor relay never sees plaintext.

End-to-end flow

When a consumer calls stillvault get <secret>, the local consumer wrapper checks its lease cache first. On a cache miss, the full approval loop runs:

stillvault get <secret>  →  consumer wrapper (Unix socket)
  ├─ lease valid in wrapper?  →  return plaintext. No network, no phone.
  └─ no lease:
     1. wrapper mints ephemeral keypair epk_c; signs
           { secret_id, epk_c.pub, nonce, ts }
        with the Org Identity key.
     2. → broker (managed cloud or self-hosted): looks up
           { secret_ciphertext, [wrapped_DEK per approver], policy }.
     3. broker → control-plane push → required approver phone(s).
        any-of-N: push to all enrolled approvers; first to approve wins.
     4. phone: verify Org-Identity sig over the request
           (→ epk_c is authentic, not vendor-swapped).
        Show trustworthy context:
              "Release <secret> to <consumer/exe@host>?"
        BIOMETRIC → PRF(salt) → K → unwrap wrapped_DEK → DEK
              → decrypt secret_ciphertext
              → plaintext → SEAL plaintext to epk_c.pub → return sealed blob.
     5. broker relays the sealed blob (cannot open it) → wrapper.
     6. wrapper unseals with epk_c private key → plaintext → consumer
        over Unix socket; cache plaintext for the lease TTL.
     7. lease expires in wrapper → plaintext wiped →
        next get repeats from step 1.

The only place plaintext ever appears is the consumer wrapper’s RAM at time of use, on the customer’s own machine.

Leases

Because the broker must never hold plaintext or DEKs, the lease is consumer-side: the wrapper caches the released plaintext (non-swappable RAM) for the lease TTL and serves repeat stillvault get calls locally — no network, no phone tap — until the TTL expires.

This is also the correct design for locality: plaintext lives only where it is used and is attributable per consumer process. The control plane enforces the maximum lease policy and logs each grant; it never sees the cached plaintext.

Lease length is the single per-secret policy knob. Range: “re-prompt every N minutes” to “approve once for this task.”

Multi-approver

Any-of-N (current): the same DEK is wrapped to each enrolled approver’s key. Any one approval releases the secret. Push fans out to all enrolled approvers; first to respond wins.

M-of-N threshold (format reserved): Shamir-split the DEK into N shares, wrap share_i to approver_i’s key. Need M sealed shares to reconstruct; reconstruction happens in the consumer wrapper so the broker still never sees a whole key. This defeats single-rogue-admin and single-compromised-phone — a genuine enterprise guarantee.

The store format carries a list of key-envelopes plus { policy, m, n } so any-of-N today and M-of-N later share the same schema.

Approval context

Once a server is compromised, the attacker is the broker and can trigger a push at will. The only thing between them and a secret is a human reading the phone. So the approval screen is a first-class security surface.

The phone shows attacker-uninfluenced context drawn from SO_PEERCRED on the Unix socket:

FieldSource
Which secretthe named secret label
Which consumer processSO_PEERCREDuid / pid / exe path
Which hosthostname of the broker/consumer machine
Whentimestamp of the request

The Org-Identity signature gates that the request is authentic and that epk_c was not vendor-substituted. Per-secret salts ensure one approval can only ever release the one named secret shown on the screen.