Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/bright-commands-retry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@understudy/connector": patch
---

Expose retryable command timeouts as a typed connector error.
5 changes: 5 additions & 0 deletions .changeset/calm-sessions-close.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@understudy/protocol": minor
---

Add strict device-control closure acknowledgements for durable, replayable session retirement.
125 changes: 69 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,77 @@
# understudy

A governed **browser-execution service** that puppets a user's *already-logged-in* browser
via a Chromium extension. The Cloudflare-hosted service holds the live sessions and exposes
`POST /v1/sessions/:sessionId/commands`; the extension executes each command in the user's
real tab via CDP and reports back. understudy runs **no LLM** — the agent brain and all
governance (approvals, RBAC, audit via breakwater/flowsafe) live in the consumer apps that
drive it over HTTP (Topology 1).

**Full design + build plan: [`docs/technical-plan.md`](docs/technical-plan.md).** Read it first.

## Repository layout

- **`packages/protocol`** — the shared command/event protocol (TypeScript + zod 4, published
`@understudy/protocol`). The stable contract between the service, the extension, and
consumer connectors; the core IP.
- **`packages/connector`** — **M4** the reference `@proofoftech/breakwater` connectors
(`@understudy/connector`): `observe` / `act` / `fill_credential`, approval-gated via
flowsafe grants, egress-pinned to the service host. What consumer apps import to turn
browser actions into governed Mastra tools. See its README.
- **`apps/cdp-spike`** — **M0** throwaway harness: a buildless MV3 extension that verifies the
`chrome.debugger` CDP command surface (the plan's one gating technical risk). See its README.
- **`apps/extension`** — **M2** the real extension: a WXT + React MV3 extension that puppets a
logged-in Chromium tab over a WebSocket. See its README.
- **`apps/backend`** — **M3** the browser-execution service: a Cloudflare Worker (Hono) plus one
Agents-SDK Durable Object per session, terminating the extension's WebSocket and exposing
`POST /v1/sessions/:id/commands` for consumer apps (metamind, smart-compliance) to drive. Runs
no LLM and embeds no agent framework — the brain and governance (breakwater/flowsafe) live in
the consumers. See its README.

M4 is complete. `@understudy/protocol@0.6.0` and
`@understudy/connector@0.4.0` are published on npm. On 2026-07-25 UTC
(2026-07-26 Asia/Dubai), Metamind completed the production cross-repository
proof against Understudy
`master@797d0e4` and Metamind `master@0814deb`: a connected Chromium extension
executed public-page observation, an approved login using a vaulted credential,
and authenticated-page observation with correlated flowsafe audit evidence. The
labeled proof batch remains in `draft`; no email or Gmail draft was created. The
agent loop and governance stay in the consumer, per Topology 1.

## Develop

```sh
<!-- Content type: Landing -->

# Run governed browser commands in user-controlled Chromium

Understudy is a model-free browser-execution service. A Cloudflare Worker coordinates attended and unattended sessions while an installed Manifest V3 extension executes commands through the Chrome DevTools Protocol (CDP). Consumer applications own model execution, approvals, role-based access control, policies, and durable audit through breakwater and flowsafe.

Read [`docs/technical-plan.md`](docs/technical-plan.md) for the architecture, safety contract, limits, and rollout gates.

## Explore the repository

| Path | Purpose |
|---|---|
| `packages/protocol` | Published Zod 4 command, event, control-frame, and status contracts |
| `packages/connector` | Published breakwater connectors for observe, act, and vaulted credential fill |
| `apps/backend` | Hono Worker, session and device Agents, tenant coordinator, quotas, and telemetry |
| `apps/extension` | WXT and React extension with attended and two-tab unattended hosting |
| `apps/cdp-spike` | Historical Manifest V3 CDP capability harness |

`@understudy/protocol@0.7.0` and `@understudy/connector@0.5.0` are prepared in this repository. A local build does not publish them.

## Understand the isolation boundary

An unattended device is one tenant-dedicated Chrome profile with capacity for two extension-owned tabs. Those tabs have separate command, CDP, ref, and lifecycle state, but share cookies and browser storage.

Understudy never:

- Uses a Cloudflare-managed browser
- Automatically attaches an existing tab for unattended work
- Restores old URLs or tasks after restart
- Replays a granted write with an unproven result
- Records video, GIF, Document Object Model history, or session content
- Replaces consumer approval or durable audit

Protocol 2 provides at-most-once write execution with explicit pending and unknown outcomes.

## Develop the repository

Requirements:

- Node 22 or newer
- pnpm 11.5.2
- Chrome 125 or newer for production extension verification

Run:

```bash
pnpm install
pnpm build # first on a fresh clone: @understudy/* resolve via gitignored dist/
pnpm build
pnpm typecheck
pnpm test
```

Requires Node ≥22 and pnpm ≥10.16 (see `package.json`). Dependencies are quarantined for
7 days via `minimumReleaseAge` in `pnpm-workspace.yaml` (supply-chain guard against
freshly-published malicious versions; first-party `@proofoftech/*` packages are exempt).
Dependencies use a 7-day minimum release age through `pnpm-workspace.yaml`. First-party `@proofoftech/*` packages are exempt.

For the production extension:

```bash
pnpm --filter @understudy/extension build
```

Load `apps/extension/.output/chrome-mv3/` through `chrome://extensions`. Follow the [real-Chromium acceptance runbook](apps/extension/RUNBOOK.md).

## Release published packages

Changesets and GitHub Actions manage releases from `master`. A pull request that changes a published package adds a changeset with:

```bash
pnpm changeset
```

The release workflow opens or updates the **Version Packages** pull request. Merging that pull request publishes approved versions with npm provenance. `NPM_TOKEN` needs publish access to the `@understudy` scope.

## Release (npm)
Backend deployment remains a separate Wrangler operation. Keep `UNATTENDED_ENABLED_TENANTS=[]` until the canary extension passes the production acceptance suite.

Changesets and GitHub Actions manage releases from `master`; see
`.changeset/README.md`. A pull request that changes a published package adds a
changeset with `pnpm changeset`. The release workflow opens or updates the
“Version Packages” pull request, then publishes the approved versions with npm
provenance. The repository secret `NPM_TOKEN` needs publish access to the
`@understudy` scope.
## Preserve attended proof history

The M0 harness needs no build — load `apps/cdp-spike` unpacked in a Chromium browser
(`apps/cdp-spike/README.md`).
The completed attended design and production proof remain in Git at Understudy `master@797d0e489df2772d0f5d597141982547861881bb` and Metamind `master@0814deb`. Attended mode remains compatible in the current extension and API.
7 changes: 7 additions & 0 deletions apps/backend/.dev.vars.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ CALLER_TOKENS={"dev-caller-token":{"actor":"stub-consumer","tenantId":"dev-tenan
# settings.
EXTENSION_TOKENS={"dev-ext-token":"dev-tenant"}

# JSON map: SHA-256(device credential) -> tenant-bound device identity.
# The example digest is for the literal credential "dev-device-token".
DEVICE_TOKENS={"7053fe692ce151a1a4e066d93850420b420ce95d823a0c7e8609fddf5272438d":{"tenantId":"dev-tenant","deviceId":"00000000-0000-4000-8000-000000000001","credentialVersion":1}}

# Independent HMAC key for 60-second, single-use WebSocket tickets.
WS_TICKET_SECRET=dev-ticket-secret-change-me

# base64url-encoded 32-byte AES-256-GCM key that envelope-encrypts every
# vault value (src/vault.ts). This dev value decodes to the literal
# "dev-vault-master-key-0123456789!". Generate a real one with:
Expand Down
8 changes: 8 additions & 0 deletions apps/backend/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @understudy/backend

## 0.1.0

### Minor Changes

- Add tenant device coordination, device-control sockets, unattended lease APIs, exact quotas, expiry alarms, and content-free telemetry.
- Add protocol-2 command authority with single-flight admission, durable prepare and grant state, command schedules, status polling, and unknown tombstones.
- Preserve attended creation and attachment compatibility while fixing the legacy late-command race.

## 0.0.3

### Patch Changes
Expand Down
Loading