# Security model

> LOPW's security model: what is protected, trust assumptions, the threats and their mitigations, residual risks, and the known limits accepted in version 1.

*LOPW documentation · Deep dive.* Canonical page: https://lopw.com/docs/deep-dive/security.html · This Markdown twin: https://lopw.com/docs/deep-dive/security.md · Index of all pages: https://lopw.com/llms.txt · Everything in one file: https://lopw.com/llms-full.txt

Scope: the version 1 Stellar binding, which is the Bond Registry, Claims and validator contracts, the seller middleware, the buyer SDK, and the off-chain indexer and API. Nothing here is a promise that the system is unbreakable; it is a list of what was considered, what was done about it and what remains.

## What is protected

| Asset | Where it lives | Who must not be able to take it |
| --- | --- | --- |
| Seller stake (USDC) | Bond Registry | Anyone except the seller after unbonding, and buyers with a proven claim |
| Buyer payouts | Paid from stake on a proven claim | Anyone other than the buyer named in the receipt |
| Service signing keys | Seller’s server | Attackers, who could sign receipts the seller is liable for |
| Seller account keys | Seller | Attackers, who could withdraw stake after the delay |
| Admin key | Protocol operator | Attackers, who could rewire which contracts may slash |
| Integrity of claim records | Claims contract | Anyone trying to be paid twice for one payment |

## Trust assumptions

| Trusted | For | If wrong |
| --- | --- | --- |
| Consensus and the contract host | Correct execution, finality | Everything fails; out of scope |
| The SEP-40 oracle named in the terms | P6 truth | Wrong P6 outcomes (known limit) |
| The x402 facilitator | Settling the buyer’s payment | Payment fails or is not settled; LOPW does not escrow, so no stake is at risk |
| The admin key | Initial wiring and time-locked changes | Bounded by the 26-hour time lock |
| The seller’s own reports for P3 to P5 | `data_as_of`, `status`, `response_bytes` | A consistently lying seller is not caught by P3 to P5 |

Not trusted: buyers, claim submitters, sellers beyond what they sign, and the API and indexer, which are a convenience only; the chain is the source of truth, and the SDKs read it directly for every payment decision.

## Threats and mitigations

| # | Threat | Mitigation | Residual risk |
| --- | --- | --- | --- |
| T1 | Replay a paid claim with the same or a different receipt | One paid claim per `(service_id, payment_reference)`, stored on-chain | None known |
| T2 | Cross-network replay (a test-network receipt on the production network) | Network ID in every signed hash | None |
| T3 | Cross-deployment replay against another registry | Registry address in every signed hash; Claims checks `offer.registry` | None |
| T4 | Forged receipt | Ed25519 signature by the service key registered on-chain | Key theft (T6) |
| T5 | Receipt for an offer the buyer never saw | Claims requires `receipt.offer_hash == hash(offer)` and a valid offer signature | Not slashable, by design |
| T6 | Signing key stolen | `rotate_key` retires the old key; receipts with `served_at` after retirement fail with `KeyRetired` | Receipts forged before rotation, within the claim window, bounded by `max_claim` each and the slash rate limit |
| T7 | Seller self-drain: invented payments to its own addresses to pull stake out ahead of honest claims | Slash rate limit: at most 50% of epoch-start stake per epoch (epoch = unbonding delay); every payout capped by `max_claim` | A seller can still pay itself up to the cap; it gains nothing over a normal withdrawal, and honest claims compete in the same epoch |
| T8 | Seller exits before claims land | Unbonding delay exceeds the longest claim window of any terms version ever used; stake stays slashable while unbonding | None within the claim window |
| T9 | Admin rewires slashing (malicious Claims contract or validator) | First setting immediate; any later change time-locked 26 hours, longer than any unbonding delay; changes are on-chain events | Sellers must watch for `ClaimsChangeProposed` / `ValidatorChangeProposed` |
| T10 | Contract upgrade abuse | Contracts have no upgrade function; changes require a new deployment | None |
| T11 | Oracle manipulation or outage | P6 compares against one past tick, read on-chain; missing data is `NotVerifiable` (no payout, no penalty); the seller chooses its tolerance | The oracle itself is trusted |
| T12 | Off-tick oracle timestamp to dodge P6 | The validator uses the tick the oracle returns and checks the age against it | None |
| T13 | Rounding to dodge P6 | Exact integer comparison with no division | None |
| T14 | Front-running a claim | Anyone may submit, but the payout always goes to `receipt.buyer` | None |
| T15 | Claim spam or griefing | The submitter pays fees; rejected claims change no state except an event | Event noise only |
| T16 | Buyer pays a suspended or under-staked seller | Middleware stops offering when not Active; the SDK reads live state, stake and terms before paying and aborts on policy failure | A race of seconds between read and payment |
| T17 | Over-subscription: the seller serves more calls than its stake covers | Stake and `max_claim` are public before payment; agents set `minStake` / `minMaxClaim` | Later claims may find stake exhausted |
| T18 | State expiry of services, terms, keys or claim records | Persistent storage with lifetime extended on every access; claim windows are far shorter | Long-idle services may need a lifetime extension or restore |
| T19 | Indexer or API compromise or lag | Read-only, rebuilt from chain events; SDKs read the chain directly for every payment decision | Misleading dashboards, no fund risk |
| T20 | API abuse | Rate limits, pagination caps, no write endpoints | Denial of service of a convenience service |

## Known limits accepted in version 1

- **P3 to P5 rest on seller-reported fields.** They catch a seller that contradicts itself or admits a breach in its own signed receipt, not one that lies consistently. P6 is the only predicate checked against evidence the seller does not control.
- **Payment is not verified on-chain.** The seller’s signature over `payment_reference` and `buyer` is its admission; T7 bounds the abuse. A facilitator-signed settlement attestation would close this in a later version.
- **Missing receipts and bad signatures are not slashable.** The buyer SDK reports them as untrusted responses.
- **The oracle is trusted.** A wrong observation produces a wrong claim outcome.
- **A single admin key on the first deployment.** Before a production launch: a multisig admin account, and consideration of making the wiring immutable once stable.
- **Receipts are public once claimed.** Claim records and events reveal the buyer address and payment reference.
- **A seller that serves far more calls than its stake covers** leaves later claims unpaid. Buyers see the stake before paying and decide.

## Before any production deployment

1. Independent security audit of all contracts.
2. Admin moved to a multisig account; time lock reviewed.
3. Fuzz and property tests on stake accounting (deposit, slash, withdraw invariants); a property test already checks that the contract’s token balance equals the sum of stakes after random sequences.
4. Monitoring alerts live: admin-change proposals, self-drain patterns, oracle freshness, suspensions, indexer lag, storage lifetimes.
5. Review storage lifetime defaults against service lifetimes.
6. A guarded launch with low caps and an allowlist, loosened through the time lock after the audit.
