DOCS / POLICY & RULES
Make permission programmable.
Define who may sign and what they may sign. Both signers enforce current rules independently.
Authentication, policy and application logic
| Layer | Purpose | Where it lives |
|---|---|---|
| Exact-request authentication | Prove the authority authorized these bytes, key and expiry. | Wallet signature or bound passkey/OIDC proof, verified privately by each signer. |
| Standard rules | Restrict destination, path, payload or signing availability. | Optional compact Solana account, independently read by each signer. |
| Program rules | Apply business logic and, when needed, consume persistent state. | Your deployed program and authenticated application accounts. |
Browser restrictions are convenience checks. They are not a replacement for signer or program enforcement.
No registration or policy setup required to start
V2 establishes public namespace metadata without registering an account on-chain. With no saved rules, every message still requires exact-request authentication. A read error is never treated as “no policy.” Once you create rules, their current revision remains authoritative.
Save policy on Solana and approve ordinary messages off-chain. This avoids per-message authorization transaction fees and approval-account rent. There is still RPC, service and signing work. Fully off-chain policy storage is deferred: an owner signature alone cannot prove that a policy is the latest revision.
Who authorizes a policy edit?
| Source | Rule-edit authority | Per-message Solana approval transaction |
|---|---|---|
| Solana account | Account wallet signs the policy transaction | None for off-chain requests |
| Solana program | Program PDA authorizes a gateway CPI after application checks | Only for pairkey_approve_commit |
| Passkey | Fresh WebAuthn P-256 assertion; a separate wallet may pay fees | None for off-chain requests |
| OIDC | Fresh issuer token and bound key; both signers attest the update, with a separate fee payer | None for off-chain requests |
Creating or editing rules is a separate Solana transaction for every source. A new or enlarged rules account may require rent; same-size edits reuse it. Service, RPC and destination costs are separate from Solana authorization fees.
Account policy: create and edit
Your wallet authorizes edits with a Solana transaction signature. The policy address is derived from ["rules", namespace_digest] under the gateway. No prior namespace registration is required. Use the browser SDK from Quickstart: Account:
const current = await pairkey.rules(namespace);
const prepared = await pairkey.preparePolicy(namespace, {
revision: current.rules.revision + 1,
destinations: [key.account.destination],
path_prefixes: [[0]],
max_payload_bytes: 256,
payload_prefix: [],
disabled: false
});
await pairkey.setPolicy(namespace, wallet, prepared);Creating or enlarging rules can require rent funding. Same-size edits reuse the account’s deposit and pay only transaction fees. A policy update does not require threshold signing. The controller can change its own policy; use a program with stricter governance if the controller itself must be constrained.
Passkey and OIDC policy
Passkey: identify the namespace by the credential’s compressed P-256 public key, RP ID and origin. For ordinary signing, authenticate a bound ephemeral Ed25519 key. For a policy edit, request a fresh WebAuthn assertion directly over the policy update challenge. Solana’s native P-256 verification checks that owner authorization; no MPC signature or per-edit ephemeral-key registration is needed.
import { passkeyAssertion } from './hosted-v2.mjs';
const update = await pairkey.preparePolicy(namespace, nextRules);
const assertion = await passkeyAssertion(namespace.identity, update.challenge, credentialId);
await pairkey.setPolicy(namespace, feePayerWallet, update, { Passkey: assertion });A fee payer funds the transaction but gains no policy authority. This keeps passkey policy updates to one ordinary transaction and reuses the policy account. Setting rules is optional; it is not required before the first passkey-approved message.
OIDC: the issuer, audience and subject define the identity. Create the bound ephemeral key before preparing the update, and authenticate with its binding challenge as the OIDC nonce. The ephemeral key signs the exact update. Both signers independently verify the configured issuer’s ID token and attest the update; only those public attestations go on-chain.
import { ephemeralIdentity } from './hosted-v2.mjs';
const ephemeral = await ephemeralIdentity(namespace);
// Complete your configured OIDC provider's login using ephemeral.oidcNonce.
const update = await pairkey.preparePolicy(namespace, nextRules);
const authentication = await ephemeral.approvePolicy(update, idToken);
await pairkey.setPolicy(namespace, feePayerWallet, update, authentication);No provider is enabled implicitly. JWTs and passkey assertions go only to private authentication endpoints; they are not included in public receipts or persisted job records. See the identity flow.
Program policy: evaluate or consume
Your program owns its application rules. Its management instruction authenticates the owner or governance process, verifies state account ownership and PDAs, and updates a monotonic revision. The example stores one policy per owner under ["pairkey-policy", owner], shared by that owner’s namespaces under the program.
| Rule | Approval path |
|---|---|
| Allowed destination/path, membership, enabled flag, current application state | pairkey_approve: read-only RPC evaluation by each signer. |
| Consume one allowance, reserve cumulative budget, mark a proposal used | pairkey_approve_commit: change state and record exact approval atomically on Solana. |
Simulation does not persist writes or reserve state. The supplied example implements an approval-count budget, not transaction parsing or cumulative financial spending. If signing fails after a commit, an allowance is not automatically refunded.
Optional standard PairKey rules can apply in addition to application rules. The example’s owner-authorized SetRules CPI manages those namespace-specific restrictions. Custom programs must authenticate all management calls and validate the exact operation before gateway CPI. See program integration.
Standard rule fields
| Field | Meaning |
|---|---|
revision | Implicit default 0; first saved revision 1; every edit increments exactly once. |
destinations | Optional allowlist of destination digests. Null means unrestricted; an empty list allows none. |
path_prefixes | Optional allowed numeric prefixes. Null means unrestricted; an empty list allows none. |
max_payload_bytes | Maximum canonical committed payload size, at most 1,232 bytes. |
payload_prefix | Required byte prefix of that payload, at most 128 bytes. Empty imposes no prefix. |
disabled | Refuse signing while true. |
Solana and NEAR commit raw message bytes. EVM, Zcash and Bitcoin commit a serialized signing intent containing the key and typed message. A text prefix is therefore not a universal message filter across suites. Hosted raw messages are limited to 256 UTF-8 bytes independently of these policy bounds.
Update or stop permission
Read the current rules, choose the next revision and authorize the exact update. A stale edit is refused rather than silently rebased. Set disabled: true to stop new authorization; later re-enable through another authorized revision. There is no delete-to-default policy fallback.
Each signer checks fresh policy before signing and before releasing output. An observed newer policy fences stale prepared requests. These checks do not retract an issued signature or make destination execution atomic with a policy change. Keep destination nonces and replay rules in your application.
Sessions are deferred for v2
Bound ephemeral keys authenticate requests; they do not bypass policy or create a scoped session. The older native-transfer API retains its registered Policy, COMMIT, grant and revoke formats. It has different fields and supported payloads. New standard rules also constrain that legacy authorization path; switching endpoints does not remove them. An existing customized legacy policy needs explicit migration to v2 rules. See legacy sessions.
Put rules into your flow
See the Policy demo for sample pairkey_approve and pairkey_approve_commit instructions and how to create their program-owned rules. Continue with account rules, program rules or the hosted API.