Files
ai-stack-deployer/docs/AGENTS.md
Oussama Douhou 19845880e3 fix(ci): trigger workflow on main branch to enable :latest tag
Changes:
- Create Gitea workflow for ai-stack-deployer
- Trigger on main branch (default branch)
- Use oussamadouhou + REGISTRY_TOKEN for authentication
- Build from ./Dockerfile

This enables :latest tag creation via {{is_default_branch}}.

Tags created:
- git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest
- git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:<sha>

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 23:33:39 +01:00

5.8 KiB

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)

# 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

# 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)

// 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

// 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)

// 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)
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