# AI Agent Instructions - AI Stack Deployer **Project-specific guidelines for AI coding agents** --- ## Project Context This is a self-service portal for deploying OpenCode AI stacks. Users enter their name and get a fully deployed AI assistant at `{name}.ai.flexinit.nl`. **Key Technologies:** - Bun + Hono (backend) - Vanilla HTML/CSS/JS (frontend) - Docker + Dokploy (deployment) - Hetzner DNS API + Traefik (networking) --- ## Critical Information ### API Endpoints #### Hetzner Cloud API (DNS) ```bash # Base URL https://api.hetzner.cloud/v1 # IMPORTANT: Use /zones/{zone_id}/rrsets NOT /dns/zones # The old dns.hetzner.com API is DEPRECATED # List records (RRSets) GET /zones/343733/rrsets Authorization: Bearer {HETZNER_API_TOKEN} # Create DNS record (individual A record for user) # NOTE: Wildcard *.ai.flexinit.nl already exists pointing to Traefik # For per-user records (optional, wildcard handles it): POST /zones/343733/rrsets Authorization: Bearer {HETZNER_API_TOKEN} Content-Type: application/json { "name": "{name}.ai", "type": "A", "ttl": 300, "records": [ { "value": "144.76.116.169", "comment": "AI Stack for {name}" } ] } # Zone ID for flexinit.nl: 343733 # Traefik IP: 144.76.116.169 # Wildcard *.ai.flexinit.nl -> 144.76.116.169 (already configured) ``` #### Dokploy API ```bash # Base URL http://10.100.0.20:3000/api # All requests need: Authorization: Bearer {DOKPLOY_API_TOKEN} Content-Type: application/json # Key endpoints: POST /project.create # Create project POST /application.create # Create application POST /domain.create # Add domain to application POST /application.deploy # Trigger deployment GET /application.one # Get application status ``` ### BWS Secrets | Purpose | BWS ID | |---------|--------| | Dokploy Token | `6b3618fc-ba02-49bc-bdc8-b3c9004087bc` | | Hetzner Token | Search BWS or ask user | ### Infrastructure IPs - **Traefik**: 144.76.116.169 (public, SSL termination) - **Dokploy**: 10.100.0.20:3000 (internal) - **DNS Zone**: flexinit.nl, ID 343733 --- ## Implementation Guidelines ### Backend (Bun + Hono) ```typescript // Use Hono for routing import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { serveStatic } from 'hono/bun'; const app = new Hono(); // Serve frontend app.use('/*', serveStatic({ root: './src/frontend' })); // API routes app.post('/api/deploy', deployHandler); app.get('/api/status/:id', statusHandler); app.get('/api/check/:name', checkHandler); ``` ### SSE Implementation ```typescript // Server-Sent Events for progress updates app.get('/api/status/:id', (c) => { return streamSSE(c, async (stream) => { // Send progress updates await stream.writeSSE({ event: 'progress', data: JSON.stringify({ step: 'dns', status: 'completed' }) }); // ... more updates await stream.writeSSE({ event: 'complete', data: JSON.stringify({ url: 'https://...' }) }); }); }); ``` ### Frontend (Vanilla JS State Machine) ```javascript // State: 'idle' | 'deploying' | 'success' | 'error' let state = 'idle'; function setState(newState, data = {}) { state = newState; render(state, data); } // SSE connection const eventSource = new EventSource(`/api/status/${deploymentId}`); eventSource.addEventListener('progress', (e) => { const data = JSON.parse(e.data); updateProgress(data); }); eventSource.addEventListener('complete', (e) => { setState('success', JSON.parse(e.data)); }); ``` ### Docker Stack Template The user stack needs: 1. OpenCode server (port 8080) 2. ttyd web terminal (port 7681) ```dockerfile FROM git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free:latest # Install ttyd RUN apt-get update && apt-get install -y ttyd # Expose ports EXPOSE 8080 7681 # Start both services CMD ["sh", "-c", "opencode serve --host 0.0.0.0 --port 8080 & ttyd -W -p 7681 opencode attach http://localhost:8080"] ``` --- ## Code Style ### TypeScript - Use strict mode - Prefer `const` over `let` - Use async/await over callbacks - Handle all errors explicitly - Type all function parameters and returns ### CSS - Use CSS variables for theming - Mobile-first responsive design - BEM-like naming: `.component__element--modifier` - Dark theme as default ### JavaScript (Frontend) - No frameworks, vanilla only - Module pattern for organization - Event delegation where possible - Graceful degradation --- ## Testing Checklist Before considering implementation complete: - [ ] Name validation works (alphanumeric, 3-20 chars) - [ ] Reserved names blocked (admin, api, www, root, etc.) - [ ] DNS record created successfully - [ ] Dokploy project created - [ ] Application deployed and healthy - [ ] SSL certificate provisioned - [ ] ttyd accessible in browser - [ ] Error states handled gracefully - [ ] Mobile responsive - [ ] Loading states smooth (no flicker) --- ## Common Gotchas 1. **Hetzner API** - Use `api.hetzner.cloud`, NOT `dns.hetzner.com` (deprecated) 2. **Dokploy domain** - Must create domain AFTER application exists 3. **SSL delay** - Let's Encrypt cert may take 30-60 seconds 4. **ttyd WebSocket** - Needs proper Traefik WebSocket support 5. **Container startup** - OpenCode server takes ~10 seconds to be ready --- ## File Reading Order When starting implementation, read in this order: 1. `README.md` - Full project specification 2. `AGENTS.md` - This file 3. Check existing oh-my-opencode-free for reference patterns --- ## Do NOT - Do NOT use any frontend framework (React, Vue, etc.) - Do NOT add unnecessary dependencies - Do NOT store secrets in code - Do NOT skip error handling - Do NOT make the UI overly complex - Do NOT forget mobile responsiveness --- ## Reference Projects - `~/locale-projects/oh-my-opencode-free` - The stack being deployed - `~/projecten/infrastructure` - Infrastructure patterns and docs