Threat model

What can break, what cannot, what is currently out of scope.

Cannot break

These are guaranteed by the program logic.

Admin cannot move user funds

The vault PDA's authority is the room PDA. The room PDA's authority is the program. The program has no instruction that lets the admin (or the program's own upgrade key) move a vault to anywhere except room.action_destination, and only after the trigger condition truths.

The upgrade key can change the program's behavior in future versions, but it cannot retroactively drain existing rooms without a code change that users would see in source.

Trigger cannot fire early

The threshold comparison happens on-chain against either:

  • A Pyth PriceUpdateV2 account whose ownership and feed_id are verified, with a 90-second staleness window, or
  • The current Clock slot.

There is no off-chain trust path. A malicious responder cannot fake a price.

Disarmed rooms cannot be triggered

Once status is DISARMED or EVACUATED or EXPIRED, every other instruction checks status and rejects with RoomNotArmed. The state machine is one-way.

Destination is immutable after arm

action_destination is set at create_safe_room time and never written again. Even the owner cannot redirect a live room.

Can break

These are real risks. Mitigations exist but are not zero-effort.

Pyth Hermes outage

The trigger flow requires a fresh price update from Hermes. If Hermes is down, no one can post a PriceUpdateV2 account, and the trigger cannot fire even if the threshold is breached.

Mitigation: the owner can always disarm, recovering the vault. The vault is never bricked.

Future mitigation: the responder bot can run its own Hermes mirror and post updates from a backup source.

Responder griefing

The responder pays for the Pyth update posting (~0.001 SOL) and the trigger transaction. A malicious responder could spam stale-update posts to waste their own SOL — but that costs them, not you. Stale data is rejected on-chain.

A more interesting case: the responder posts a real update but does not bundle the trigger instruction. The price is on-chain, but no evac happens. Solution: the SDK bundles post + trigger in one transaction by default. If you write your own responder, do the same.

Network congestion

Solana priority fees can rise. The SDK sets computeUnitPriceMicroLamports = 50_000 for trigger bundles. In extreme congestion, this may not be enough.

Mitigation: the responder can re-send with higher fees. The trigger is idempotent: it either lands or it doesn't.

Single upgrade key

The program upgrade authority is currently a single keypair. A compromised upgrade key could deploy a malicious version that drains vaults.

Mitigation roadmap: transition to a Squads multisig before mainnet. The keypair lives on a separate machine in the meantime.

No audit

The program has 13 unit tests and a live e2e integration. No third-party audit. Do not put production money in a room.

This is loudly disclosed on the site. The beta cap (0.5 SOL per room) exists for this reason.

Out of scope

These are not threats the program tries to address.

  • Oracle manipulation of Pyth itself. If Pyth publishes a bad price, the program will trust it. Pyth has its own committee and aggregator protections. The program's only oracle assumption is that the canonical Pyth Receiver program ID hasn't been compromised.
  • User wallet compromise. If your private key is leaked, the attacker can disarm your rooms and drain the refund to your own wallet, which they now control. The program assumes wallet custody is your problem.
  • Application-layer mistakes. If you set a SOL price threshold of $5 instead of $245, your trigger will never fire under any realistic market condition. The program has no opinion about what threshold is sane.

Audit posture

Targeted scope for a future audit:

  1. Borsh layout of SafeRoom and Registry accounts.
  2. PDA seed derivation correctness (no canonical-bump confusion).
  3. Pyth PriceUpdateV2 parsing offsets and feed_id verification.
  4. CPI signing patterns for the vault drain.
  5. Reentrancy on trigger_safe_room if the destination is itself a program with a custom transfer hook.

The SDK is also in scope: discriminator collisions, instruction encoding bugs, and the Hermes-to-on-chain bundle integrity.