mirror-connections
v0.2.0
Published
Connected context and raw mirror records for agents.
Readme
Mirror
Mirror is connected context infrastructure for agents: Plaid-like provider connection management plus durable local state. Instead of every agent needing its own OAuth apps, API keys, MCP setup, sync jobs, and local caches, Mirror gives the user one connection surface and gives agents a stable API/MCP/CLI surface for reading and acting on those connections.
Mirror owns provider authentication, account lanes, raw source mirrors, approval ledgers, connector MCP tools, connector skills, and the Connections UI. Agents call Mirror for connected context; external product logs can record those calls as ordinary tool events.
Product Model
- A user connects accounts once through Mirror.
- Each provider/account pair gets a
ConnectionLaneDO. - Raw provider records are stored as versioned mirror records.
- Live reads use provider runtimes when available and fall back to mirrored records when not.
- Write-like provider actions are captured as exact approval requests before execution.
- D1 is for canonical shared product state, not primary chat or mirror history.
- R2 stores large provider blobs, attachments, and generated artifacts.
- MCP exposes
mirror.*tools so agents can discover providers, query records, fetch sources, run live reads, sync lanes, and request approval-wrapped actions.
Cloudflare Shape
This repo publishes Mirror as a single Cloudflare Worker:
- Worker script:
mirror - D1 database:
mirror - R2 bucket:
mirror-blobs - Code-facing bindings:
MIRROR_DBandMIRROR_BLOBS - Durable Object classes include
ConnectionLaneDO,UserVaultDO, andSkillRegistryDO - Connection callback/webhook paths stay under
/v1/connections/*
User-facing UI, docs, MCP names, login app ids, and provider display text should say Mirror.
Runtime Surface
Active Mirror surfaces:
GET /api/healthGET /api/catalog/connectionsGET|POST /api/users/:userId/connections/*GET|POST /api/users/:userId/connections/mirrorGET /api/users/:userId/sources/:sourceId/v1/connections/*OAuth callback and webhook ingressPOST /mcpwith Mirror MCP tools- The static browser UI served from
web/distthrough the WorkerASSETSbinding
Legacy non-connection Durable Object exports and some old API/CLI code remain in the repo for stored-data preservation and migration continuity. They should not be expanded as Mirror product surface.
Local Development
Install dependencies from the repo root and web workspace:
npm install
npm --prefix web installRun the Worker and Vite UI together:
npm run devLocal ports:
- Worker/API/MCP:
http://127.0.0.1:8787 - Vite UI: Vite chooses its normal dev port and proxies
/apiand/mcpto the Worker
Useful local checks:
npm run check
npm run build:web
env HOME="$PWD/.wrangler-home" npx wrangler types --check
env HOME="$PWD/.wrangler-home" npx wrangler deploy --env production --dry-runUse a workspace-local HOME for Wrangler in restricted environments so Wrangler can write logs without touching host preference directories.
Cloudflare Deploy
Production deploy bundles the UI into web/dist and deploys the Worker plus assets:
npm run deploy:setup
npm run deploy:dry
npm run deployRun npm run deploy:setup once per Cloudflare account to create the
mirror-connection-sync Queue used by the production Worker binding.
npm run deploy checks that queue before building or uploading assets.
Production deploys the Mirror Worker and bundled UI assets.
For the clean post-Vesper Cloudflare cutover, use docs/cloudflare-mirror-cutover.md. The short version: secrets must be set on the mirror Worker, provider redirect URIs must point at the Mirror origin, and old Vesper Cloudflare resources should remain available until connection validation passes.
Current test URL:
https://mirror.jasonjbadeaux.workers.devTo bind Mirror to a branded Cloudflare domain:
- Pick a hostname on a Cloudflare-managed zone, for example
mirror.example.com. - In
wrangler.jsonc, underenv.production, uncomment/add:
"routes": [{ "pattern": "mirror.example.com", "custom_domain": true }]- Update
env.production.vars.APP_PUBLIC_URLto the samehttps://mirror.example.comorigin. - Add the same origin/callbacks to provider OAuth apps:
https://mirror.example.com/v1/connections/oauth/callback
https://mirror.example.com/v1/connections/mcp/oauth/callback/:provider- Run
npm run build:webandenv HOME="$PWD/.wrangler-home" npx wrangler deploy --env production --dry-run. - Deploy with
npm run deploy.
Secrets And Provider Setup
Set secrets with Wrangler, not wrangler.jsonc:
wrangler secret put MIRROR_SHARED_SECRET --env production
wrangler secret put CONNECTION_OAUTH_STATE_SECRET --env production
wrangler secret put CONNECTION_TOKEN_ENCRYPTION_KEY --env production
wrangler secret put CONNECTION_WEBHOOK_SIGNING_SECRET --env productionProvider-specific secrets are only required for providers you want to enable:
- Google:
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET - Strava:
STRAVA_CLIENT_ID,STRAVA_CLIENT_SECRET - Polar:
POLAR_CLIENT_ID,POLAR_CLIENT_SECRET - Spotify:
SPOTIFY_CLIENT_ID,SPOTIFY_CLIENT_SECRET - HubSpot MCP:
HUBSPOT_MCP_CLIENT_ID,HUBSPOT_MCP_CLIENT_SECRET - Slack MCP/events:
SLACK_MCP_CLIENT_ID,SLACK_MCP_CLIENT_SECRET,SLACK_EVENTS_SIGNING_SECRET - GitHub MCP:
GITHUB_MCP_CLIENT_ID,GITHUB_MCP_CLIENT_SECRET - Refero fallback:
REFERO_MCP_BEARER_TOKEN - AgentCash Worker-side bridge, optional:
AGENTCASH_MCP_URL,AGENTCASH_MCP_BRIDGE_TOKEN - Plaid:
PLAID_CLIENT_ID,PLAID_SECRET
Figma MCP is also provider-gated: set non-secret FIGMA_MCP_CLIENT_NAME only after Figma has allowlisted Mirror's Worker origin and callback URL for remote MCP access.
wrangler.jsonc keeps non-secret defaults such as Plaid environment, public URL, and AgentCash spend caps.
CONNECTION_OAUTH_TOKEN_SECRET is accepted only as a deprecated transition alias for CONNECTION_TOKEN_ENCRYPTION_KEY. New deployments should set the canonical secret. OAuth state signing must use CONNECTION_OAUTH_STATE_SECRET in production; it does not fall back to the shared bearer secret.
Webhook ingress uses signed lane tokens created with CONNECTION_WEBHOOK_SIGNING_SECRET. Production Slack events also require Slack request signatures, and Google/Slack webhook URLs should not rely on query-string userId or accountId routing.
For public production deployments, add Cloudflare WAF rate limiting rules that mirror the app-level limits for /mcp, /v1/connections/*/webhook, /v1/connections/*/events, OAuth callback paths, and agent-binding pairing paths.
End-To-End Smoke
With npm run dev running:
curl http://127.0.0.1:8787/api/health
curl http://127.0.0.1:8787/api/catalog/connections
npm run test:smoke
npm run test:connectionstest:smoke currently aliases test:connections. test:connections verifies catalog readiness, connection registration, mirror source ingestion, live-read fallback, aggregate mirror reads, source fetches, custom MCP setup, sync, approvals, and MCP tool behavior.
Mirror CLI
The npm package is mirror-connections and exposes the mirror binary:
npm install -g mirror-connections
mirror configure --api-url https://mirror.example.com --user USER_ID --api-token TOKEN
mirror mcp config --include-token
mirror connections catalog
mirror connections list
mirror skills seed
mirror skills sync --runtime codex --project .The CLI uses MIRROR_HOME, MIRROR_PROFILE, MIRROR_API_URL,
MIRROR_USER_ID, MIRROR_BRANCH_ID, and MIRROR_API_TOKEN.
Durable Releases
GitHub Actions owns repeatable package publishes and Worker deploys.
For npm, prefer npm Trusted Publishing for package mirror-connections:
- provider: GitHub Actions
- repository:
jasonbadeaux/mirror - workflow filename:
publish-cli.yml - allowed action:
npm publish
As a fallback, set repository secret NPM_TOKEN to a granular npm automation
token with package read/write access and 2FA bypass enabled. Publish by pushing
a matching tag such as cli-v0.1.4, or manually run the Publish CLI workflow.
The package version in package.json and cli/mirror.mjs must match.
For Worker deploys, set repository secrets CLOUDFLARE_ACCOUNT_ID and
CLOUDFLARE_API_TOKEN, then run the Deploy Worker workflow or push a
deploy-v* tag. Runtime secrets such as MIRROR_SHARED_SECRET,
CONNECTION_OAUTH_STATE_SECRET, CONNECTION_TOKEN_ENCRYPTION_KEY,
CONNECTION_WEBHOOK_SIGNING_SECRET, and provider credentials still live in
Cloudflare Worker secrets, not GitHub source.
Current Readiness Notes
- TypeScript check passes.
- Vite production build passes.
- Wrangler production dry-run bundles Worker assets and shows the expected D1, R2, Durable Object, and asset bindings.
- Generated Wrangler types are current.
- Active connection smoke passes against a local Worker.
- Production is deployed and remotely reachable at the Mirror workers.dev URL.
- A Mirror custom domain is not active in config yet because the target hostname is not known. Add the
routesentry and updateAPP_PUBLIC_URLonce the domain is chosen. - Legacy docs, CLI packaging, and non-connection routes still contain old product concepts and should be reduced or moved out as a follow-up cleanup.
