# RORApay security self-assessment — 12 October 2026

This is a closed testnet research prototype. No audit, certification, absolute security guarantee or mainnet readiness is claimed.

## Trust boundaries
- Owner authority: encrypted local test-owner keystore or the user's browser wallet. Never uploaded to the server or model. The local automated deployment identity is separate from personal wallets.
- Agent authority: one dedicated server key. Contract permissions constrain token, network, recipients, total budget, per-payment amount, expiry and operation count. Compromising the executor can misuse its remaining permission but cannot legitimately expand that permission.
- Invoice/model boundary: invoice text and model output are untrusted. Trusted structured owner destination and exact amount are compared independently before submission. The model cannot grant permission or provide arbitrary calldata.
- Web boundary: WebAuthn verifies RP ID, origin, one-time challenges and user verification. Bootstrap authorizes initial enrollment only, never payments. Sessions expire after 15 minutes and use HttpOnly, Secure, SameSite=Strict cookies. Non-GET requests require exact Origin and a custom same-origin header.
- Infrastructure boundary: non-root service, root-owned code, private writable data directory, systemd restrictions, SSH keys, firewall and private encrypted backups. Monitoring port 10050 is allowed only from Timeweb's configured monitoring addresses.

## Verified controls and evidence
- F01 — Prompt injection leading to a changed payment: strict proposal schema, deterministic explicit-intent comparison and independent vault constraints. The repeated Responses API benchmark contains 50 valid tasks and 200 generated adversarial invoice variants across 10 templates; all proposals preserve intended address/amount. This measures proposals, not 250 on-chain transactions.
- F02 — Permission bypass/replay/budget splitting: local EVM tests exercise direct calls, wrong agent, unauthorized grant/withdrawal, recipient epoch replacement, shared budget, per-payment cap, expiry, call count, replay and competing transactions. Ethereum Sepolia receipts independently confirm unauthorized recipient, per-payment cap and replay rejection, owner revocation, and rejection after revocation (evidence/public-controls.json).
- F03 — Unauthorized web payment: live HTTP checks reject unauthenticated access, bootstrap-as-payment authorization, cross-origin requests, forged passkey assertions and enrollment without the bootstrap secret. Real owner passkey enrollment and authenticated end-to-end execution require user participation.
- F04 — Uncertain transaction state: SQLite records intent and signed raw transaction before broadcast. Pending transactions are reconciled by receipt. No automatic replacement after timeout. A single executor instance and dedicated signer are required.
- F05 — Unsupported OS/exposed access: Ubuntu Pro ESM Apps/Infra and Livepatch enabled; kernel upgraded to 5.4.0-241 and reboot verified. SSH effective PasswordAuthentication=no, root key-only, no forwarding/X11. UFW allows 22/80/443 and monitoring allowlist. Fail2ban and unattended-upgrades active.
- F06 — Production dependency risk: clean runtime-only lock installs 105 packages and npm audit reports zero known advisories at check time. Development-only Ganache has known advisories; it is excluded from deployment.
- F07 — Secret disclosure: AI gets only structured instruction, invoice and public recipient addresses. No secret-printing endpoint or shell/browser/network tool exists. API keys, owner keystore and Android signing keystore are outside Git. Credentials in the server env and signed transaction journal remain sensitive.

## Limits and remaining work
- Contract is application-specific, unaudited and not a MetaMask Delegation integration.
- Public testnet transactions and real owner passkey enrollment/sign-in are verified; native screen QA remains blocked by capture errors. Full encrypted archive and current application snapshot were copied offsite and selected recovery verified; full VM boot recovery remains untested.
- Android uses device authentication as a gate to the web workspace; it does not prove hardware integrity to the backend. App Attest and Play Integrity are not integrated.
- Root/provider/account compromise, stolen synchronized passkeys, supply-chain compromise and denial of service are residual risks. Full server compromise may spend within the agent's remaining contract envelope.
- The execution gas reservation caps EVM execution fees, not Base L1 data fees. Keep the isolated test signer minimally funded; do not claim an exact full-fee budget.
- Application AI reservations are conservative cost controls, not a provider billing cap. Gateway availability can fail safely. Responses API fixed the observed Chat Completions reasoning/tools compatibility errors in the measured run.
- Public landing/reference-market screens do not make this a financial service. Trade calculations are hypothetical, and test tokens have no financial value.

## Sources used for implementation
[OWASP prompt injection prevention](https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html), [Timeweb AI Gateway](https://timeweb.cloud/docs/ai-agents/api-usage/ai-gateway), [OpenAI function tools](https://developers.openai.com/api/docs/guides/tools), [Ubuntu ESM](https://ubuntu.com/security/esm), [Apple App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity), [Google Play Integrity](https://developer.android.com/google/play/integrity/overview).

## Additional user controls
Permission rehearsal is authenticated and read-only; it does not reserve funds or approve a future execution. Original-intent verification requires a successful receipt, configured chain, signer, vault event and exact original request/recipient/amount. It trusts the configured RPC and does not establish merchant identity/delivery. Exact nonblank invoice fingerprints prevent another pending/confirmed backend payment across request IDs; modified documents and direct agent-key calls are outside this deduplication scope. Private dashboard data is cleared from the current UI after logout/session absence.

## Domain isolation
Credentials, registration/login challenges and hashed sessions carry an exact configured origin. The backend resolves a permitted Host and requires its matching Origin for mutations. Legacy rows are migrated to the legacy origin, not shared with the new domain. New-domain passkey enrollment and sign-in were completed by the owner. Public competition pages, market quotes and test-vault status grant no payment authority.

## Release 0.2.0 — account, native session and judge walkthrough
Native apps can request a five-minute connection handshake with a random proof key. The owner signs in with a passkey on the configured HTTPS origin and explicitly approves a matching six-character code. A one-use claim proves possession of the key; only its hash is on the server. Issued session secrets stay in native process memory and grant GET-only account/history/permission/receipt access. They cannot call payment or owner-mutation endpoints. Expiry is bounded by the parent browser session (15 minutes); browser logout invalidates them. Native logout revokes its session, and an app restart clears local access.
The account endpoint reads the configured vault owner's actual test ETH and rUSD wallet balance, separately from agent-vault funds. This remains a single-owner competition prototype; public multiuser onboarding is not implemented. Native clients show connection state, expiry and explicit network/expired-session errors. History contains amount, asset, recipient, UTC timestamp, status, explorer link and original-intent proof.
Public /demo.html is an interactive synthetic walkthrough requiring no credentials or AI calls, clearly distinguished from real execution evidence. China probe evidence is in evidence/china-connectivity.json; it does not establish passkey availability or nationwide connectivity. A development-signed IPA is kept locally outside the public bundle because its embedded provisioning profile contains registered-device identifiers.
