DOCS / SOLANA PROGRAM INTEGRATION

Make your program the authority.

Keep application rules in your Solana program. Evaluate them off-chain or commit an approval when the decision must consume state.

Start with hosted signers

The program quickstart includes wallet controls, a deployed-example selector and a downloadable program with the complete typed ABI. You deploy application logic; PairKey operates the gateway and signers.

  1. Choose an authority PDA under your program. The example uses ["controller", owner_public_key].
  2. Obtain attested public namespace metadata. There is no namespace registration transaction.
  3. Create your application’s policy once, or explicitly edit it later.
  4. Choose a destination/path and prepare the exact message approval.
  5. Authenticate the caller, evaluate or commit approval, then submit the request and verify the result.

Use the supplied program

The self-contained template includes pairkey_approve, pairkey_approve_commit, owner-managed rules and the gateway ABI. Its authority remains stable when policy or approval mode changes. The Policy demo walks through both instructions and creation of their program-owned rules; the complete SDK example covers setup, signing and recovery.

Off-chain evaluation requires immutable deployed code. Use on-chain approval while developing upgradeable code. Removing an upgrade authority is irreversible; do not do it merely to try the quickstart.

Implement the read-only ABI

A Program source binds program, authority, PDA seeds, pinned code_hash, application accounts and mode. Every supplied state account must be validated for its expected owner and address. The example-source helper supports only its documented seeds and account layout.

pairkey_approve/v1\0 takes a Borsh Input from pairkey_types::program_approval. The input binds the authenticated caller, authority, exact account/path and payload, request digest, observed application state and validity. The program returns Output { input_digest, allowed }; the digest must match that exact input.

// Inside the program, after decoding Input and validating app accounts:
pair_registry::program_approval::authenticate_input(
    accounts, &input, &expected_enclave_set_address,
)?;
let allowed = policy.allows(input.caller, &input.account, &input.payload);
set_return_data(&borsh::to_vec(&Output {
    input_digest: input.digest(), allowed,
})?);

This is an excerpt; use the complete implementation for errors and account validation. The expected signer set must come from authenticated application policy, never from unchecked caller input. Accounts are the enclave set, instructions sysvar and ordered application accounts.

Each signer verifies the caller’s request signature, fetches finalized state, attests its own simulation input, and invokes RPC with native signature verification enabled. The program verifies that attestation using the instructions sysvar. Simulation signer flags alone cannot authenticate a wallet. The signer validates returned program ID, input digest and decision, then rechecks state before release. Standard RPC executes deployed code; it cannot load a policy program from an off-chain URL.

Commit a state-changing decision

pairkey_approve_commit/v1\0 decodes a CommitCall containing the namespace, exact approval request and committed payload. Validate the real transaction caller, application accounts and operation. Apply the state change and invoke the pinned gateway’s ApproveProgram with invoke_signed using your authority seeds.

The gateway records the request digest at ["program-approval", namespace_digest, request_id]. A failing CPI rolls back the application’s state change. Both signers require the finalized matching record. A wallet cannot substitute for PDA authorization, and a generic forwarding instruction must not bypass your application checks.

For a one-time governance decision, check that the proposal passed and its expected payload matches, then mark it used in the same transaction as approval. Read-only simulation can check “passed”; it cannot safely consume “unused.” For financial rules, parse and validate a typed transaction payload. Arbitrary text such as “send 1 ETH” does not enforce spending.

Recovery and verification

Save public recovery metadata before submission. After a timeout, recover the same job; do not automatically commit another allowance or request a fresh signature. A committed allowance may be consumed even if signing later fails. Recovery does not refund it.

The browser checks request context and performs local Ed25519 verification. The hosted Rust SDK checks all suites and signer receipts. For independent verification, save the original request, approval, key, result and trusted signer identities and run the offline verifier.

Manage authority and policy

Your program can manage optional namespace-specific PairKey rules through owner-authorized SetRules CPI. Its own application state can enforce additional rules and stricter change control. Program authorities differ from direct account authorities and derive different keys. Solana remains the control plane; Solidity contracts are not direct controllers in this implementation.

Simulation relies on RPC correctness and a state snapshot; it is not consensus approval and cannot make later cross-chain execution atomic. See policy guidance and security boundaries.

Legacy and self-hosted integrations

program_jobs.rs demonstrates the older registered-namespace, COMMIT and transfer-session coordinator path. It remains useful for compatibility and native-transfer tests, but it is not the hosted v2 onboarding flow. Internal interfaces are documented in the API reference.