Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
260aad2
docs(specs): scheduled digests and anomaly alerts (D-43, D-44)
arg1998 Sep 29, 2026
0dea0fc
feat(contracts): digest and anomaly report contract, schedules and th…
arg1998 Sep 29, 2026
a6f3d45
docs(specs): the digest route counts previews and sends in one limit
arg1998 Sep 29, 2026
c2c72d9
feat(notifications): scheduled digests and anomaly alerts in core
arg1998 Sep 29, 2026
a714575
feat(cli): report schedules on startup channels and in channels list
arg1998 Sep 29, 2026
9380ba3
refactor(contracts): share the report calendar with the dashboard
arg1998 Sep 29, 2026
b5db65e
fix(notifications): digests read as digests on every platform
arg1998 Sep 29, 2026
edc2fae
feat(dashboard): digest and anomaly settings, next run on the cards, …
arg1998 Sep 29, 2026
7097cfb
fix(notifications): three small leftovers from the first channels
arg1998 Sep 29, 2026
5585c9d
docs(specs): a proxy's 5xx page is an unreachable public address
arg1998 Sep 29, 2026
4d1a9c0
docs(notifications): digests, time zones, late digests and anomaly al…
arg1998 Sep 29, 2026
bf84fbe
ci(notifications): live check sends a digest and an anomaly alert on …
arg1998 Sep 29, 2026
20dcda0
docs(specs): channels list prints the reports line it actually prints
arg1998 Sep 29, 2026
3d6edae
fix(dashboard): server clock for the next run; say why Send now is di…
arg1998 Sep 29, 2026
898513d
fix(notifications): a digest on Discord reads top to bottom
arg1998 Sep 29, 2026
c043dd8
fix(notifications): a weekly digest charts half-days, so the bars fit…
arg1998 Sep 29, 2026
4bef16e
test(dashboard): e2e schedules a daily digest in a zone and sends one…
arg1998 Sep 29, 2026
cf3697b
docs(specs): reports in the dashboard, weekly Friday default and week…
arg1998 Sep 29, 2026
dbdb976
feat(notifications): reports in the dashboard: one in-app copy per pe…
arg1998 Sep 29, 2026
ef0662c
feat(dashboard): Reports tab, report page, inbox Reports chip, in-app…
arg1998 Sep 29, 2026
089e75b
fix(notifications): keep a chart's peak on one line, write the report…
arg1998 Sep 29, 2026
bf32e79
docs(notifications): report retention, and what leaves the machine on…
arg1998 Sep 29, 2026
eda269f
docs(upgrading): upgrading from 0.1.x to 0.2, schema v2 to v6 in order
arg1998 Sep 29, 2026
2a7c30f
docs: doctor's publicUrl and channel checks, init --skipBrowsers, not…
arg1998 Sep 29, 2026
88a44ec
docs(readme): the 0.2 feature set: notifications, browser choice and …
arg1998 Sep 29, 2026
c3e69da
docs(website): landing page lists notifications, browser choice and h…
arg1998 Sep 29, 2026
a09a123
docs(changeset): the outbox note no longer says channels cannot be co…
arg1998 Sep 29, 2026
f67dd6a
docs(cli): banner and version examples for 0.2, with the Notify row
arg1998 Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .changeset/notification-contract-outbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"browserhive": minor
---

Notifications now follow what they announce, and the groundwork for sending them to your phone is in place.
Notifications now follow what they announce, and every notification is a versioned message that can be delivered reliably to other apps.

- **A notification keeps its place as things change.** When you resolve an attention request or a vault confirmation, reject it, or it times out, its notification shows the outcome (**resolved**, **expired**) as a small pill in the bell and on the Notifications page, instead of staying as if it were still waiting. A toast still on screen for it closes. A recovered subsystem marks its degradation notification resolved the same way.
- **Richer notification data in the API.** `GET /api/v1/notifications` and the `notifications` WebSocket topic add `kind`, `category`, `severity`, `state`, `revision` and `thread` to every notification. Existing fields are unchanged.
- **Safer text.** An agent's attention reason and other text copied into a notification now go through the same redaction as the logs, and page addresses lose their query strings.
- **Foundations for Telegram, Discord and ntfy.** Every notification is also a versioned message document, whose JSON Schema is published in the reference docs. Delivery to chat apps goes through a new outbox in the database, with retries and a circuit breaker, so nothing is lost when a service is down. No channel can be configured yet; with none configured nothing changes and nothing extra runs.
- **Built for delivery to your phone.** Every notification is also a versioned message document, whose JSON Schema is published in the reference docs (the webhook channel sends it as is). Delivery to Telegram, Discord, ntfy and webhooks goes through a new outbox in the database, with retries and a circuit breaker, so nothing is lost when a service is down. With no channel configured nothing extra runs.
- The database upgrades on start (schema v5: new columns on notifications and three new tables; a backup is written first). Older releases can still open it. Existing notifications are classified from what they already recorded; nothing is invented for them.
14 changes: 14 additions & 0 deletions .changeset/notification-digests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"browserhive": minor
---

A daily summary and a heads-up when something's off.

- **Daily or weekly digest.** Give a channel a digest (every day at 09:00, optionally weekdays only with Monday covering the weekend, or every week on Friday at 17:00; day and time are yours to change) and it gets the period in numbers: sessions, tool calls and errors with the rate, attention requests and how fast they were answered, vault fills, blocked requests, the slowest tool against the period before, the top errors, open problems, a small chart of tool calls per hour and a table per harness, with a link to the Overview for exactly that period. The new **Daily digest** preset sets it up in one click.
- **In your time zone.** Each channel has a time zone, BrowserHive's own unless you pick another, and digests and quiet hours follow it through daylight saving time.
- **Nothing lost, nothing spammed.** If BrowserHive was off when a digest was due, the most recent one arrives when it starts again, marked late, with how many earlier ones were skipped. A day with no activity sends nothing (the delivery log says so). A digest due in quiet hours arrives silently.
- **Tell me when something looks off.** An hourly check that stays silent until a threshold is crossed: many tool calls failing, a request waiting too long, sessions at the limit, a spike in blocked requests, BrowserHive degraded. The alert updates itself and says **Back to normal** when things recover, without flapping. Thresholds can be tuned per channel.
- **Reports in BrowserHive.** Every digest and anomaly alert also lands in the dashboard once per period, however many channels it reached: in the bell and the inbox (a new **Reports** filter), quietly for digests (no pop-up, no badge) and like a System notification for anomaly alerts. The new **Notifications → Reports** tab keeps them for 90 days, even after you dismiss them, with filters and a page per report (its numbers, chart and tables, the channels it reached, and Open Overview for this period). It can also run a digest and anomaly alerts for the dashboard alone, with no channel at all.
- **Send a digest now.** Preview the real digest exactly as your phone will show it, then send it on demand, from the channel card or `POST /api/v1/channels/{id}/digest`.
- **Setup and terminal.** Startup channels take `digest=daily@09:00`, `digest=daily:weekdays` or `digest=weekly` (Friday 17:00; `weekly:mon@08:30` for another day), `tz=` and `anomaly=on` with `anomaly.*` thresholds; `browserhive channels list` shows each channel's next digest, and `channels preview --sample digest|anomaly` renders the samples.
- **Also:** the Allow this person button explains when you lack `channels:write`, a deleted channel's reply-topic cursor goes with it, the live delivery log no longer shows stale superseded rows, and the public address check reports a proxy's 5xx page as unreachable. The message contract gains the `digest.weekly` kind, a `chart` block and an optional `report` field (schema 1, additive); `GET /api/v1/notifications` takes a `category` filter; new `GET /api/v1/notifications/reports`, `GET /api/v1/notifications/reports/{id}` and `GET`/`PUT /api/v1/notifications/report-settings`; no database migration.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

</div>

BrowserHive is a local [Model Context Protocol](https://modelcontextprotocol.io) server that gives any agent harness (Claude Code, Claude Desktop, Cursor, VS Code, your own) many parallel browser sessions. Each session is its own Chromium process with its own cookies, storage and service workers. Agents log in through your password manager without ever handling the password, hand the page to a human when they get stuck, and leave a replayable audit trail you can inspect on a built-in dashboard.
BrowserHive is a local [Model Context Protocol](https://modelcontextprotocol.io) server that gives any agent harness (Claude Code, Claude Desktop, Cursor, VS Code, your own) many parallel browser sessions. Each session is its own Chromium process with its own cookies, storage and service workers. Agents log in through your password manager without ever handling the password, hand the page to a human when they get stuck, and leave a replayable audit trail you can inspect on a built-in dashboard. When an agent needs you, BrowserHive can tell you on Telegram, Discord or ntfy, and you can answer from there.

Everything runs on your machine. Nothing leaves it unless you turn telemetry on.
Everything runs on your machine. Nothing leaves it unless you turn on telemetry or a notification channel, and then only to the endpoint or chat you chose.

## Why

Expand All @@ -30,11 +30,13 @@ BrowserHive handles all of that behind 43 MCP tools and a best in class admin da
## Features

- **Isolated sessions.** One Chromium process and context per session: separate cookies, local storage, IndexedDB and service workers. In-memory by default, persistent profiles on request, saved logins you can restore.
- **Your choice of browser, sandboxed.** Run sessions in the bundled Chromium or in the Google Chrome or Microsoft Edge installed on the machine (`browserhive init` shows what it found and lets you pick). Sessions run inside Chromium's sandbox wherever the machine allows it, and `--sandbox on` makes that a guarantee.
- **Stealth, honestly scoped.** Full Chromium in new-headless mode, Patchright, automation flags removed, a coherent user agent and client hints derived from your real host, optional display fingerprint and human-like input. No invented OS, GPU or location, and [the limits are documented](docs/guide/stealth.md#ceilings).
- **Vault credential injection the model never sees.** The agent names a Bitwarden entry and the form fields. BrowserHive checks the origin, session and principal, optionally asks you to confirm, types the credential and returns only a status. Tool results are redacted and Playwright traces exclude the keystrokes.
- **Human takeover.** `request_attention` blocks the agent while you watch its browser live and drive it with your own mouse and keyboard, then resolve with a message back.
- **Audit trail and trace replay.** Every tool call, navigation, vault access and blocked URL goes to SQLite, and every session can record a Playwright trace you open in the built-in Trace Viewer.
- **Operator dashboard.** Overview, sessions with live view and timeline, attention queue, visited websites, blocklist, vault policies and log, server logs, system status and effective configuration with provenance.
- **Notifications on your phone.** Your own Telegram bot, a Discord webhook or bot, an ntfy topic or a webhook of yours get a message when an agent needs you or something breaks. Approve, reject or resolve right in the chat, get a daily or weekly digest and anomaly alerts in your time zone, with optional screenshots, self-destructing messages and links that open on your phone.
- **Operator dashboard.** Overview, sessions with live view and timeline, attention queue, visited websites, blocklist, vault policies and log, notifications, channels and reports, server logs, system status and effective configuration with provenance. It recognises which agent (Claude Code, Codex, Cursor, …) ran each session and counts sessions and tool calls per agent.
- **OpenTelemetry.** Opt-in OTLP export of traces (one per tool call), metrics and logs to Grafana, Jaeger, Honeycomb, Datadog or any collector, with deep links from the dashboard.
- **One port.** MCP, REST API, WebSocket and dashboard share `127.0.0.1:9876`, with one bind rule and one authentication surface. Non-loopback binds require bearer tokens.
- **Guardrails.** URL blocklist with hot reload, launch-argument deny-list, per-session ownership, configurable result recording and retention.
Expand Down Expand Up @@ -96,7 +98,7 @@ Exposing BrowserHive beyond localhost, bearer tokens and client-specific setup a
| | |
|---|---|
| **Start** | [Installation](docs/guide/installation.md) · [Quick start](docs/guide/quick-start.md) · [MCP clients](docs/guide/mcp-clients.md) |
| **Use** | [Dashboard](docs/guide/dashboard.md) · [Vault](docs/guide/vault.md) · [Human takeover](docs/guide/attention.md) · [Stealth](docs/guide/stealth.md) · [Telemetry](docs/guide/telemetry.md) |
| **Use** | [Dashboard](docs/guide/dashboard.md) · [Vault](docs/guide/vault.md) · [Human takeover](docs/guide/attention.md) · [Notifications](docs/guide/notifications.md) · [Stealth](docs/guide/stealth.md) · [Telemetry](docs/guide/telemetry.md) |
| **Operate** | [Configuration](docs/guide/configuration.md) · [Security model](docs/guide/security.md) · [CLI](docs/guide/cli.md) · [Upgrading](docs/guide/upgrading.md) · [Troubleshooting](docs/guide/troubleshooting.md) · [FAQ](docs/guide/faq.md) |
| **Embed** | [Programmatic API](docs/guide/programmatic-api.md) |
| **Reference** | [Tools](docs/reference/tools.md) · [Configuration keys](docs/reference/configuration.md) · [Errors](docs/reference/errors.md) · [REST API](docs/reference/api.md) · [WebSocket](docs/reference/websocket.md) |
Expand All @@ -105,7 +107,7 @@ Exposing BrowserHive beyond localhost, bearer tokens and client-specific setup a

- **Bun ≥ 1.4.** Bun is the only supported runtime; installing with npm or pnpm is fine.
- **macOS, Linux or Windows.**
- **Chromium**, installed by `browserhive init` (never during package install).
- **A browser:** the bundled Chromium, installed by `browserhive init` (never during package install), or the Google Chrome or Microsoft Edge installed on the machine.
- **Bitwarden CLI** (`bw`), only for the vault.

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ BrowserHive is a local MCP server that gives AI agents isolated, stealthy Chromi
- [Security model](guide/security.md): authentication, bind rules, the vault model, redaction, what is recorded
- [Vault](guide/vault.md): Bitwarden setup, folder policies, bindings, confirmations
- [Human takeover](guide/attention.md): `request_attention` and the live view
- [Notifications](guide/notifications.md): notifications on your phone through Telegram, Discord, ntfy or a webhook, with screenshots, self-destruct and a public address for links
- [Notifications](guide/notifications.md): notifications on your phone through Telegram, Discord, ntfy or a webhook, answering from the chat, daily digests and anomaly alerts, reports in the dashboard, screenshots, self-destruct and a public address for links
- [Stealth](guide/stealth.md): what it does, what it does not, ceilings, proxies
- [Telemetry](guide/telemetry.md): OpenTelemetry export and a local Grafana stack
- [Command line](guide/cli.md): every command and exit code
Expand Down
16 changes: 9 additions & 7 deletions docs/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@ Help text is generated from the configuration schema, so `browserhive --help` al
Resolves the configuration, opens storage (migrating the database if needed), starts the listener and prints the banner:

```
BrowserHive 0.1.0 · bun 1.4.2 · patchright 1.63.0
BrowserHive 0.2.0 · bun 1.4.2 · patchright 1.63.0
MCP http://127.0.0.1:9876/mcp (auth: token)
Dashboard http://127.0.0.1:9876/ (admin)
Data dir /home/me/.local/share/browserhive (db v1, 12 MiB, 3 backups)
Data dir /home/me/.local/share/browserhive (db v6, 12 MiB, 3 backups)
Config env:2 file:/home/me/browserhive.config.json:9 cli:1
Sessions cap 8 (derived from 12 GiB RAM) · lease 2h · persistence memory
Stealth standard · patchright · humanize on · fingerprint off
Telemetry otel → http://127.0.0.1:4318 (http/protobuf)
Vault bitwarden (locked)
Notify 2 channels (1 from startup) · links → https://browserhive.example.net

config: maxSessions=8 (cli) shadows config-file=4
Press Ctrl-C to stop.
```

The first-run dashboard password and the first agent token are printed once in this banner and never logged. `Ctrl-C` (or `SIGTERM`) stops gracefully within `--shutdownTimeout`: listeners, then sessions (traces are finalized), then storage. A second `Ctrl-C` exits immediately with code 130.
The **Notify** row appears once a [notification channel](notifications.md#channels) or [`publicUrl`](notifications.md#public-address) is set. The first-run dashboard password and the first agent token are printed once in this banner and never logged. `Ctrl-C` (or `SIGTERM`) stops gracefully within `--shutdownTimeout`: listeners, then sessions (traces are finalized), then storage. A second `Ctrl-C` exits immediately with code 130.

Output streams: under `--transport stdio`, stdout carries only MCP frames and everything else goes to stderr. Under HTTP, the banner and pretty logs go to stdout on a terminal, and JSON logs go to stderr, so `browserhive 2> logs.jsonl` captures them. Colour is on for terminals; `NO_COLOR` or `--color never` disables it.

Expand All @@ -60,7 +61,7 @@ A list value joins its items with `+`; a value cannot contain `,` (write `%2C`).
| `ntfy` | `name`, `topic` (a topic, or `env:NAME`), optional `server` (default `https://ntfy.sh`), `token=env:NAME`, `reply` (the [reply topic](notifications.md#ntfy-a-second-topic-for-answers) for act buttons, or `env:NAME`), `replyToken=env:NAME` |
| `webhook` | `name`, `url` (a URL, or `env:NAME`), optional `secret=env:NAME` (the signing key) |

Rules, all optional: `categories` (`needs-you+problems+wrap-ups+reports+system`), `min` (`info`, `warn`, `error`, `critical`), `sessions` (session name patterns such as `shop-*`), `harness`, `content` (`counts`, `titles`, `full`), `quiet=22:00-07:00` with `tz=Europe/Berlin`, `ttl.<category>=2h` (Telegram at most `47h`), `deleteWhenResolved` (`true`, or a `+` list of categories), `images` (a `+` list of categories; needs `content=full`), `maskImages=true`, `actButtons=true` ([answer from your phone](notifications.md#answer-from-your-phone); Telegram, Discord bot mode, ntfy with `reply`, webhook) and `allow` (a `+` list of Telegram or Discord user ids allowed to press them; not for ntfy).
Rules, all optional: `categories` (`needs-you+problems+wrap-ups+reports+system`), `min` (`info`, `warn`, `error`, `critical`), `sessions` (session name patterns such as `shop-*`), `harness`, `content` (`counts`, `titles`, `full`), `quiet=22:00-07:00`, `tz=Europe/Berlin` (the channel's time zone, for quiet hours and reports; default BrowserHive's), `digest` (`daily@09:00`, `daily:weekdays@09:00` for Monday to Friday, `weekly:fri@17:00`; the day and time may be left out: `daily` is every day at 09:00, `weekly` is Friday at 17:00), `anomaly=on` with `anomaly.errorRate` (percent or `off`), `anomaly.minCalls`, `anomaly.attention` (minutes or `off`), `anomaly.blocked` (the spike factor or `off`), `anomaly.blockedMin`, `anomaly.capacity` and `anomaly.degraded` (`on`/`off`) ([daily digests and anomaly alerts](notifications.md#daily-digests-and-anomaly-alerts)), `ttl.<category>=2h` (Telegram at most `47h`), `deleteWhenResolved` (`true`, or a `+` list of categories), `images` (a `+` list of categories; needs `content=full`), `maskImages=true`, `actButtons=true` ([answer from your phone](notifications.md#answer-from-your-phone); Telegram, Discord bot mode, ntfy with `reply`, webhook) and `allow` (a `+` list of Telegram or Discord user ids allowed to press them; not for ntfy).

## `init`

Expand All @@ -77,6 +78,7 @@ One-time setup, safe to re-run:
|---|---|
| `--browsers chromium` | Browsers to install (only `chromium` today; Chrome and Edge channels use the OS installation). |
| `--force` | Re-download even if present. |
| `--skipBrowsers` | Skip the browser downloads (data directory and database only). |
| `--dataDir <path>`, `--config <path>` | Where state and configuration live. |
| `--stealthDriver <auto\|patchright\|playwright>` | `playwright` skips the Patchright download. |
| `--writeSchema` | Write `browserhive.schema.json` next to a discovered config file. |
Expand Down Expand Up @@ -139,16 +141,16 @@ Token commands work on the database directly when the server is stopped, or thro

| Command | Meaning |
|---|---|
| `channels list [--json]` | Every notification channel: platform (with the Discord mode), status (and "from startup"), where it sends, whether its variables are set, whether chat answers reach BrowserHive (`connected`, `reconnecting`, `offline (reason)`, or `—` when act buttons are off), the last delivery and the last 24 hours. |
| `channels list [--json]` | Every notification channel: platform (with the Discord mode), status (and "from startup"), where it sends, whether its variables are set, whether chat answers reach BrowserHive (`connected`, `reconnecting`, `offline (reason)`, or `—` when act buttons are off), the last delivery and the last 24 hours; under the table, each channel's next digest in its time zone and its anomaly state. |
| `channels test <name> [--json]` | Sends a real test message. Exit `0` when the platform accepted it, `1` with the reason when it did not. |
| `channels preview <name> [--sample <kind>] [--json]` | Prints the platform request a send would make (secrets shown as variable names); sends nothing. Samples: `attention` (default), `attention-resolved`, `vault-confirm`, `tool-errors`, `crash`, `degraded`, `test`. |
| `channels preview <name> [--sample <kind>] [--json]` | Prints the platform request a send would make (secrets shown as variable names); sends nothing. Samples: `attention` (default), `attention-resolved`, `vault-confirm`, `tool-errors`, `crash`, `degraded`, `test`, `digest`, `anomaly` (the report samples follow the channel's schedule, zone and content level). |

They talk to the running server: `--url` defaults to the configured host and port, and `--token` (an operator API token) or `--cookie` authenticates. See [Notifications](notifications.md).

## `version`

```
browserhive 0.1.0 (bun 1.4.2, sqlite 3.53.2, playwright 1.63.0, patchright 1.63.0)
browserhive 0.2.0 (bun 1.4.2, sqlite 3.53.2, playwright 1.63.0, patchright 1.63.0)
```

`--json` for scripts.
Expand Down
Loading
Loading