Soloraa
Security

Guarantees that map to code.

Every constraint Soloraa enforces is a specific check in the Anchor program with a typed error code. The list below is the whole list. If a property isn't on it, the program doesn't check it.

Canonical signed intent

The 169 bytes the chain verifies. Every field is a single invariant on the agent's behavior.

byte 0SOLORA_INTENT_V2 · 169 bytesbyte 168
SOLORA_INTENT_V2
Program ID
Wallet PDA
Nonce
Expiry slot
Recent blockhash
Blockhash slot
Kind
Payload hash

Active checks

IntentNonceMismatch · 6018

Replay protection

Every signed intent commits to wallet.nonce. The on-chain verifier reads the current nonce, compares, and bumps it on success. A resubmit with a fresh blockhash and the same signed bytes lands on the program — and is rejected. Solana's own dedup catches naïve resubmits; Soloraa catches the sophisticated ones.

programs/solora/src/verify.rs:78
BlockhashMismatch · 6033

Fork-resistant binding

Signed messages bind a (recent_blockhash, slot) pair. The verifier loads the SysvarS1otHashes account, binary-searches for the signed slot, and compares hashes byte-for-byte. Replays across forks or skipped slots miss the entry.

programs/solora/src/verify.rs:142
EnclaveSignerMismatch · 6017

Enclave signature

An Ed25519Program instruction must immediately precede every execute call. The verifier parses that instruction's payload, extracts the signing key, and rejects unless it equals wallet.enclave_signer.

programs/solora/src/verify.rs:52
AttestationMeasurementMismatch · 6045

Attested rotation

register_enclave_v2 requires both the wallet authority's signature AND a governor proof citing a measurement currently in the on-chain registry. Neither side alone can rotate the signer.

programs/solora/src/lib.rs:431
TargetProgramNotAllowed · 6027

CPI allowlist

Even a perfectly-signed intent cannot CPI into a program not in wallet.policy.allowed_programs[16]. The list is authority-controlled and capped at 16 — every entry is an explicit user decision.

programs/solora/src/lib.rs:312
Architecture

Move the security boundary into Solana.

The agent never holds a private key. A confidential-compute enclave holds a signing key sealed to its image hash and runs the policy. The Solana program is the verifier — every constraint is checked against the bytes of the signed intent.

01

AI agent

submits an intent

destination · amount · feed id · slippage cap

02

Attested enclave

policy + oracle + sign

Pyth merkle · Wormhole quorum · sealed Ed25519 key

03

Signed intent

169 canonical bytes

program · wallet · nonce · blockhash · payload hash

04

On-chain verifier

re-checks every byte

Ed25519 sysvar · SlotHashes · nonce bump

Trust model in one paragraph

A user's wallet can only be drained by Ed25519-signed intents from wallet.enclave_signer. That signer is rotated only via register_enclave_v2, which requires the wallet authority's signature and a governor-signed proof citing a measurement that's currently in the on-chain registry as active. Compromise of the agent, relayer, or governor alone cannot move funds. The architecture supports multisig governor and recursive attestation.

programs/solora/src/verify.rsrust
// every execute_* instruction passes through this gate.
pub fn verify_enclave_intent(
    instructions_sysvar: &AccountInfo,
    slot_hashes_sysvar: &AccountInfo,
    program_id: &Pubkey,
    wallet_pda: &Pubkey,
    enclave_signer: &Pubkey,
    nonce: u64,
    kind: IntentKind,
    payload_hash: &[u8; 32],
) -> Result<()> {
    // ① Ed25519Program ix immediately precedes us
    let ed_ix = ed25519_ix_at(instructions_sysvar, current_ix - 1)?;
    // ② signing key equals wallet.enclave_signer
    require!(ed_ix.pubkey == *enclave_signer, EnclaveSignerMismatch);
    // ③ 169 bytes parse cleanly into SOLORA_INTENT_V2
    let msg = parse_intent_v2(ed_ix.message)?;
    // ④ bindings: program_id, wallet_pda, nonce, kind, payload_hash
    require!(msg.program_id == *program_id, IntentProgramMismatch);
    require!(msg.wallet_pda == *wallet_pda, IntentWalletMismatch);
    require!(msg.nonce == nonce, IntentNonceMismatch);
    require!(msg.kind == kind, IntentKindMismatch);
    require!(msg.payload_hash == *payload_hash, IntentPayloadMismatch);
    // ⑤ slot hashes match
    verify_blockhash_binding(slot_hashes_sysvar, &msg)?;
    Ok(())
}