Architecture
Technical architecture and design decisions
Architecture
System overview
npm package (profer)
├── MCP server (3 tools: publish, get, update)
└── API client (HTTP to Supabase edge functions)
Supabase
├── cp_pages (id, title, html, questions, version, status, webhook_url)
├── cp_feedback (id, page_id, version, answers, reviewer, source)
├── Edge function: /pages (auth CRUD)
└── Edge function: /v/:id (public page + feedback submit)Design decisions
Why Supabase
- Postgres + Edge Functions in one service
- No extra hosting or infrastructure to manage
- Edge functions run close to users globally
- Built-in auth and row-level security (available if needed)
Why the agent generates HTML
- No templates to maintain
- No renderer to debug
- Works with any agent (Claude, GPT, Gemini, etc.)
- Agents are already good at generating HTML
- Maximum flexibility for content type
Why sandboxed iframes
Agent HTML could contain anything — including malicious JavaScript. We render it inside:
<iframe sandbox="allow-same-origin" srcdoc="...">Plus Content Security Policy headers:
script-src 'nonce-{random}'; style-src 'unsafe-inline';This means agent scripts can't execute, but styling works fine.
Why short IDs
PF-K8M3N format uses:
- 2-letter prefix (
PF= Profer) - 5 characters from an unambiguous alphabet (A-Z + 2-9, excluding 0/O/I/1/L)
- Rejection sampling eliminates modulo bias from
crypto.getRandomValues() - ~24M possible IDs — sufficient for the foreseeable future
Why structured questions over comments
Unstructured comments are hard for agents to parse. "I think option B is better but we should also consider..." — is that an approval? A rejection? A maybe?
Structured questions give agents typed data:
approve→"yes"|"no"|"needs_changes"choice→ one of the provided optionsmulti→ array of selected optionstext→ free string (when structure isn't possible)
Security model
| Layer | Protection |
|---|---|
| Publishing | API key in Authorization header |
| Page viewing | Public (by design) |
| Feedback submission | Public (no auth, validated against question schema) |
| Agent HTML | Sandboxed iframe + CSP |
| API key comparison | Constant-time to prevent timing attacks |
| Answer validation | Server-side validation against question types and options |