# Delegated signing

[Ephemeral signers](/wallets/ephemeral-signers/overview) let scheduled jobs, trading agents, and other server-side processes sign when the user is away. The user authorizes specific wallets and can revoke the signer. Provisioning does not give your backend the wallet's private key.

## Authorization to provision a signer

Provisioning requires an authenticated wallet session and an `ephemeral_signer_provision` presence token. An eligible [identity-backed session](/security/identity-backed-encryption) can authorize provisioning, re-provisioning, and revocation without email OTP or a verified email address.

The SDK prefers an enrolled passkey. If the assertion fails or the user cancels it, the SDK can fall back to an eligible identity-backed session. Passkey-only users still need a passkey assertion.

Session-based presence does not provide a separate user confirmation. Account or wallet-session compromise can therefore grant an attacker the ability to create delegated signing authority. Existing passkey wraps do not disable this identity-backed route.

See [Set up an identity-backed signer](/wallets/ephemeral-signers/identity-backed-setup) for the React setup flow.

## Provisioning is attested end to end

The user provisions an agent from the browser, and the browser verifies what it is sealing key material to before anything leaves the page:

* The MoonPay Wallets iframe fetches the pinned attestation policy: the expected enclave measurements (Platform Configuration Register (PCR) values) and the root certificate authority for the attestation chain.
* It then requests an attestation document bound to a fresh nonce and verifies the signature chain against the pinned root and the measurements against the pinned policy. If verification fails, nothing is provisioned.
* Only after verification does the browser generate a fresh Ed25519 keypair for the agent and seal the wallet key shares to the enclave's attested public key using Hybrid Public Key Encryption (HPKE) with X25519 key agreement, HKDF-SHA256, and ChaCha20-Poly1305.
* The signing policy, the authorized wallets, their allowed derivation paths, and an optional expiry, is bound into the sealed payload as authenticated data, so the policy cannot be swapped after the fact.

The result: the wallet key shares are readable only inside the exact enclave image the user's browser verified, not by your servers and not by the regular MoonPay Wallets infrastructure.

## Using an agent requires two credentials

Signing as an agent requires both your app's secret key and a request signed with the agent's private key. The enclave verifies the agent signature and evaluates the request against the provisioned policy before signing, and MoonPay Wallets independently checks that the agent belongs to the calling app.

* A leaked app secret key alone cannot forge signatures; it lacks the agent key.
* A leaked agent key alone cannot reach the signing endpoint; it lacks the app secret key.
* Requests outside the provisioned policy, such as an unauthorized wallet or derivation path, or past the expiry, are refused inside the enclave.
* Signed requests are timestamped, and the enclave refuses stale ones, so a captured request cannot be replayed later.

## Revocation and account state

* Revocation requires its own `ephemeral_signer_revoke` presence token, authorized by a passkey or eligible identity-backed session. The signer then refuses further requests from that agent.
* Delegated signing honors the wallet owner's account state: a compliance-blocked user's wallets cannot sign through an agent either. See [Compliance and data handling](/security/compliance-and-data-handling).

## Related

<CardGroup cols={2}>
  <Card title="Ephemeral Signers" icon="robot" href="/wallets/ephemeral-signers/overview">
    Provisioning, the REST API, and integration patterns.
  </Card>

  <Card title="Sessions and presence" icon="shield-check" href="/security/sessions-and-presence">
    The presence checks that gate provisioning and revocation.
  </Card>
</CardGroup>
