---
title: Passkey Manager Program
description: Understand the on-chain program behind passkey-backed
  authorization, account state, events, and instruction flow.
source_url:
  html: https://thru.org/docs/core-programs/passkey-manager-program/
  md: https://thru.org/docs/core-programs/passkey-manager-program.md
---

# Passkey Manager Program

The Passkey Manager Program is the core authorization program for passkey-backed flows on Thru.

From a developer perspective, the key idea is that the passkey does not sign the outer Thru transaction directly. Instead, the program verifies a WebAuthn signature over a challenge built from the wallet nonce, the ordered account list, and the trailing instruction bytes that the program action is authorizing.

## Use This When

- you need passkey-backed authorization for a transfer, CPI flow, or other nonce-bound program action
- you need to reason about authority records, credential lookup accounts, or nonce advancement
- you are integrating WebAuthn or native passkey signatures into a Thru transaction flow

## Quickstart

Fetch the ABI, generate TypeScript builders, then build instruction bytes and send the outer transaction.

```bash
thru abi account get --include-data --out ./passkey-manager.abi.yaml <ABI_ACCOUNT_ADDRESS>

thru abi codegen \
  --files ./passkey-manager.abi.yaml \
  --language typescript \
  --output ./generated
```

```ts
import { decodeAddress } from "@thru/sdk/helpers";
import type { Thru } from "@thru/sdk/client";
import {
  PasskeyInstruction,
  TransferArgs,
} from "./generated/thru/program/passkey_manager/types";

const instructionData = PasskeyInstruction.builder()
  .payload()
  .select("transfer")
  .writePayload(
    TransferArgs.builder()
      .set_wallet_account_idx(2)
      .set_to_account_idx(3)
      .set_amount(1_000_000)
  )
  .finish()
  .build();

const tx = await thru.transactions.build({
  feePayer: { publicKey: decodeAddress(feePayerAddress) },
  program: "taUDdQyFxvM5i0HFRkEK3W45kWLyblAHSnMg4zplgUnz6Z",
  accounts: {
    readWrite: [walletAddress, destinationAddress],
    readOnly: [],
  },
  instructionData,
});

const signedWire = await signTransaction(tx.toWireForSigning());
const signature = await thru.transactions.send(signedWire);
```

> **Tip:**
>
> Real user-facing passkey flows usually prepend a generated `validate` instruction before the trailing program action. If you want the full validate-and-send path instead of raw generated builders, use `@thru/programs/passkey-manager`.

> **Caution:**
>
> The passkey proof and the outer transaction signature are different layers. A successful `validate` step authorizes the wallet action inside the program, but the outer transaction still needs to be built and submitted normally.

## How It Works

Most passkey-managed flows follow this pattern:

1. fetch the nonce from the on-chain wallet account
2. build the trailing instruction payload you want the program to authorize
3. hash `nonce || ordered account addresses || trailing instruction bytes`
4. sign that challenge with WebAuthn
5. encode `validate`
6. concatenate `validate` with the trailing instruction payload
7. submit the resulting transaction against the Passkey Manager Program

This validate-then-execute model works whether the caller is a first-party wallet, a custom web app, or a mobile/backend integration.

## Account Model

| Account | What it stores | Notes |
| - | - | - |
| `WalletAccount` | Wallet header with `num_auth` and `nonce` | In practice, wallet data also includes trailing 65-byte authority records after the fixed header. |
| `CredentialLookup` | Wallet pubkey for a registered credential | Useful when a passkey needs a lookup account for registration or recovery flows. |

### Authority Records

Wallet authority entries are 65-byte tagged records:

- `tag = 1`: passkey authority, stored as P-256 `x[32] + y[32]`
- `tag = 2`: pubkey authority, stored as `pubkey[32] + padding[32]`

## Required Accounts

Passkey-manager instructions rely on stable account indices:

- the fee payer is index `0`
- the Passkey Manager Program is index `1`
- the wallet account must appear as a non-fee-payer account in the transaction
- trailing instructions reference accounts by their position in the final ordered account list

If you are using the Web SDK, `buildAccountContext(...)` handles this ordering and index lookup.

## Events

| Event | What it means |
| - | - |
| `wallet_created` | A new passkey-managed wallet account was created. |
| `wallet_validated` | A `validate` step succeeded and advanced the wallet nonce. |
| `wallet_transfer` | The wallet transferred balance to another account. |
| `credential_registered` | A credential lookup account was registered for the wallet. |

## Instructions

| Instruction | Use it when | Notes |
| - | - | - |
| `create` | Create a new passkey-managed wallet | Includes the initial authority and a state proof for the new wallet account. |
| `validate` | Submit a WebAuthn proof for the trailing wallet action | Carries `r`, `s`, `authenticatorData`, and `clientDataJSON`. |
| `transfer` | Move native balance from the managed wallet | Usually follows `validate` in the same transaction payload. |
| `invoke` | Have the managed wallet call another program | This is the main path for wallet-controlled CPI flows. |
| `add_authority` | Add another passkey or pubkey authority | Useful for multi-authority or recovery flows. |
| `remove_authority` | Remove an existing authority by index | Use with care because authority ordering matters. |
| `register_credential` | Create a credential lookup account | Used when you want a stable on-chain mapping for a credential. |

## Integration Surfaces

The program stands on its own, but most developers will interact with it through one of these integration patterns:

### Web

- Use `@thru/programs/passkey-manager` when your app needs to build `validate`, `transfer`, `invoke`, or authority-management instructions directly.
- Use [`@thru/passkey`](https://thru.org/docs/sdks/web-packages/passkey.md) when you need the browser WebAuthn registration and signing layer that feeds signatures into passkey-manager transactions.
- Embedded-wallet integrations typically use the wallet or provider layer for the outer transaction while relying on passkey-manager for the inner authorization payload.

### Mobile

- Mobile apps can use native passkey surfaces to register a credential and produce the WebAuthn proof locally.
- A common mobile pattern is challenge-submit: fetch or construct the passkey-manager challenge, sign it on-device, then send the signature components and WebAuthn payload back to a backend or transaction service that assembles the final transaction.

## Related References

- [`@thru/programs`](https://thru.org/docs/sdks/web-packages/programs.md)
- [`@thru/passkey`](https://thru.org/docs/sdks/web-packages/passkey.md)
- [Cross Program Invocation](https://thru.org/docs/program-development/cross-program-invocation.md)

## On-Chain Link

- Program explorer: [scan.thru.org/address/taUDdQyFxvM5i0HFRkEK3W45kWLyblAHSnMg4zplgUnz6Z](https://scan.thru.org/address/taUDdQyFxvM5i0HFRkEK3W45kWLyblAHSnMg4zplgUnz6Z)
