VRP Threat Model (v0.1)
A short, STRIDE-oriented threat model for the core Vacation Rental Protocol. It covers the four things that decide whether an AI agent can trust a stay offer:
- Ed25519 signing of verified stay offers,
- the JWKS that publishes the verification keys,
- host-domain control as the source of truth, and
- what happens when a host signing key is compromised.
It does not model OTA, marketplace, ranking, or payment-processor scenarios — VRP has none of those (see ADR 0002). The normative rules referenced here live in spec §3–§9; this document only makes the trust boundaries and attacker goals explicit.
Trust root
The root of trust is domain control (did:web): a VRP node's signing keys are whatever the host-owned domain serves at its JWKS. There is no central issuer, registry, or accreditation authority to appeal to. Consequently:
- A valid signature proves the offer came from whoever controls the host domain.
- If the domain itself is taken over (DNS, TLS, or hosting compromise), the attacker *is* the source of truth as far as the protocol can tell. Domain-level security is out of scope for VRP and is the operator's responsibility (spec §3.1).
Assets
| Asset | Why it matters |
|---|---|
| Host private signing key | Whoever holds it can mint offers that verify as official. |
JWKS (/.well-known/jwks.json) | The *only* revocation authority — a key is valid exactly while it is present here. |
Discovery document (/.well-known/vacation-rental.json) | Points agents at the JWKS and the offer endpoint. |
| Signed stay offer | Carries the quoteable facts (price, availability, valid_until, booking URL). |
Actors
- Host / operator — controls the domain and the signing key; the legitimate signer.
- Verifying agent — an AI agent or client that fetches, verifies, and quotes offers. Must fail closed (spec §8).
- Network attacker — can intercept or modify traffic that is not integrity-protected.
- Key thief — has obtained the host's private signing key but does *not* control the domain.
- Domain attacker — controls what the host domain serves (out of scope, listed for completeness).
STRIDE
Spoofing — "pretend to be the host"
- *Threat:* an attacker serves a forged offer or an unsigned "offer" and hopes the agent quotes it as official.
- *Mitigation:* every quoteable fact is inside a compact Ed25519 JWS whose key must be present in the host domain's JWKS. An agent MUST verify the signature against the JWKS and MUST NOT quote anything unsigned or unverifiable (spec §5, §7, §8). Fetching the discovery document and JWKS over HTTPS from the host domain binds identity to domain control.
- *Residual risk:* a domain attacker can publish their own keys and sign freely — no key-level control detects this. Out of scope (domain security is the operator's).
Tampering — "change price, dates, or the booking URL"
- *Threat:* alter the offer in transit — bump the price, extend
valid_until, or swap thedirect_booking_urlfor an attacker-controlled destination. - *Mitigation:* integrity comes solely from the JWS over the payload; any change invalidates the signature. The
direct_booking_urlis a signed field, and an agent MUST reject a booking URL whose host is not the offer'scanonical_domain(spec §5.1). There is no separate, spoofable side-channel for these facts.
Repudiation — "deny having offered these terms"
- *Threat:* a host later disputes which price or refund terms were in force at quote time.
- *Mitigation:* the signed payload binds the terms (including
rules.refund_schedule) to a specific key and moment; a verifier can prove after the fact exactly what was signed. A node MAY additionally record key events in an append-only transparency log for a tamper-evident history of which key was valid when.
Information disclosure — "leak the key or private data"
- *Threat:* exposure of the private signing key is the critical loss (see *Compromised host key* below). VRP offers themselves carry public offer facts, not secrets.
- *Mitigation:* only public keys are ever published (in the JWKS). Key custody is declared via
operator.key_custody(selforplatform) so an agent knows what a valid signature guarantees (spec §2). Protecting the private key at rest is the operator's responsibility; where to keep it and how to confirm it is not served are in KEY_ROTATION.md ("What an implementer must do").
Denial of service — "make verification fail or flood the node"
- *Threat:* make the JWKS or offer endpoint unreachable, or drive verifiers into excessive JWKS fetches using offers with unknown
kids. - *Mitigation:* unreachable is treated as Unknown, never as a valid offer — the agent fails closed rather than quoting stale data (spec §8, §9). Verifiers SHOULD rate-limit or coalesce refresh-on-miss per domain to avoid amplification (spec §3.1). The JWKS is a small, CDN-cacheable document. Availability of a given node's infrastructure is out of scope for the protocol.
Elevation of privilege — "get a bad offer treated as official"
- *Threat:* make a stale, unavailable, or unsigned value cross the line into "official host-domain verified offer."
- *Mitigation:* the Safe-to-Quote gate (spec §7) requires every condition it lists, among them: the signature verifies; the offer names the domain it was fetched from, a host-owned domain (§2); it is for the dates and guest count the agent asked for, with real dates and a
check_inthat has not passed;valid_untilis fresh and at most 900 seconds after agenerated_atthat is not in the future;availability.availableandprice.exactare true; the direct booking URL is a node URL; and the host grants explicit agent permission. A value the node echoes from the request is never the host's statement (§5). Three-state verification (spec §9) forbids inferring Affirmed from Unknown. A lower verifiability class must never be presented as a higher one (spec §5.4).
Central scenario: compromised host key
An attacker who steals the private signing key (but does not control the domain) can mint fresh, well-formed offers with a future valid_until — so freshness alone does not stop them. The only thing that de-trusts them is the key being absent from the JWKS the verifier holds. This drives two protocol controls:
- The compromised key MUST be removed from the JWKS immediately. Its presence is the entire trust signal; removing it makes every offer signed with it unverifiable, including the operator's own legitimate ones — this is intentional, because a verifier cannot tell the attacker's signatures from the host's.
- A conforming verifier MUST NOT cache a JWKS for longer than 5 minutes, so a removed key is fully de-trusted within that window. This bound is a conformance requirement, not something VRP can force on a non-conforming verifier (spec §3.1).
Handling is detailed in KEY_ROTATION.md and INCIDENT_RESPONSE.md.
Explicitly out of scope
- Domain compromise (DNS / TLS / hosting takeover) — the attacker becomes the source of truth; no key-level mechanism can detect it.
- OTA / marketplace / ranking / payment-processor attack scenarios — VRP is none of these.
- The HemmaBo product and any specific host's server-side application.