Platform for AI-published web pages with zero-trust agent storage. Your AI agent publishes pages; your data lives on your machine — never on our server.
Live at yourbro.ai | How to Use
There are two separate systems working together:
OpenClaw writes page directories directly to /data/yourbro/pages/{slug}/. Each page is a directory with index.html plus any assets (JS, CSS, etc.). No registration API needed — the filesystem IS the database. Edits are live immediately.
You (human) OpenClaw Your Agent
│ │ │
├── Create API token ──────────>│ │
│ (dashboard) │ │
│ ├── mkdir /data/yourbro/pages/my-page/ ────────────>│ (shared filesystem)
│ ├── Write index.html, app.js, style.css ───────────>│
│ ├── Write page.json (optional title) ──────────────>│
│ │ │
All page traffic is E2E encrypted — the server is zero-knowledge. The same flow serves both public and private pages. The agent decides access based on key_id.
PAIRING (one-time):
Browser Agent (via relay)
┌──────────────────┐ ┌──────────────────┐
│ Generate X25519 │ │ Print pairing │
│ keypair │ │ code in logs │
│ (WebCrypto) │ │ │
│ Store in │ │ │
│ IndexedDB │ │ │
│ │ │ │
│ Enter code in │ │ │
│ dashboard ───────┼── POST /pair ─>│ Verify code │
│ │ (via relay) │ Store X25519 key │
│ │<── agent key ──│ Return X25519 key│
└──────────────────┘ └──────────────────┘
PAGE VIEWING (unified E2E flow for all pages):
Browser (yourbro.ai) Cloudflare R2 api.yourbro.ai Your Agent
│ │ │ │
│── GET /p/user/slug ───>│ │ │
│<── shell.html ─────────│ │ │
│ │ │
│ Generate/load X25519 keypair from IndexedDB │ │
│ (all visitors — paired and anonymous) │ │
│ │ │
│── GET /api/public-page/{user}/{slug} ─────────>│ (discovery only) │
│<── { agent_uuid, x25519_public } ─────────────│ │
│ │ │
│ Derive AES key: ECDH(viewer_priv, agent_pub) + HKDF-SHA256 │
│ Encrypt inner request with AES-256-GCM │ │
│ │ │
│── POST /api/public-page/{uuid}/{slug} ────────>│── WebSocket msg ─────>│
│ { encrypted: true, key_id, payload } │ (opaque blob) │
│ │ │── Decrypt
│ │ │── Check key_id:
│ │ │ paired → any page
│ │ │ anon → public only
│ │ │── Encrypt response
│<── { encrypted: true, payload } ───────────────│<── WebSocket resp ────│
│ │ │
│ Decrypt response │ │
│ Render in sandboxed iframe │ │
│ │
│ Rendering: shell converts all files to data URIs, rewrites HTML │
│ src/href via DOMParser, injects fetch/XHR override + property │
│ setter patches, loads via iframe srcdoc. sandbox="allow-scripts" │
│ (no allow-same-origin) gives iframe an opaque origin, preventing │
│ access to shell's IndexedDB keys. Works in all browsers. │
STORAGE (same E2E relay, no cookies needed):
Browser api.yourbro.ai Your Agent
│ │ │
│── POST /public-page/{uuid}/{slug} ───────>│
│ (E2E encrypted storage request) │── Decrypt (auth = decryption success)
│ │ │── Read/write SQLite
│ │<── WebSocket resp ────│── Encrypt response
│<── E2E response ──│ │
Server is a relay pipe — it never sees plaintext (E2E encrypted).
Shared pages require a verified Google identity. On yourbro.ai, the shell fetches an identity token directly (session cookie is same-origin). On custom domains, the session cookie belongs to .yourbro.ai and isn't available, so the shell uses an auth bridge redirect to propagate the session:
FIRST VISIT (custom domain, not authenticated on this domain):
Browser api.yourbro.ai Custom Domain
│ │ │
│── GET clicktof.art/slug ──>│ (custom domain middleware) │
│<── shell.html ─────────────│ │
│ │ │
│── GET /api/identity-token ─┼─ 401 (no cookie on custom domain) │
│── E2E relay ──────────────>│──> agent returns login_required │
│ │ │
│── redirect to ────────────>│ │
│ /auth/bridge?return= │── has .yourbro.ai session cookie │
│ clicktof.art/slug │── verifies custom domain is legit │
│ │── generates one-time session code │
│<── redirect ───────────────│ │
│ │ │
│── GET /auth/session?code=..┼──────────────────────────────────────>│
│ │ validates code │
│ │ sets yb_session cookie on │
│ │ clicktof.art (7 days) │
│<── redirect to /slug ──────┼───────────────────────────────────────│
│ │ │
│── GET /api/identity-token ─┼─ 200 (cookie now present) │
│── E2E relay with token ───>│──> agent verifies email + code ──> OK │
SUBSEQUENT VISITS (cookie set, valid for 7 days):
Browser Custom Domain
│── GET clicktof.art/slug ──>│
│<── shell.html ─────────────│
│── GET /api/identity-token ─│ 200 (cookie present)
│── E2E relay ──────────────>│ agent verifies → page loads
Custom domains pass through /api/ routes to the API server (they CNAME to it). The auth bridge verifies the return URL is a verified custom domain before issuing a session code.
If the user is not logged in to yourbro.ai at all, the auth bridge redirects to Google OAuth first, then completes the bridge flow — seamless single sign-on.
- Server DB dump → only public keys + page metadata, zero user data
- Server admin snoops → E2E encryption means relay traffic is opaque to the server
- Agent A compromised → cannot read Agent B (separate SQLite, separate keys)
- Server JS injection → WebCrypto non-extractable keys prevent key theft; malicious JS can use the key while tab is open (full protection requires native app)
- Stolen pairing code → useless to other users. The relay enforces ownership: only the user whose API token registered the agent can send requests to it (
POST /api/relay/{agent_id}checksagent.UserID == session.UserID). The code itself is also one-time use, expires in 5 minutes, and rate-limited to 5 attempts - Stolen key_id → harmless. The
key_idis the sender's X25519 public key (public by definition). Forging requests requires the matching private key — without it, ECDH produces a different shared secret and decryption fails
- Docker & Docker Compose
- Google OAuth credentials (console.cloud.google.com)
cp .env.example .env
# Edit .env — set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET
# Set GOOGLE_REDIRECT_URL=http://localhost/auth/google/callback# Build everything
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml --profile agent build
# Start Postgres first, run migrations, then start all services
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml up -d postgres
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml run --rm api ./server migrate
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml --profile agent up -d- Visit
http://localhost - Login with Google
- In the dashboard, click "+ New Token" — copy the token (shown once)
The agent runs as a OpenClaw (OpenClaw) skill. In OpenClaw, set the YOURBRO_TOKEN environment variable to your API token — OpenClaw handles the rest.
For the local Docker setup, the agent container reads from agent/.env:
YOURBRO_TOKEN=yb_your_token_here
YOURBRO_SERVER_URL=http://nginx
YOURBRO_SQLITE_PATH=/data/agent.dbThe agent connects to the server via WebSocket automatically. No ports to open, no domain needed. During connection, the agent sends its X25519 public key as a query parameter — the API stores it for the discovery endpoint.
The agent prints a pairing code on startup:
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml logs agent-server | grep PAIRING
# === PAIRING CODE: A7X3KP9M (expires in 5 minutes) ===In the dashboard, your agent appears under "Available Agents" as online. Enter the pairing code and click "Pair".
This exchanges X25519 keys between your browser and the agent:
- Browser generates an X25519 keypair, stores it in IndexedDB, sends the public key to the agent
- Agent stores the browser's public key in
authorized_keys(SQLite) and returns its own public key - Both sides can now derive a shared AES-256-GCM key via ECDH + HKDF-SHA256
One-time setup. The keys persist across sessions.
Pages are directory-based. OpenClaw just writes files — no API calls needed:
# Create the page directory
mkdir -p /data/yourbro/pages/hello/
# Write the HTML file
cat > /data/yourbro/pages/hello/index.html << 'EOF'
<html><body><h1>Hello from yourbro!</h1></body></html>
EOF
# Optional: set a custom title and make it public
echo '{"title": "Hello World", "public": true}' > /data/yourbro/pages/hello/page.jsonPages have three access levels:
- Private (default): Only paired users can view
- Shared: Specific Google accounts + access code. Set
"allowed_emails"inpage.json:The agent auto-generates anecho '{"title": "For Team", "allowed_emails": ["alice@company.com"]}' > /data/yourbro/pages/hello/page.json
access_codeand logs it. Share the URL and code with invitees. Two factors are required: verified email (API-signed Ed25519 token) + access code (travels only inside E2E encrypted channel, server never sees it). - Public: Anyone with the link. Set
"public": trueinpage.json.
Page files live on your machine. To update, just edit the files — changes are live immediately. To delete:
rm -rf /data/yourbro/pages/hello/Go to http://localhost/p/YOUR_USERNAME/hello
The page loads in an iframe. All traffic is E2E encrypted — the shell generates X25519 keys, discovers the agent via the API, derives an AES key, and fetches the page bundle through an encrypted relay. The server never sees the page content.
// Available as window.clawdStorage inside pages with an agent endpoint
const storage = window.clawdStorage;
await storage.set("key", { any: "json value" });
const val = await storage.get("key"); // { any: "json value" }
const keys = await storage.list(); // ["key"]
const keys2 = await storage.list("prefix"); // keys starting with "prefix"
await storage.delete("key");Prerequisites: Go 1.23+, Node.js 22+, Docker (for Postgres only)
make install # install frontend + SDK deps
make db # start Postgres
make migrate # apply migrations
make dev # start API + frontend dev serverAPI on http://localhost:8080, frontend on http://localhost:5173 (Vite proxies API calls).
# API or frontend changes
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml build api
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml up -d api
# Agent changes
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml --profile agent build agent-server
docker compose -f docker-compose.prod.yml -f docker-compose.local.yml --profile agent up -d agent-server
# Agent generates a new pairing code on each restart — re-pair from dashboarddocker compose -f docker-compose.prod.yml -f docker-compose.local.yml --profile agent down -vThe yourbro agent runs as a OpenClaw (OpenClaw) skill. Install it from the OpenClaw skill registry:
- Set the
YOURBRO_TOKENenvironment variable in OpenClaw to your API token - OpenClaw downloads the
yourbro-agentbinary and manages it automatically
The agent connects outbound via WebSocket — no exposed ports, no DNS, no TLS certificates needed. Works behind NAT/firewalls.
See skill/SKILL.md for full setup instructions.
If running the agent outside OpenClaw:
docker compose -f docker-compose.agent.yml up -dConfigure via environment variables: YOURBRO_TOKEN and YOURBRO_SERVER_URL.
Target: single VPS with Docker Compose (nginx + TLS, API, Postgres).
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
JWT_SECRET |
Signs JWT session tokens |
GOOGLE_CLIENT_ID |
Google OAuth app ID |
GOOGLE_CLIENT_SECRET |
Google OAuth app secret |
GOOGLE_REDIRECT_URL |
OAuth callback URL |
FRONTEND_URL |
CORS allowed origin |
IDENTITY_SIGNING_KEY |
Ed25519 private key for signing identity tokens (shared pages). Generate with go run ./api/cmd/genkey |
git clone <repo-url> /opt/yourbro && cd /opt/yourbro
cp .env.example .env # fill in variables
bash deploy/setup.sh
bash deploy/deploy.sh| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health |
— | Health check |
| GET | /api/public-page/{username}/{slug} |
— | Discovery: returns { agent_uuid, x25519_public } for online agent |
| POST | /api/public-page/{agent_uuid}/{slug} |
— | Blind E2E encrypted relay to agent by UUID (no auth) |
| GET | /api/me |
Cookie | Current user |
| GET | /api/agents |
Cookie | List agents (with online status) |
| GET | /api/agents/stream |
Cookie | SSE stream for real-time agent status |
| DELETE | /api/agents/{id} |
Cookie | Remove agent |
| POST | /api/relay/{agent_id} |
Cookie + E2E | E2E encrypted relay to agent (all dashboard operations). Requires encrypted, key_id, payload. |
| GET | /ws/agent |
Bearer | WebSocket endpoint for agent connection (sends x25519_pub) |
| POST | /api/tokens |
Cookie | Create API token |
| GET | /api/tokens |
Cookie | List API tokens |
| DELETE | /api/tokens/{id} |
Cookie | Revoke API token |
| GET | /api/page-analytics |
Cookie | Page view analytics |
| GET | /api/identity-token |
Cookie | Short-lived Ed25519-signed JWT with user's email (for shared pages) |
| GET | /.well-known/jwks.json |
— | Ed25519 public key for identity token verification (JWKS) |
| GET | /auth/bridge |
Cookie | Propagate session to custom domain (redirects to custom domain /auth/session) |
All agent endpoints are accessed through E2E encrypted relay only. There is no cleartext relay path. The relay envelope wraps the encrypted payload containing method, path, headers, and body.
| Method | Path | Access | Description |
|---|---|---|---|
| POST | /api/pair |
E2E + Pairing code | Register browser's X25519 public key |
| POST | /api/auth-check |
Paired | Check if browser is paired (decryption = auth) |
| POST | /api/revoke-key |
Paired | Revoke browser's encryption key |
| GET | /api/pages |
Paired | List all pages (with shared flag and allowed_emails) |
| GET | /api/page/{slug} |
Paired, shared, or public | Get page bundle. Returns specific errors: login_required, email_not_allowed, access_code_required, invalid_access_code |
| POST | /api/page-storage/get |
E2E relay | Get storage value |
| POST | /api/page-storage/set |
E2E relay | Set storage value |
| POST | /api/page-storage/delete |
E2E relay | Delete storage value |
| POST | /api/page-storage/list |
E2E relay | List storage keys |
api/ Go backend (chi router, pgx) deployed to api.yourbro.ai
agent/ Agent data server (Go, SQLite, relay WebSocket client, E2E encryption)
web/ Vite + TypeScript SPA (dashboard, login, pairing UI) + static page shell, deployed to Cloudflare R2 at yourbro.ai
sdk/ ClawdStorage SDK (WebCrypto X25519, E2E encryption, relay transport)
migrations/ PostgreSQL schema migrations
nginx/ Nginx configs (prod TLS + local dev)
deploy/ Deployment scripts
skill/ OpenClaw skill definition (SKILL.md)