Search docs

Find a documentation page

Threat model

Assets, adversaries, attacks and their implemented mitigations, with residual risks stated honestly.

The canonical security home is DESIGN.md §7 in the marketplace repo (PRD §9.9 is a summary that points there). This page is the public form of that section. Every mitigation below cites the file that enforces it: paths under backend/ live in the marketplace repo, paths under enclave/, host-proxy/, jwt/, cosign/, and deploy/ live in the open-source provider repo github.com/nodexpert-labs/staked-quic-connection-provider. Items that are designed but not yet shipped are explicitly labeled planned at the bottom.

Assets

AssetWhere it livesWorst-case impact if compromised
K1, validator identity key (Ed25519)Plaintext only in Nitro Enclave memory. At rest, a KMS-encrypted envelope installed as a mode-600 file on the EC2 instance in the validator's own AWS account (deploy/scripts/deploy.sh)Full validator impersonation: votes, transactions, slashing-adjacent damage. Protecting K1 is the design's first priority
K2, marketplace JWT signing key (ECC P-256 in AWS KMS, used via kms:Sign, never exported)Marketplace AWS account (backend/crates/jwt/src/sign.rs)Attacker mints valid lease JWTs, worst case bounded by the signing oracle (see below)
Lease JWTs (bearer tokens, default TTL 600 s)Trader infrastructure, in flight on gRPC metadataRate-limited TLS handshakes against one specific TEE until exp
Payment fundsThe trader's own wallet until the atomic 90/10 transaction lands on-chainLease activated without full payment, or one payment settling two leases
Signing-path availabilityhost-proxy + enclave on the validator's EC2 instanceTrader loses staked connections mid-lease (no contractual SLA in v1, see residual risks)
Release artifacts (EIF, host-proxy binary)GitHub Releases of the provider repoBackdoored binary running next to K1

Adversaries

  • JWT thief: a network attacker or compromised trader box holding a stolen lease token.
  • Malicious or fully compromised marketplace: the marketplace itself turning hostile, including its K2 key.
  • Compromised host: root on the validator's EC2 instance, outside the enclave.
  • Unauthenticated flooder: anyone who can reach the TEE endpoint on 443.
  • Payment fraudster: a trader trying to activate a lease without paying the full amount, or replaying a payment.
  • Supply-chain attacker: someone substituting or forging release artifacts.

Out of scope: the validator operator attacking themselves (they own the AWS account and the key, the model protects them rather than defending against them), and Solana protocol-level attacks.

Attacks vs mitigations

AttackMitigations (file that enforces each)Honest worst case
Stolen JWT replayaud claim must equal the TEE's own endpoint URL, mismatch is PERMISSION_DENIED (host-proxy/src/jwt.rs, enforced claims in jwt/src/types.rs). TTL default 600 s, hard cap 3600 s, 0 rejected (backend/crates/api/src/config.rs:200-205). Per-lease quota bucket keyed on lease_id, default 100 handshakes/min, JWT quota claim overrides it (host-proxy/src/rate_limiter.rs:51-66)Quota-limited handshakes against exactly one TEE for at most the remaining TTL. Never a transaction or vote
Malicious marketplace mints JWTs at willThe enclave's signing oracle refuses everything that is not a TLS 1.3 client CertificateVerify: payload length in , first 64 bytes all 0x20, context string "TLS 1.3, client CertificateVerify\0", mirroring Firedancer's fd_keyguard_payload_matches_tls_cv (enclave/src/signer.rs:11-49). The marketplace never holds K1Unauthorized handshakes on listed validators for the JWT TTL window. The marketplace can sell access it should not, it can never sign a transaction, vote, or arbitrary message
Compromised host instanceThe enclave has no network and no persistent disk, it is reachable only over vsock (enclave/src/kms_vsock.rs). The key envelope is useless to the host: KMS Decrypt is gated on the Nitro attestation document, key-policy conditions kms:RecipientAttestation:PCR0/PCR1/PCR2 (deploy/terraform/kms.tf:46-56), so a tampered enclave image gets AccessDeniedA root attacker can talk vsock directly and obtain client-CV signatures (the enclave trusts its host for transport, DESIGN.md TB6), and can drop traffic. It can never extract K1 or obtain a non-handshake signature
Key extractionK1 plaintext exists only inside enclave memory, decrypted at boot via KMS Decrypt with an attestation Recipient (RSAES_OAEP_SHA_256), SigV4 over TLS over vsock (enclave/src/kms_vsock.rs:21-30). At rest there is no copy outside the KMS-encrypted envelope on the instance itself (/etc/staked-quic/seed.bin, mode 600, fed to the enclave via host-proxy/src/bin/provision.rs --seed-file). The seed is zeroized after key construction (enclave/src/signer.rs)A PCR mismatch means the enclave refuses to start rather than running with a key it cannot prove it should hold
Payment fraud (short payment, memo reuse, one tx for two leases)Settlement is the sole confirmation path. It matches the memo, then verifies via RPC getTransaction at finalized that both transfer legs credited at least their frozen share, a short leg is AmountMismatch and the lease stays unpaid (backend/crates/settlement/src/reconciler.rs:122-168). payment.memo is UNIQUE and onchain_tx_signature is partially UNIQUE, so one on-chain transaction settles at most one lease (DESIGN.md §4.2.4bis)An attacker paying in full activates the lease they paid for, nothing else
Late-payment race (pay after the booking expired, slot already resold)POST /v1/solana-pay/{memo} returns a uniform 404 for unknown, expired, and already-paid memos, so no transaction can even be built for a dead booking (backend/crates/api/src/routes/solana_pay.rs:213-220). The pending-payment window is 15 minutes, the expiry sweep runs every 60 sA payment that bypasses the endpoint and lands after expiry is a settlement no-match, refunded manually (DESIGN.md §4.2.1, see residual risks)
Unauthenticated floodPer-IP fallback limiter hardcoded at 100 req/min, burst 20, LRU-capped at 100k buckets so an IP spray cannot exhaust memory (host-proxy/src/rate_limiter.rs:14-19). Release builds fail closed when the transport exposes no peer address (host-proxy/src/grpc_service.rs). gRPC decode cap 4096 bytes, sign payload cap 162 bytes, JWT token cap 8192 bytesVolumetric DoS at the network layer remains possible (the planned CDN/WAF layer addresses it, see below)
Compromised or vulnerable releaseKill switch: the host-proxy polls revocation.json plus a detached K5 Ed25519 signature from the provider repo's GitHub Releases every 60 s (host-proxy/src/revocation.rs:36), verified over the exact asset bytes with verify_strict (cosign/src/lib.rs). The revocation flag latches and never clears, so replaying an older signed list cannot un-revoke a node (host-proxy/src/revocation.rs). Boot fails closed: a revoked version or an unfetchable list is exit(1) (host-proxy/src/main.rs:99-111). A revoked node answers FAILED_PRECONDITION "version_revoked" on every RPC (host-proxy/src/grpc_service.rs:79-84)Propagation is bounded by the 60 s poll. Note the K5 release key is currently a placeholder pending the production key ceremony (cosign/src/lib.rs:16-21)
JWT forgery or malleabilityES256 via kms:Sign (KMS has no Ed25519), DER-to-JOSE conversion with low-S normalization so a signature cannot be reshaped into a second valid token (backend/crates/jwt/src/sign.rs:123-125). Verification is kid-based against a static trust map in the host config, rotation is add-key-and-restart (host-proxy/src/jwt.rs:1-3). Disabling K2 in KMS stops all new minting, outstanding tokens die at exp, at most 10 minutes on the default TTLSee the key-rotation runbook for the compromise procedure
Shared co-tenant starvationToken issuance stamps shared leases with max(1, floor(budget / max_concurrent_shared_leases)) handshakes/min instead of the full budget, and the host-proxy keys its limiter on lease_id so each seat gets its own bucket (backend/crates/api/src/routes/token.rs:268-285, DESIGN.md §5.3.2)Post-handshake stream allocation is not isolated, see residual risks

The request pipeline

Every SignCertificateVerify call walks these gates in order (host-proxy/src/grpc_service.rs:125-156). Order matters: the kill switch stops a revoked binary even on un-rate-limited bursts, and the per-IP gate keeps a flood of bad JWTs from exercising the verifier.

  1. Kill-switch check (revoked version: FAILED_PRECONDITION "version_revoked")
  2. Per-IP fallback limiter (100/min, burst 20, hardcoded)
  3. JWT verification (enforced claims: aud, val_pk, lease_id, exp, nbf, quota only, everything else the marketplace mints is deliberately ignored, jwt/src/types.rs)
  4. Per-lease quota bucket (JWT quota override, quota 0 denies outright)
  5. 162-byte payload cap before anything crosses vsock
  6. The enclave matcher itself (enclave/src/signer.rs)

Residual risks and accepted trade-offs

These are known, documented, and accepted for v1. They are listed so you do not have to discover them yourself.

  • Leader-side stream dilution (shared mode). A shared lease bounds co-tenants only at the handshake layer (the per-seat quota). Once connections are open, stream allocation across them is the Solana leader's call, not the TEE's (DESIGN.md §2, item 4). This is a performance trade-off shared traders accept for the lower price, not a security issue. Related: there is no contractual SLA in v1, the SDK hard-fails after UNAVAILABLE x3 with no auto-credit (DESIGN.md OQ9).
  • Manual refunds. Prorated refunds (revoked lease, admin takedown, late payment) are debited from the platform's SOL buffer funded by the 10% fee leg and triggered by ops within 24 h. There is no refund smart contract (DESIGN.md §4.2.4bis, PRD §9.3).
  • vsock is cleartext by design between host and enclave, protected only by Nitro hypervisor isolation (DESIGN.md TB6). Combined with the enclave trusting its host for transport, this is why host compromise yields unauthorized handshakes (and only handshakes).
  • The transcript hash is not validated. The matcher checks structure, not the 32/48/64-byte hash content (enclave/src/signer.rs:38). The signature is still only ever valid as a TLS 1.3 client CertificateVerify, so this widens nothing beyond handshakes.
  • Nitro side channels are an accepted v1 residual: ed25519-dalek is constant-time and Nitro provides no cross-VM cache sharing (DESIGN.md §7.9).

Planned, not yet implemented

Honest status of designed-but-unshipped defenses. Do not assume these are live.

  • CDN/WAF in front of the TEE (FR-T11): the current Terraform exposes a plain security-group allowlist via a single allowed_grpc_cidr variable (deploy/terraform/security.tf:6-11). No Cloudflare module exists yet.
  • Host-proxy binary self-verification at boot (FR-T12): not implemented in host-proxy/src/main.rs.
  • Enclave-side K4-signed audit log (DESIGN.md §7.7.1): not implemented. Marketplace-side audit rows do exist (a jwt_issuance row per token, audit_log on revoke).
  • KMS/IAM policy drift detection: the dashboard field kms_policy_drift is a placeholder that always reports false.
  • Production K5 release key: the current key is a placeholder whose private half was destroyed, the production key ceremony is pending (cosign/src/lib.rs:16-21).
  • FIDO2 2FA is available as a Clerk policy but not enforced in code.
  • External audit, penetration test, and a paid bug bounty are pre-GA checklist items (DESIGN.md §7.10), none is live today.

Dev-only escape hatches (STAKED_QUIC_DEV_BYPASS_MANIFEST_SIG, the dev-plaintext gRPC feature) are compiled out of release builds (cosign/src/lib.rs:37-46, host-proxy/src/main.rs:182-196). They are documented behavior, not vulnerabilities, unless you can reach one in a release build, which would itself be a critical finding.

See also