// side projects

Projects

Three experiments in agent-native infrastructure — small pieces of glue that let my AI assistants talk to each other, and render into a real browser tab, instead of trapping everything in chat windows.

bridge chatgpt-natasha-bridge A private, authenticated relay between ChatGPT and my assistant Natasha — no copy-paste, no shared credentials. cast cast-relay Real-time relay so my assistant can push rich HTML and charts into any browser tab, instead of walls of chat text. json agent-native-site A Vercel-hosted homepage whose entire content is a JSON blob the assistant can rewrite — the prototype that led to cast.
Heads up: these are personal sandboxes. Endpoints below work, but authenticated features and the good demos are wired to my own accounts. What you see live is a stripped-down version.
bridge

chatgpt-natasha-bridge

A private, authenticated relay between two AI agents — ChatGPT and my personal assistant

I have two AI assistants I talk to constantly. ChatGPT is where I brainstorm, research, and think out loud. Natasha is my personal assistant on OpenClaw — she has my finances, my calendar, my health records, my house context. Until this project the only bridge between them was me: copy from one window, paste into the other, retype the question, repeat. That's slow and lossy.

The goal was a secure, low-friction channel so ChatGPT can hand a task or question directly to Natasha, and Natasha's answer lands in a place I already read (my Telegram). No sharing of accounts. No exposed secrets. No copy-paste.

A real exchange between ChatGPT (left) and Natasha on Telegram (right) — the bridge in action
a real exchange — ChatGPT Custom GPT ⇄ Natasha via the bridge, 09 Sep 2026

Architecture

Five moving parts, each doing one thing:

Security model

Stack

Python 3.12FastAPIUvicornTelethon (MTProto)ngrokPydanticOpenAPI 3.1ChatGPT Custom GPT ActionSystemd-style supervisor + cron

The exchange, end to end

1.  Me → ChatGPT (Natasha Relay GPT):
    "Tell Natasha the bridge works"

2.  ChatGPT → decides to call sendToNatasha
    POST https://<public>.ngrok-free.dev/send
    Authorization: Bearer <43-char key>
    { "text": "The bridge works." }

3.  bridge.py → validates key, prepends marker,
    hands text to Telethon → sends as DM.

4.  Telegram delivers to Natasha:
    [via ChatGPT] The bridge works.

5.  Natasha replies. I see the answer in Telegram.

Why it took longer than it should have

Estimated at ~1 hour. Took two days. The blocker was Telegram's own anti-phishing protection: when you're logging Telethon into an account, Telegram emits a 5-digit login code — and if it ever sees those digits sent as a message from your own account, it auto-invalidates the login while still returning "success" to the API. I was pasting the code back to Natasha via Telegram to relay it into the login script. Same account → self-triggered lockout, silently, every attempt.

The fix was obvious in hindsight: send the code through any channel that isn't Telegram. We used email — I sent the 5 digits to Gmail, Natasha read via IMAP, submitted to Telethon in ~5 seconds. First try worked. Documented so I never lose those two days again.

Resilience

Runs on OpenClaw hostCost: $0/moSelf-healing

Public URL: reachable but returns 401 without the key. The OpenAPI schema is served from the bridge itself at /openapi.yaml for anyone who's been granted access.

cast

cast-relay

Real-time relay from AI assistant → any browser tab

Chat is a lousy medium for anything visual. If my assistant wants to show me a product comparison, a chart, a map, or a formatted table, the chat window flattens all of it into a scrolling wall of markdown. cast-relay fixes that by giving the assistant a second screen to render into.

How it works

The service has three pieces:

The interesting part is the command vocabulary. The receiver knows how to render four command types:

The unlock

Because iframe embedding is blocked almost everywhere, the killer pattern is display_html: the assistant fetches the underlying data, applies my personal context (size, budget, location, preferences), and renders a bespoke view in that tab. The proof-of-concept was a four-tab mini-site for Garmin Fenix 8 watch bands — Overview / Options / Comparison / Recommendation — pushed as a single ~13 KB HTML payload.

How to try it

Mint a code from a shell, then open the receiver in your browser:

curl -s -X POST https://cast-relay-two.vercel.app/api/cast/new
# → { "code": "CAST-A7X2" }

open https://cast-relay-two.vercel.app/cast/CAST-A7X2

Then push a command:

curl -s -X POST \
  https://cast-relay-two.vercel.app/api/cast/CAST-A7X2/command \
  -H "Content-Type: application/json" \
  -d '{"type":"display_html","html":"<h1>Hello from the relay</h1>"}'

The receiver tab updates instantly. If it shows ○ disconnected, the tab isn't open or lost focus — reload it.

Next.jsVercelServer-Sent EventsREST

Live: cast.schmidtmarcelo.com

🔴 Live receiver: cast.schmidtmarcelo.com/cast/latest — shows the latest cast card, auto-refreshes every 5 seconds.

json

agent-native-site

First agent-deployed Vercel project — the prototype that led to cast

Before cast-relay there was agent-native-site. The idea was blunter: what if a website's homepage was just a container the assistant could take over? Instead of a fixed HTML file, the page fetches its own content from a JSON blob the assistant owns. Change the JSON, refresh the page, new site.

How it works

This became the mental model for cast-relay. cast just replaced "fetch on load" with "push in real time" and added a session code so multiple screens can each have their own view.

How to try it

Open the live URL — it renders whatever's currently in the JSON blob:

open https://agent-native-site-silk.vercel.app

The visible content changes whenever the assistant updates the underlying document. You'll get whatever snapshot happens to be there right now.

Next.jsVercelJSON-driven

Live: agent-native-site-silk.vercel.app · Created: May 2026


Where this is going

The direction is a small library of reusable card types — product, comparison, chart, checklist, map, decision-tree — described by JSON the receiver can render natively, without the assistant having to ship raw HTML each time. Same idea as an agent-native design system: the assistant emits intent, the receiver renders it well.

← Back home