BookSaver is a self-hosted Python agent that watches refundable Booking.com hotel reservations for a cheaper equivalent room. It re-runs the search with the original dates and occupancy and alerts the right user. Reservation changes happen independently in Booking.com; BookSaver never cancels, books, pays, or runs a rebooking workflow.
Warning
BookSaver is experimental personal-use software, not a hosted service. Booking.com UI changes, account state, and datacenter-IP controls can break live checks even when the test suite passes. Automated access may violate Booking.com's terms. Read the full disclaimer and validate the tool with your own refundable booking before relying on it.
See the story index for delivery and qualification status and the decision index for accepted architecture. The implementation includes:
- bounded Browser Use price/inventory execution and an explicit deterministic/Stagehand rollback;
- same-property, dates, occupancy, room-type, currency, and refundability checks;
- local SQLite history, redacted traces, and savings alerts;
- automatic authenticated-account reservation discovery with visible eligibility reasons;
- a private Telegram interface with owner-issued, single-use invites and per-user isolation;
- encrypted, user-scoped Booking.com sessions and optional personal Anthropic keys;
- Docker/systemd deployment and an opt-in HTTPS
/connectlogin flow for a trusted VPS; - a noVNC-compatible, deny-by-default login viewer with native mobile-keyboard controls;
- immediate same-user
/connectrecovery after a viewer is closed or abandoned; - complete user purge across SQLite and encrypted sessions, plus Booking.com-only login navigation.
Use the issue tracker for current open work. Qualification and live acceptance remain explicit gates in the lifecycle artifacts.
- You connect your Booking.com account; BookSaver synchronizes positively observed reservations, preserves unseen saved rows when a refresh is partial or fails, and uses only current-run, upcoming, refundable observations for monitoring.
- Three times per UTC day by default, BookSaver gives each user one randomized slot in a different
broad part of the day. At each slot it synchronizes that user's account once, then opens a fresh
authenticated mobile Chromium context for every eligible booking.
/checknowremains available for an immediate check. - Price routing defaults to the
legacydeterministic journey.owner_canary, explicitconsented_users, or qualifiedagenticrouting admits the pinned local Browser Use classic agent in a fresh local Chromium profile. Every physical action is code-authorized; reservation, checkout, payment, cancellation, credential, MFA/captcha, arbitrary navigation, shell, clipboard, upload, and download capabilities are absent. Stagehand remains an explicit rollback adapter for a future job, never an automatic second attempt. - Only a cheaper, currency-aligned, still-refundable equivalent offer becomes a savings result.
- Telegram or email reports the result. You independently review and make any change in Booking.com; the next synchronization observes the updated account state.
Every check is locally traceable with booksaver checks trace <CHECK_ID>.
Agentic price traces include a versioned price_validation event for successful and rejected
observations. It records query/terminal rejection, all failed per-offer evidence checks, actual
room-match/selection outcomes, and bounded lexical room hints. Inspect offers[].selection for
room mismatch or other exclusions, and offers[].evidence_rejections for missing/conflicting
all-in, refundability, completeness, or currency evidence. Up to 20 offers are detailed; the total
and omitted counts are explicit. Room hints are recognized words, not semantic equivalence
claims; missing words mean unmentioned, not absent. Raw room labels, page text, screenshots,
refundability prose, and secrets are not added to these diagnostics. This instrumentation does
not change which offers are accepted or invoke additional models.
When guarded recovery assists account discovery, the local log includes a synchronization run ID;
inspect its content-free provider/call/token/action/timing audit with
booksaver bookings trace <SYNC_RUN_ID>.
Randomized slots are persisted locally, so restarts do not reroll or replay completed work. Send
/status to see your own next planned UTC slot.
The [agent] section uses a fixed Sonnet 5 primary and one measured Opus 5 escalation. Fable and
arbitrary model IDs are rejected. Model calls share persisted USD 1 per-browser-job and USD 10 per
deployment UTC-day ceilings; predictable outcomes such as a confirmed reconnect requirement use no
model call. The section also keeps the existing outer per-check limits (max_steps,
max_llm_calls, and check_timeout_seconds) and tighter recovery defaults:
primary_model = "claude-sonnet-5"
escalation_model = "claude-opus-5"
max_job_cost_usd = "1.00"
max_deployment_daily_cost_usd = "10.00"
reserve_opus_diagnostic_for_ambiguous_episode = true
max_recovery_calls_per_step = 4
recovery_timeout_seconds = 60
screenshot_after_no_progress = 2
max_semantic_action_executions = 2Older config files remain valid and receive these defaults automatically. The inner limits cannot expand the outer per-check or per-user daily LLM budgets.
Inventory and price use independent browser-executor routes. Agentic inventory is the default; the agentic price executor remains disabled by default:
[agentic_browser]
routing = "legacy" # price: legacy | owner_canary | consented_users | agentic
price_executor = "browser_use" # agentic price adapter: browser_use | stagehand
inventory_routing = "agentic" # inventory: legacy | agentic
disclosure_version = "anthropic-visible-booking-page-v1"inventory_routing = "agentic" uses the pinned Browser Use classic agent for every read-only
inventory trigger, including /bookings, /checknow, scheduled checks, and post-connect sync. It
runs locally in a fresh browser, receives only a closed set of code-guarded read-only actions, and
uses the deployment's Anthropic key. BookSaver accepts only positively observed reservations from
that run and never lets model output mark an unseen saved reservation absent. Set it to legacy as a
capability-specific rollback; this setting does not promote the price executor.
The Docker build installs the exact resolved runtime graph from requirements.lock; this is
intentional because Browser Use 0.11.13 has broad transitive dependency ranges. Telemetry, cloud
sync, external version checks, downloads, persistent screenshots, and stock Browser Use actions are
disabled by the BookSaver adapter.
For price execution, owner_canary routes only the deployment owner. An owner may explicitly select
consented_users to route the owner and every active invitee with current /connect disclosure
consent through Browser Use before statistical qualification completes. This choice does not mark
the canary qualified; qualification remains useful for monitoring and rollback. The default adapter
runs Browser Use 0.11.13 in-process against the installed
Playwright Chromium, injects encrypted Booking.com cookies through a code-owned local CDP
connection, and sends the exact transient browser's semantic view and screenshots to Sonnet 5 using
BOOKSAVER_LLM_API_KEY. BookSaver constructs the trusted search URL, removes Browser Use's stock
actions, exposes only guarded read-only clicks/scrolls/keys/waits and typed submissions, and proves
authentication independently. USD 1 per-check, USD 10 deployment-day, 15-action, six visual-click,
and 180-second limits remain hard. Set price_executor = "stagehand" only to roll a future job back
to the prior Stagehand semantic/computer-use adapter.
The executor returns observations only. BookSaver still verifies property, dates, occupancy,
authentication, Genius evidence, currency, all-in totals, explicit refundability, room equivalence,
and the cheapest valid offer before any savings alert. Agentic failure is terminal for that check;
the legacy browser is a configured rollback route, not an automatic second attempt. Invited users
always require current versioned /connect disclosure consent. They may be admitted either by the
explicit consented_users route or by the statistically qualified agentic route.
All Browser Use inventory and price calls use the deployment owner's
BOOKSAVER_LLM_API_KEY, including calls initiated by invitees. /admin users shows that funding
policy and only whether an optional personal key is configured for legacy LLM work; it never prints,
decrypts, fingerprints, or validates a key.
The owner governs that release entirely from the local VPS:
booksaver agentic status
booksaver agentic compare <CHECK_ID> --correct # or --incorrect
booksaver agentic promote
booksaver agentic regress <MACHINE_CODE>Promotion reads the persisted evidence itself and cannot be supplied a fabricated verdict. It
requires 30 owner checks over at least 14 days, ten correct manual comparisons, at least 95% valid
eligible observations, average cost at most USD 0.10, p95 cost at most USD 0.50, p95 duration at
most 180 seconds, fallback use at most 20%, zero critical violations, and explicit owner execution
of promote. During the 30-day rollback window, a critical violation or three consecutive eligible
failures automatically regresses routing to legacy. Offline fixtures qualify the adapter and
safety boundary; they do not substitute for this live owner evidence.
Model behavior can be measured without opening Booking.com or reading local sessions/database. The explicit qualification command replays the packaged synthetic corpus ten times per fixture for both approved profiles and requires at least nine correct runs per fixture with zero prohibited executions. It reports aggregate correctness, safety, calls, actions, latency, token use, and exact estimated cost without printing prompts or page content. Live execution requires an explicit cost cap and is admitted call-by-call under the same deployment ceiling:
booksaver evaluate recovery --live --qualify --persist --max-cost-usd 10.00
booksaver evaluate qualificationIt requires BOOKSAVER_LLM_API_KEY. Custom fixtures are exploratory only and cannot create a
recordable production qualification result.
If adaptive assistance indicates likely DOM drift, BookSaver correlates a content-free maintenance
incident and alerts only the configured owner. Inspect encrypted local evidence with
booksaver incidents list and booksaver incidents inspect <INCIDENT_ID>; bundles expire after
seven days and are never sent through Telegram.
- a Linux host with Docker Compose v2, 2 GB RAM minimum, and a DNS name if
/connectis enabled; - a private Telegram bot token from BotFather and your numeric Telegram chat ID;
- an Anthropic API key for Browser Use price/inventory execution, Stagehand rollback routes, and legacy LLM extraction/recovery (without one, use only the configured scripted routes);
- acceptance of the trust boundary: the VPS runs the temporary login browser and must be under your control.
git clone https://github.com/romkagribok/booksaver-agent.git
cd booksaver-agent
cp .env.example .env
cp config.toml.example config.toml
chmod 600 .env config.tomlFill in .env, set owner_chat_id in config.toml, and set the same HTTPS hostname in
BOOKSAVER_AUTH_DOMAIN and remote_auth.public_url. Then start and verify the stack:
docker compose --profile remote-auth up -d --build
docker compose ps
docker compose logs -f booksaverIn a private chat with the bot, send /start, then /connect. BookSaver discovers your
Booking.com reservations automatically. Use /bookings to refresh the account inventory and see
why any reservation is ineligible for price-drop checks. Bare /checknow immediately shows saved
caller-owned choices; selecting one runs a single fresh inventory verification and immediate price
check through the coordinator.
The first real Booking.com check is a required deployment test; VPS IPs can encounter bot walls.
The VPS deployment runbook covers DNS/TLS, backups, upgrades, smoke testing, recovery cookie import, and the non-Docker systemd alternative.
Python 3.11+ is required.
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
playwright install chromium
booksaver init
booksaver config validate
booksaver --helpbooksaver init creates ~/.booksaver/config.toml with mode 0600. For a desktop operator,
enable the Telegram bot in that file, export the required BOOKSAVER_* values, and run
booksaver run. Connect each admitted user through /connect; auth import remains a scoped
break-glass recovery path.
Useful CLI commands include bookings list, checks list, checks trace, savings list,
and auth status|delete|import. Run any command with --help for its full arguments.
- Configuration, SQLite data, encrypted sessions, traces, and snapshots stay in the configured local data directory. Backups contain sensitive booking data and must be protected.
- Secrets are read from environment variables, never from committed configuration:
BOOKSAVER_TELEGRAM_BOT_TOKEN,BOOKSAVER_LLM_API_KEY,BOOKSAVER_SECRET_KEY, and optionalBOOKSAVER_SMTP_PASSWORD. - BookSaver does not intentionally send cookies, passwords, MFA codes, or raw API keys to the LLM. When LLM features are enabled, it may send bounded rendered page text and, during escalation, screenshots to the configured Anthropic model. Rendered content can include account or booking details; use a provider/data policy you accept.
/connectkeeps credentials out of Telegram and BookSaver forms, but the browser executes on the VPS. A compromised root account could still observe login input. This is the known hardening boundary tracked in issue #6.- Telegram access is owner/invite only. There is no public-bot mode and no BookSaver-operated backend.
Install the Python development extras and existing Node validation dependencies before running the repository quality gate:
python3 -m pip install -e ".[dev]"
npm ci
python3 -m ruff check src tests
python3 -m mypy src
python3 -m pytest
npm run test:aidlc-validator
npm run validate:aidlcAccepted requirements, decisions, and delivery history live in memory-bank/.
The installed specs.md AI-DLC framework lives in .specsmd/aidlc/. Tool-specific
agent files are discovery adapters; the framework and memory bank are the authoritative sources.
Released under the MIT License. BookSaver is not affiliated with, endorsed by, or
sponsored by Booking.com or Booking Holdings Inc. See docs/DISCLAIMER.md.
