ccdeck

Detect browser automation while you were away

ccdeck's Browser Watch reads your Chromium browsers' own history for pages a program opened while nobody was browsing, and lists them as findings with every address in full; it is on by default, and reads only visits made after the deck started.

Claude Code and Codex Checked against ccdeck 3.81.3 on

You will

The film’s lines, in order: Did something drive your browser while you were away? An agent with browser control opens pages through an API. Chromium records that: FROM_API, not LINK or TYPED. The panel behind the eye icon answers one question. The pages a program opened while nobody was browsing. It is usually your own agent working in the browser. None of what it reads leaves the machine. It ends on the ccdeck logo, npx ccdeck, “Detect browser automation while you were away.” and ccdeck.dev.

Demo data The pages a program opened while nobody was browsing. It is usually your own agent working in the browser. Chromium browsers only; Firefox and Safari are not read. Not every page a program opens is marked FROM_API: a link it clicks or a tab it opens straight at an address is not. Drawn as a reconstruction of the deck.

Before you start

Steps

  1. Open Browser Watch

    Press B, or click the eye on the stripe on the right. On a phone, it is under More in the dock along the bottom.

    The top of the stripe on the right: icons for Usage and This machine; a rule; Usage history, Tool activity and Skill activity; another rule; then an eye with a pupil and a badge reading 2, and Telemetry Radar. Beside the eye, its hint: Browser watch, the key B, Watching — the deck keeps its own copy of the history, and 2 unread.
    Demo data The 2 counts the episodes that began since you last closed the panel.
  2. Read what the panel shows

    The left column shows which browsers are watched, and how many pages a program opened in each since the deck started. On the right, Findings lists what a program opened while nobody was browsing.

    The Browser watch dialog. Left: Activity overview with 5 for Google Chrome and 0 for Brave, Watched profiles with Google Chrome running and Brave idle and 6 not watched, and Remote control. Right: Findings with docs.example.com, 4 pages, and shop.example.com, 1 page, then Live activity with 9 events. At the bottom: Watching, 2 episodes on disk, and the Watch browser activity switch, on. Open the full-size picture
    Demo data Both findings are in Google Chrome.

    The footer reads Watching while its Watch browser activity switch is on, as it is by default.

    More about the panel
    • Remote control says whether Claude in Chrome is live in a profile here, which would let a Claude Code session signed in to your Anthropic account drive this browser. What it can reach, and how to stop it gives a command that blocks the relay, for you to paste into a terminal; ccdeck never runs it.
    • Live activity adds a line each time a browser’s history grew, counting your own pages without writing them down.
    • The open panel reads the browsers every 10 seconds. The circular arrow in the header copies each history again, at most once a minute.
    • While the watch is off, the panel shows only what this deck has seen since it started, and nothing new is kept.
  3. Open a finding

    Each card is one episode: one host in one browser. Click a card to list every address the program opened, in full, with its time.

    Findings, 2 episodes: docs.example.com opened to its four addresses, one of them ending in ?tab=headers and one in #errors, and shop.example.com, 1 page, closed.
    Demo data The part after ? and the part after # are kept.

    A page counts only when a program opened it and nobody browsed that profile by hand for the Nobody browsing for time, 15 minutes by default, before and after it. Until that time has passed, its card says provisional: a visit of yours can still withdraw it. Nothing is kept or done until its first page is past that time.

    A finding is not a verdict. It is usually your own agent working in the browser. The watch reports what a program opened; the judgement is yours.

    More about what counts as away

    The deck looks for Chromium’s FROM_API mark. A page loaded with Page.navigate over the browser’s debugging protocol carries it, the way Playwright and Puppeteer go to an address, and so does an extension’s tabs.update. A visit without the mark counts as you. The deck has no idle timer and does not watch the keyboard: it goes by the history alone.

    What it misses: a tab a program opens straight at an address (Target.createTarget, an extension’s tabs.create), a link it clicks, and an address handed over by open or xdg-open carry no mark. They are not reported, and like a visit of yours they can withdraw a finding near them.

    It never reports its own tabs. A card’s pages stay together while no two in a row are 15 minutes or more apart. While you are browsing, the overview counts down to when a program’s page would count.

  4. Dismiss a finding you have checked

    Click the × beside its card. The episode leaves the list for good, even while its run goes on. Addresses already kept stay in the log file.

  5. Choose what happens when it finds one

    Click the gear in the footer. Two menus open above it:

    • Nobody browsing for: 1, 5, 15, 30 or 60 min. A shorter time catches more, including more of your own work.
    • When it finds one: notify me, the default; close the tab, on macOS only; or quit the browser. On Linux it quits only the browser holding that profile, or nothing when it cannot tell which one that is, as with a Flatpak Brave.
    The settings: Nobody browsing for, 15 min; When it finds one, notify me; a note ending “Usually that is your own agent doing what you asked.”; What Browser Watch can access, closed. Below: Watching, 2 episodes on disk, the gear, and the Watch browser activity switch, on.
    Demo data This deck runs on Linux, so its menu holds notify me and quit the browser.

    Closing the tab is only cleanup. The page has already loaded by then. Only quitting takes the browser away from whatever was driving it.

    More about reactions

    Every reaction also shows a system notification titled “Browser watch”, unless the deck was started with AGENTS_DECK_NO_NOTIFY=1. The Notifications while closed switch, in Settings › Notifications, does not change it.

    A reaction runs once for each new episode, only while the watch is on, once its first page is no longer provisional: with the default 15 minutes, about 16 minutes after that page, at the next check. Live activity then says what it did, as in “docs.example.com — notified”. When several decks with Browser Watch run, only the one on the lowest port writes the record and reacts. The others show the same findings.

  6. Follow the record on disk

    While the watch is on, each new episode goes into watch.log once its first page is no longer provisional, with every address under it. On macOS or Linux, follow it with:

    tail -f ~/.claude/agent-dag/browser-watch/watch.log

    Only flagged episodes are kept, never your own browsing. The watch does not read cookies, saved passwords or page contents, and nothing from your history leaves the machine.

    More about what the watch reads and keeps
    • Beside the log, state.json holds up to 500 episodes, your dismissals and the settings. Both move under CLAUDE_CONFIG_DIR when that is set; CCDECK_HOME does not move them.
    • watch.log keeps each episode as first written, and rolls over to watch.log.1 at 2 MiB.
    • To read a history, the deck copies the browser’s History file into a private folder under browser-watch/staging/, only when it changed, and deletes the copy after one query.
    • For Remote control, it also reads the hosts file and the settings of profiles with Claude in Chrome, and, while the panel is open and where dig is installed, looks up the relay’s name about every 30 seconds. That DNS query is the one thing the watch sends out.
    • What Browser Watch can access, in the settings, lists what the watch reads, keeps and never reads.

If something goes wrong

“No browser on this machine has a profile to read.”

Open Google Chrome, Chromium, Brave, Microsoft Edge or Vivaldi once (Arc too on macOS). It is watched from then on.

A profile “could not be read”

For no-sqlite-reader, run the deck on Node.js 22.13 or newer, or put a sqlite3 command on its PATH. For another reason, click the circular arrow in the header after a minute.

A program drove the browser, and nothing is under Findings

You may have browsed that profile by hand within the Nobody browsing for time: set it to 1 or 5 min to catch more. The watch also skips visits from before the deck started, unmarked pages (step 3), and a browser run from a folder of its own, such as an automation tool’s throwaway profile.

No notification arrived, or it arrived late

A reaction waits until the finding is no longer provisional, about 16 minutes by default, and with no deck page open, until the first check after one opens. Otherwise check that the watch is on, then read Live activity. It says “could not notify” when notify-send fails on Linux.

When it finds one is greyed out

The watch is off. Turn on Watch browser activity in the footer.

Something else? See Troubleshoot ccdeck: status, logs, port and hooks.

Next

All guides Troubleshoot Browser Watch in the README