Skip to main content

English  ·  简体中文  ·  日本語  ·  Español

Ruin Everything: a receipt surfs a wave of blue and lavender browser tabs

GitHub stars Requires Python 3.11 or later MCP over stdio MIT license

Pick up a browser task where the last agent left it. Ruin connects to Chrome on your machine. A new CLI run or MCP client can find the same open application tab, finish a supported verification step, and check what the site actually confirmed.

Set up Chrome · Connect an MCP client · See what has been tested · Try your first MCP call · Contribute

In your browser What Ruin can do
A Greenhouse application asks for an email code Match a fresh Outlook message to the application and fill the code.
An Ashby tab closed after submission Look up a previously captured receipt by the exact job URL.
A Workday tenant asks you to sign in Use the local profile and follow a supported account activation back to the job.
You have phone numbers from several countries Choose a default in the offline identity editor.

Choose a starting point

Five terminal commands and when to use each, from starting Chrome to watching an existing application for its receipt.

For a new task, start the shared browser and open the target site. For an interrupted application, choose the site-specific watcher and pass the original job URL. Generic click tasks open a new tab.

Independent clients reconnect to the original tab in shared Chrome.

Use a shared local Chrome window

Two independent runner connections attach to a local Chrome profile and keep the same application tab open.

Install from the repository directory with Python 3.11+ and Google Chrome:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e .
ruin browser

Run ruin browser to start or reconnect to the project browser. Every CLI task uses Python Playwright over CDP. The default endpoint is http://127.0.0.1:9222. The runner launches Chrome with a dedicated profile at ~/.ruin-everything/canvas-browser when this port is free. The CDP connection preserves the existing browser context settings, including its download behavior. Chrome stays open after the command exits. Other local clients can attach to the same endpoint and locate the job tab by its full URL. Give one client ownership of submission for each application.

Two commands connect to the same Chrome process. A later run can recover a live tab; closing it can discard unsaved form state.

Open Outlook Web in this profile and complete any required Microsoft sign-in before requesting a fresh email code. Closing the job tab, quitting Chrome or a site session expiring can still interrupt an application. A draft in a chat-specific browser must be recovered separately.

Run a browser click task

The model receives a task, title and control labels. It returns an action ID to the browser. The next observation creates a fresh snapshot.

Install the optional dependencies with python -m pip install -e '.[agent]' and set OPENAI_API_KEY. Give the model an HTTPS starting page and a specific task:

ruin "Open the help page" --site https://example.org --channel chrome

The runner sends the task, page title and visible action labels to the selected model. It clicks the chosen control directly and observes the page again. A task can use one tab and up to 20 steps.

An illustrative Help center button is assigned ID 1 in one snapshot. The model can request a click by that ID.

For example, if a visible button is labeled Open guide, the model can select its current ID. A changed URL or an ID missing from the snapshot stops that step. A blocked model decision leaves the page open for inspection.

Local credential filling

At the selected HTTPS origin, the keyring can fill credentials and Chrome can offer saved sign-ins on the original form.

Add --autofill to use the system keyring and Chrome’s sign-in form. For a form with one visible password field that submits to the selected HTTPS origin, the runner clicks the empty field so Chrome can offer credentials saved in that browser profile. Complete sign-in in the open tab. The runner waits up to three minutes for the credential fields to disappear, then resumes the task. It leaves account selection and form submission to the login UI. A Chrome fixture verifies same-tab continuation with a restrictive script policy.

Local account registration

The selected form is checked for an explicit signup action. A matching form uses the local profile, and a subsequent code field can use a fresh domain-bound SMS.

Run ruin identity to choose phone regions by name, save multiple numbers, select a default and reveal the password while editing. The offline editor uses the browser’s native select control and the phonenumbers library for dialing codes and international formatting. python-phonenumbers

For manual setup of a browser task with --site, create ~/.ruin/identity.json with owner-only permissions (chmod 600). Set auto_signup to true and supply string values for first_name, last_name, email, phone and default_password. This file stores the password in plain text. The local runner checks the selected HTTPS origin before each model decision until it submits a registration. It recognizes one visible Create Account, Create an Account, Register or Sign Up submit button on a same-origin POST form, fills recognized fields, and checks labeled Terms of Service, Terms of Use and Privacy Policy boxes. On an ordinary same-origin form it can also fill one empty, editable contact phone field. A visible matching signup field with a missing profile value leaves the tab open for completion.

For a subsequent single one-time-code field, the runner checks macOS Messages for up to 30 seconds for an incoming text received since the submission and ending in @hostname #code. Read access to the Messages database is required. MCP challenge and receipt results include a signup_policy field reporting the local setting. Chrome and SQLite fixtures exercise form submission, ordinary phone inputs, cross-origin actions, message age, direction and domain matching.

Workday accounts and signed-in sessions

The Workday branch checks sign-in before account creation and Outlook activation. The session branch periodically checks a separate status tab.

On supported English Workday tenant forms, the CLI browser task tries the local email and password first. An explicit missing-account message opens registration. A generic sign-in failure leaves the original job open. After registration, Ruin checks the signed-in Outlook mailbox for a fresh activation message, follows a direct activation link on the exact tenant host, waits for visible confirmation and returns to the job URL. The adapter supports native POST forms and script-driven Workday dialogs with a recognized submit control. Local Chrome fixtures cover existing accounts, registration, activation and returning to the job. Production tenant layouts and wrapped email links can differ.

The adapter selects Sign In inside the open Workday dialog, even when the page header also offers Sign In. For a six-digit email challenge, it checks for one fresh Outlook message with a link to the same tenant and one labeled code, enters the code in the original tab, then waits for visible activation confirmation. The Jain Global dialog controls were inspected in shared Chrome. The six-digit activation path was verified with a simulated mailbox.

For a code sent to an existing tab, use the request time recorded when the site sent the SMS. Optionally add sms_senders to the local identity file, mapping an exact hostname to its dedicated sender string. Plain six-digit codes must come from that sender. Domain-bound messages work without a sender entry. Both paths require read access to the Mac Messages database.

ruin verify-sms --site "$VERIFICATION_URL" --requested-at "$REQUEST_UNIX_SECONDS"
ruin keep-alive --site "$STATUS_URL" --signed-in-text "Account overview" --interval 900 --timeout 86400

Keep-alive opens a separate tab for the selected HTTPS status page. Use a read-only page with text unique to the signed-in view. The process checks that text after each visit, stops on redirects or missing text, and closes its own tab when finished. Run the command for the desired duration. Sites control their own session lifetimes. Simulated Chrome checks cover repeated GET requests and preservation of the original tab.

To provision credentials for local filling, use the entries below. The optional TOTP entry holds the site's Base32 seed.

keyring set ruin:https://example.org username
keyring set ruin:https://example.org password
keyring set ruin:https://example.org totp

Verify a Greenhouse application with Outlook Web

Two lanes track one request. The inbox supplies a matched code to the original job tab, which submits and checks for a receipt.

On macOS or Linux, attach to a local debugging-enabled Chrome session that already has Outlook Web signed in and the original Greenhouse job tab open. Replace the example URL and employer with the exact job and the name used in its email. Start the watcher before the application requests a code. The existing application workflow still owns the initial submit.

ruin verify-email --cdp http://127.0.0.1:9222 \
  --site https://job-boards.greenhouse.io/example/jobs/12345 \
  --company "Example Employer" --timeout 180

The watcher takes a snapshot of visible Outlook inbox rows and waits for a new or updated row whose timestamp follows the request window. It checks the employer, subject wording and Greenhouse sender, reads the code from the opened message, and submits it in the original job tab. A local lock serializes watcher processes. Keep other applications for the same employer idle during verification.

Example inbox timeline: an old row precedes watcher startup; a unique matching new row can be inspected. Two new candidates stop the run.

In the inbox diagram, a 09:59 message already exists when the watcher starts at 10:00. The watcher checks the 10:01 candidate against the employer and verification wording in the row, then checks the sender in the opened message. If two new candidates match, it stops before selecting either one. These times illustrate ordering; the watcher uses the recorded timestamps from the live run.

Recover an open challenge

Recovery options depend on the form: resend once, supply the original request time, or inspect the original tab if the result is uncertain.

If a challenge is already open at startup and offers a resend control, it requests a fresh code there. It makes at most one resend per run.

Two jobs can share a mail subject. Recovery without a resend requires the job ID in the body. A missing or different ID leaves the tab open. An example embed URL separates the employer parameter from the job ID. The watcher checks that exactly one matching tab is open.

For a direct Greenhouse embed, pass the full quoted URL (including both for and token). If an existing challenge has no resend control, supply --requested-at UNIX_SECONDS from that job’s original request. For this recovery path the message body must also identify the job ID. A subject shared by two roles at the same employer cannot establish which form owns the code. A tab in another chat’s isolated browser must be handled there.

The prototype supports English, top-level Greenhouse forms and Outlook layouts exposing message identifiers, timestamps, sender addresses and a message body. Missing evidence or an ambiguous match stops verification. Only a visible receipt counts as success. A timeout can also happen after submission; check the original tab before retrying. Message contents and codes stay outside the model loop. The integration has been tested with browser fixtures; compatibility with a live employer form and Outlook tenant remains unverified.

Resume an authorized Ashby application

Example of the Ashby status used as evidence: the visible Success heading and official submission text on the same job URL.

Use this workflow when a reviewed application has already reached verification. The original application driver owns the authorization for that job and completes the live challenge in the same tab. Start the receipt watcher alongside that driver, using the exact application URL:

ruin resume-ashby --cdp http://127.0.0.1:9222 \
  --site 'https://jobs.ashbyhq.com/EMPLOYER/JOB-UUID/application' --timeout 300
The original driver handles the challenge in the existing tab; the receipt watcher observes that tab and emits state changes.

The CLI attaches to one matching tab and emits JSON state changes on standard error. On challenge_visible, the authorized driver uses the current widget. A driver using the Python API can supply its own approved handler directly:

receipt = await resume_ashby(context, application_url, timeout=300,
                             directory=receipt_directory, on_event=report_status,
                             on_challenge=complete_authorized_challenge)

Import resume_ashby from ruin.agent.ashby. The handler receives the original Playwright page once per run. The standalone CLI observes the browser; it requires an application driver or person to interact with the widget. A completed challenge may automatically submit the form. Leave any existing submission in flight and watch for its result. An enabled Submit button by itself is insufficient evidence to send another request.

Receipt storage and retries

Retrying a recorded application returns its saved receipt. An unrecorded application stays pending until the watcher sees official success.

The watcher records success only when the same job displays Ashby's visible Success status with Your application was successfully submitted. It returns the receipt as JSON on standard output and stores one row per job in ~/.ruin-everything/ashby-receipts.sqlite3. A retry returns that stored receipt. A timeout, changed job or ambiguous tab produces an error without marking the job submitted. An external application tracker should import only a returned receipt.

After a timeout, inspect the pending tab or look for saved evidence. A missing receipt leaves the outcome unknown.

For example, a challenge can disappear while the site is still processing the application. If the watcher times out then, check the original tab before taking another action. If that tab later displays the official receipt, rerun the watcher with the same URL to capture it.

The illustrated receipt is an example. The receipt pattern comes from a completed Baseten application. The reusable command was tested with simulated Ashby pages in Chrome, including a hidden receipt, a changed job, duplicate tabs and a retry after the tab closed.

Connect an agent over MCP

An agent checks the local Ashby journal by job URL. It can return saved evidence offline or watch the original tab for a new receipt.

Tagged releases include the MCP server by default. A registry client can launch the package directly with uvx ruin-everything@0.2.0. GitHub releases

Give the client the exact job URL and describe what you see. If the question is whether an Ashby application went through, the client checks the receipt journal first. If the form is waiting for verification, the client inspects the open tab.

For one exact job URL, the inspector identifies a Greenhouse email code prompt or an Ashby reCAPTCHA frame and returns the observed state.
Tool When to call it Result
ruin_auto_register The selected registration or contact form is open at its exact HTTPS URL. Local profile action and current signup policy. A submitted form can still require verification.
inspect_application_challenge Greenhouse asks for an eight-character email code, a connector reports [one-time authentication code withheld], or Ashby shows a reCAPTCHA grid. Also use it to locate a lost supported form. The provider and visible state. A missing or duplicate tab gets its own result. The form stays open.
get_ashby_receipt At handoff, even with Chrome closed A saved receipt or unrecorded. A missing record leaves the outcome unknown.
watch_ashby_receipt The original form is open and the outcome is uncertain An existing receipt, or fresh evidence from the same job. A timeout is a tool error. The watcher leaves the form open.

Install uv once. In an MCP client with JSON configuration, this example runs the latest PyPI release. The client must be able to find uvx on its PATH. An absolute path to uvx also works.

{
  "mcpServers": {
    "ruin-everything": {
      "command": "uvx",
      "args": [
        "--isolated", "--refresh",
        "--from", "ruin-everything[mcp]",
        "ruin-mcp"
      ],
      "env": { "RUIN_AUTO_UPDATE": "0" }
    }
  }
}

To track the GitHub branch directly, set --from to ruin-everything[mcp] @ git+https://github.com/yuki4266/ruin-everything.git@main. Both configurations launch the actual ruin-mcp executable. A two-version smoke test verified refresh on both the Git and package paths. An existing editable installation can still launch it directly.

To register the server for your user account, run the command for your client. Open a new session to pick up the tool list.

codex mcp add ruin-everything --env RUIN_AUTO_UPDATE=0 -- uvx --isolated --refresh --from 'ruin-everything[mcp]' ruin-mcp
claude mcp add --scope user --transport stdio ruin-everything -e RUIN_AUTO_UPDATE=0 -- uvx --isolated --refresh --from 'ruin-everything[mcp]' ruin-mcp

Try your first MCP call

After connecting your client, try this prompt with the fictional URL below. It works with Chrome closed and requires no job application or identity profile.

Call get_ashby_receipt for https://jobs.ashbyhq.com/example/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/application and tell me its state.

On a fresh install, the expected state is unrecorded: the tool checked the local journal and found no receipt for this example. This read-only call was verified against the PyPI server.

The repository includes routing instructions for Codex and imports them from CLAUDE.md. To apply them in other workspaces, copy the application routing section into your user-level ~/.codex/AGENTS.md or ~/.claude/CLAUDE.md. Client permissions and site verification still apply to Duo, PayPal and other unsupported providers.

All four tools take site. The inspector checks once by default. Set wait_seconds to 1–300 to watch that same tab for a change from pending. From the CLI, use ruin watch --site "$JOB_URL" --timeout 30. The watch ends after detecting a supported state, losing the original tab, or reaching the time limit.

In a local Chrome simulation, a fresh Codex CLI session given only the Greenhouse URL and the eight-character-code symptom selected inspect_application_challenge as its first tool call and returned email_verification. The simulated form stayed empty.

The Ashby receipt watcher takes timeout_seconds (default 15, range 1–300). A returned receipt contains state, site, observed_at and the site's confirmation message. The application driver owns form interaction. After a timeout, check the same URL again before considering another submission.

The server uses the official Python MCP SDK over stdio. A browser automation client such as Playwright MCP can run alongside it by attaching to the same local Chrome with --cdp-endpoint http://127.0.0.1:9222. The integration test waits for Chrome to advertise its local debugging port, then connects two successive SDK clients to the simulated employer page. Python MCP SDK · Playwright MCP connection options

How updates reach your machine

Three update schedules: uvx resolves the package on MCP launch, direct installs check every six hours on startup, and site rules refresh at the start of each workflow.
What changes When you get it
MCP package via uvx On the next server launch, uv resolves the latest version available from the configured source. A long-running server keeps its loaded code until restart. Initial installation and online refresh require network access.
Direct installation CLI and MCP startup schedule a quiet background check at most once every six hours per installation. A clean main checkout tracking origin/main fast-forwards. A regular pip installation checks PyPI for a newer version. Installed uv tools use their own upgrade command. Set RUIN_AUTO_UPDATE=0 to manage versions yourself.
Site rules Each new Greenhouse verification or live Ashby observation requests the project's selector file with a two-second network timeout. A workflow keeps its initial snapshot through completion. Offline or invalid updates fall back to the last valid cache at ~/.ruin/sites.json, then to packaged defaults. Set RUIN_RULE_UPDATES=0 to use the bundled copy.

The rule file describes Ashby receipt elements, Greenhouse verification inputs and the email-code pattern, and Outlook rows and message bodies. The installed adapters enforce trusted origins, sender identity, job matching and receipt wording. A change to those checks requires a code release.

For maintainers, configure one PyPI Trusted Publisher for project ruin-everything, owner yuki4266, repository ruin-everything, workflow publish.yml and environment pypi. A push to main builds the version in pyproject.toml with the workflow run number as a fourth release component. For a tagged release, set the project version and push the matching tag (for example, 0.2.0 and v0.2.0). Both routes run the browser tests, build and check the wheel and source distribution, then publish using GitHub's OIDC identity. Release workflow · uv tool updates

Example: read courses after school sign-in

An active session returns the course list directly. If Canvas redirects, complete Duke sign-in to resume the same lookup.

The Duke Canvas adapter demonstrates how a task resumes after an interactive login. Run the lookup in the shared profile:

ruin courses

The result contains course names and links. Use --json for structured output:

Illustrative JSON output with an example course name and abbreviated link.

To select an existing local debugging endpoint explicitly, run:

ruin courses --cdp http://127.0.0.1:9222 --json

The sign-in branch waits up to 180 seconds for the school to return the browser to Canvas. Set --timeout 300 for a longer wait. Duo approval takes place on the school sign-in path. Duke device recovery

Verification

Local tests exercise session reuse, control labels and model-directed clicks. Live checks cover course discovery and a repeat lookup.

Update tests cover overlapping startup checks, changes between rule snapshots, invalid downloads and offline fallback. Runtime checks verified a shared Chrome tab across independent CDP connections. For browser tests, install the Playwright Chromium build or use installed Chrome. Run python -m unittest discover -s tests -v. Install the agent extras to include the mocked model test. For installed Chrome, prefix the command with RUIN_TEST_CHANNEL=chrome.

A live Duke run returned course links after sign-in. A second run with the same profile also returned the list after visiting the school login route. The number of Duo prompts was not measured. The general click loop has been exercised with a simulated model response.

The hand-drawn diagrams are editable SVGs. They use Rough.js linework and Kalam lettering. Figure sources · Rough.js · Kalam font license

Development plan · Contribution and figure guidelines · MIT license

Contribute to Ruin Everything

Where to report a bug, share a workflow or propose a patch on GitHub.

Trying Ruin in a new MCP client? Share your setup and one tool result so someone else can repeat it. If setup stopped partway, ask in Q&A.

Report a bug · Suggest an idea · Contribute a translation, test or fix

Contributors

Thank you to everyone who tests a workflow, answers a question or sends a patch. GitHub commit contributors appear below.

View GitHub commit contributors

Star History

Dated radar chart of GitHub stars, forks, contributors, and seven-day activity for Ruin Everything.

Select the snapshot to explore the latest star history.

⭐ If Ruin helped you recover a browser task, give it a star on GitHub.

Metadata

Release files for ruin-everything 0.2.0.17

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ruin-everything 0.2.0.17
File Size Uploaded Provenance Actions
ruin_everything-0.2.0.17.tar.gz 59.2 kB With provenance Details for ruin_everything-0.2.0.17.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ruin-everything 0.2.0.17
File Interpreter ABI Platform Actions
ruin_everything-0.2.0.17-py3-none-any.whl Python 3 none any Details for ruin_everything-0.2.0.17-py3-none-any.whl

Total release size: 106.9 kB

Release files / ruin_everything-0.2.0.17.tar.gz

Download URL ruin_everything-0.2.0.17.tar.gz
Size 59.2 kB
Tags Source
SHA-256 checksum
How to use checksums
a87337e2c72646169eb8c9d776466a0e61a3ee3b5da4aa84173600b7e3d7f5c9
BLAKE2b-256 checksum
How to use checksums
81e7894cf1621b054d5a796b2eab58a3cdfe617cb3ccdcceb89f9c2142bca5da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Release files / ruin_everything-0.2.0.17-py3-none-any.whl

Download URL ruin_everything-0.2.0.17-py3-none-any.whl
Size 47.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9f97320d3e66e861a709a59da8e02fb54e7524ab7fda22888d2f25a01394eff1
BLAKE2b-256 checksum
How to use checksums
da45d2523f08359ca0a6c44898c248fbbc58c4fd9d1440baffc7dc8b6602438e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Security for ruin-everything 0.2.0.17

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ruin_everything-0.2.0.17.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page