# Amanat Assist Mini App - Deployment Guide **Codename:** `amanat-assist` **Version:** 1.0 **Last Updated:** 2026-06-05 **Owner:** Deployment --- ## ๐ŸŽฏ Overview This document describes the deployment architecture for the Amanat Assist Telegram Mini App, using: - **Frontend:** Vite + React (static hosting) - **LLM Edge Function:** Cloudflare Workers (server-side LLM calls) - **Backend:** Amanat API (`dev.amn.gg` or `amn.gg`) --- ## ๐Ÿ“ Architecture Diagram ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Telegram Client โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ–ผ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Mini App (Static) โ”‚ โ”‚ LLM Edge Function โ”‚ โ”‚ - React + Vite โ”‚ โ”‚ - Cloudflare Workers โ”‚ โ”‚ - Hosted on CF Pages โ”‚ โ”‚ - Route: /api/llm โ”‚ โ”‚ - URL: assist.amn.gg โ”‚ โ”‚ - Handles auth + LLM calls โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ LLM Providers โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Mistral โ”‚ โ”‚ Kimi โ”‚ โ”‚ DeepSeek โ”‚ โ”‚ OpenCode โ”‚ โ”‚ โ”‚ โ”‚ (Primary) โ”‚ โ”‚ (Fallback) โ”‚ โ”‚ (Fallback) โ”‚ โ”‚ (Local) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Amanat Backend โ”‚ โ”‚ - POST /api/auth/telegram (Telegram SSO) โ”‚ โ”‚ - GET /api/marketplace/categories โ”‚ โ”‚ - POST /api/files/upload (File upload) โ”‚ โ”‚ - POST /api/marketplace/purchase-requests (Submit w/ aiGenerated) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ๐Ÿš€ Quick Start ### Prerequisites 1. **Cloudflare Account** with Workers + Pages enabled 2. **Amanat Backend** running at `dev.amn.gg` (already deployed โœ…) 3. **LLM API Keys** (at least one): - Mistral: `sk_...` - Kimi: `sk_...` - DeepSeek: `sk_...` 4. **Domain** configured in Cloudflare: `assist.amn.gg` (or subdomain) --- ## ๐Ÿ“ฆ Step 1: Deploy Static Frontend (Cloudflare Pages) ### 1.1 Create Cloudflare Pages Project ```bash # Navigate to project cd /Users/manwe/CascadeProjects/escrow/amanat-assist # Install dependencies npm install # Build production bundle npm run build ``` ### 1.2 Cloudflare Dashboard Setup 1. Go to: [https://dash.cloudflare.com](https://dash.cloudflare.com) 2. Select your account โ†’ **Workers & Pages** โ†’ **Create application** โ†’ **Pages** 3. **Connect Git repository** (if using Git) OR **Upload files** 4. **Project name:** `amanat-assist` 5. **Production branch:** `main` (or your deployment branch) 6. **Build command:** `npm run build` 7. **Build output directory:** `dist` 8. **Environment variables:** (see Section 3) ### 1.3 Configure Custom Domain 1. In Pages project โ†’ **Custom domains** โ†’ **Set up custom domain** 2. Enter: `assist.amn.gg` 3. Cloudflare will issue SSL certificate automatically 4. Wait for DNS propagation (~5-10 minutes) --- ## โ˜๏ธ Step 2: Deploy LLM Edge Function (Cloudflare Workers) ### 2.1 Create Worker 1. Go to: [https://dash.cloudflare.com](https://dash.cloudflare.com) 2. Select your account โ†’ **Workers & Pages** โ†’ **Create service** โ†’ **Worker** 3. **Service name:** `amanat-assist-llm` 4. **Starter:** `Fetch handler` ### 2.2 Worker Code Create `index.ts`: ```typescript // src/index.ts for Cloudflare Worker interface LLMRequest { messages: Array<{ role: 'user' | 'assistant'; content: string }>; provider?: 'mistral' | 'kimi' | 'deepseek' | 'opencode'; model?: string; } interface ProviderConfig { baseUrl: string; apiKeyEnv: string; chatEndpoint: string; model: string; } const PROVIDERS: Record = { mistral: { baseUrl: 'https://api.mistral.ai', apiKeyEnv: 'MISTRAL_API_KEY', chatEndpoint: '/v1/chat/completions', model: 'mistral-large-latest', }, kimi: { baseUrl: 'https://api.moonshot.cn', apiKeyEnv: 'KIMI_API_KEY', chatEndpoint: '/v1/chat/completions', model: 'moonshot-v1-8k', }, deepseek: { baseUrl: 'https://api.deepseek.com', apiKeyEnv: 'DEEPSEEK_API_KEY', chatEndpoint: '/chat/completions', model: 'deepseek-chat', }, opencode: { baseUrl: 'http://127.0.0.1:3456', apiKeyEnv: '', chatEndpoint: '/v1/messages', model: 'claude-3-sonnet', }, }; export default { async fetch(request: Request, env: Env): Promise { // Only allow POST if (request.method !== 'POST') { return new Response(JSON.stringify({ error: 'Method not allowed' }), { status: 405, headers: { 'Content-Type': 'application/json', 'Allow': 'POST' }, }); } // Validate origin (optional - for production) const origin = request.headers.get('origin'); const allowedOrigins = [ 'https://assist.amn.gg', 'https://dev.amn.gg', 'https://amn.gg', ]; if (origin && !allowedOrigins.some(o => origin.startsWith(o))) { return new Response(JSON.stringify({ error: 'Origin not allowed' }), { status: 403, headers: { 'Content-Type': 'application/json' }, }); } try { const body: LLMRequest = await request.json(); const provider = body.provider || 'mistral'; const config = PROVIDERS[provider]; if (!config) { return new Response(JSON.stringify({ error: `Unknown provider: ${provider}` }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Get API key from environment const apiKey = config.apiKeyEnv ? env[config.apiKeyEnv] : ''; if (config.apiKeyEnv && !apiKey) { return new Response(JSON.stringify({ error: `API key for ${provider} not configured` }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Build request for the provider const headers: Record = { 'Content-Type': 'application/json' }; if (apiKey) { headers['Authorization'] = `Bearer ${apiKey}`; } const model = body.model || config.model; const messages = body.messages; // Format request based on provider let providerBody: any; if (provider === 'opencode') { providerBody = { model, messages: messages.map(m => ({ role: m.role, content: m.content, })), max_tokens: 1024, }; } else { providerBody = { model, messages, temperature: 0.7, }; } // Call the LLM provider const providerResponse = await fetch(`${config.baseUrl}${config.chatEndpoint}`, { method: 'POST', headers, body: JSON.stringify(providerBody), }); if (!providerResponse.ok) { const error = await providerResponse.text(); return new Response(JSON.stringify({ error: `LLM error: ${providerResponse.status} - ${error}` }), { status: providerResponse.status, headers: { 'Content-Type': 'application/json' }, }); } const data = await providerResponse.json(); // Extract content based on provider let content: string; if (provider === 'opencode') { content = data.content?.[0]?.text || data.content || JSON.stringify(data); } else { content = data.choices?.[0]?.message?.content || JSON.stringify(data); } return new Response(JSON.stringify({ content, model: data.model || model }), { status: 200, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': origin || '*', 'Access-Control-Allow-Methods': 'POST', }, }); } catch (err) { return new Response(JSON.stringify({ error: `Internal error: ${err instanceof Error ? err.message : String(err)}` }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } }, }; // Type for Cloudflare environment variables export interface Env { MISTRAL_API_KEY?: string; KIMI_API_KEY?: string; DEEPSEEK_API_KEY?: string; } ``` ### 2.3 Configure Worker Settings 1. **Routes:** `assist.amn.gg/api/llm/*` (or `assist.amn.gg/api/llm`) 2. **Environment variables:** Add your LLM API keys 3. **Enable CORS:** Handled in code --- ## โš™๏ธ Step 3: Environment Variables ### 3.1 Frontend (Cloudflare Pages) | Variable | Value | Required | Notes | |----------|-------|----------|-------| | `VITE_AMANAT_API_BASE` | `https://dev.amn.gg` | โœ… | Amanat backend | | `VITE_LLM_PROVIDER` | `mistral` | โœ… | Primary LLM provider | | `VITE_LLM_API_URL` | `https://assist.amn.gg/api/llm` | โœ… | Edge function URL | **Optional:** - `VITE_OPENCODE_PROXY_URL` - If using local proxy ### 3.2 Edge Function (Cloudflare Worker) | Variable | Value | Required | |----------|-------|----------| | `MISTRAL_API_KEY` | Your Mistral key | โœ… (if using Mistral) | | `KIMI_API_KEY` | Your Kimi key | โŒ | | `DEEPSEEK_API_KEY` | Your DeepSeek key | โŒ | --- ## ๐Ÿค– Step 4: Configure Telegram Mini App ### 4.1 Create Telegram Bot 1. Open [@BotFather](https://t.me/BotFather) in Telegram 2. Send `/newbot` 3. Follow prompts to create bot 4. **Save the bot token** (needed for backend Telegram webhook) ### 4.2 Enable Mini App 1. In [@BotFather](https://t.me/BotFather), send `/mybots` 2. Select your bot 3. Go to **Bot Settings** โ†’ **Mini App** 4. Set **URL:** `https://assist.amn.gg` 5. Enable **Inline mode** (optional) ### 4.3 Configure Bot Menu (Optional) 1. In [@BotFather](https://t.me/BotFather), send `/setcommands` 2. Set commands: ``` start - Open Amanat Assist help - Show help auth - Re-authenticate ``` --- ## ๐Ÿงช Step 5: Test Deployment ### 5.1 Test Frontend ```bash # Local test npm run dev # Open: http://localhost:3000 # Production test # Open: https://assist.amn.gg ``` ### 5.2 Test Edge Function ```bash # Direct test curl -X POST https://assist.amn.gg/api/llm \ -H "Content-Type: application/json" \ -d '{ "messages": [{"role": "user", "content": "Hello"}], "provider": "mistral", "model": "mistral-large-latest" }' ``` ### 5.3 Test via Telegram 1. Open your bot in Telegram 2. Click the Mini App button 3. Verify: - โœ… Silent auth (no login prompt) - โœ… Greeting message appears - โœ… Chat works - โœ… File upload works - โœ… Submit creates request in Amanat - โœ… AI badge appears in Amanat UI --- ## ๐Ÿ“Š Monitoring & Logging ### Cloudflare Workers 1. Go to: [https://dash.cloudflare.com](https://dash.cloudflare.com) 2. Workers & Pages โ†’ Your Worker โ†’ **Logs** 3. View real-time requests and errors ### Cloudflare Pages 1. Workers & Pages โ†’ Your Pages project โ†’ **Deployments** 2. View build logs and deployment status --- ## โš ๏ธ Security Considerations ### 1. Origin Validation The edge function validates the `Origin` header to prevent unauthorized access: - Allowed: `https://assist.amn.gg`, `https://dev.amn.gg`, `https://amn.gg` - **Action:** Update the `allowedOrigins` array if adding new domains ### 2. API Key Protection - **Never** expose LLM API keys in frontend code - All LLM calls go through the edge function - API keys stored only in Worker environment variables ### 3. Rate Limiting (Recommended) Add to Worker `wrangler.toml`: ```toml [triggers] crons = ["*/5 * * * *"] # Optional: cleanup # Rate limiting via Cloudflare # Configure in Cloudflare Dashboard โ†’ Workers โ†’ Rate Limiting ``` --- ## ๐Ÿ”„ Update Process ### Frontend Updates ```bash # Make changes npm run build # Push to Git (if using Git integration) git add . && git commit -m "..." && git push # Cloudflare Pages auto-deploys ``` ### Worker Updates ```bash # Make changes # Deploy via Wrangler or Dashboard npx wrangler deploy ``` --- ## ๐Ÿ“ž Support & Troubleshooting ### Common Issues | Issue | Solution | |-------|----------| | CORS errors | Verify `Access-Control-Allow-Origin` in Worker | | 403 from LLM | Check API key in Worker environment | | 404 on /api/llm | Verify Worker route is configured | | Telegram auth fails | Verify backend `/api/auth/telegram` endpoint | | No AI badge | Verify backend schema changes (PRD ยง12) | ### Debug Mode Add to frontend `.env`: ```bash VITE_DEBUG=true ``` --- ## ๐Ÿ“š References - **PRD:** `/Users/manwe/CascadeProjects/escrow/nick-doc/PRD - AI Request Assistant Mini App.md` - **Backend PRD ยง12:** Already implemented (commits `6da6e27`, `1ef9b95`) - **Cloudflare Workers Docs:** [https://developers.cloudflare.com/workers/](https://developers.cloudflare.com/workers/) - **Cloudflare Pages Docs:** [https://developers.cloudflare.com/pages/](https://developers.cloudflare.com/pages/) - **Telegram Mini App Docs:** [https://core.telegram.org/bots/webapps](https://core.telegram.org/bots/webapps) --- *Document version: 1.0 โ€” 2026-06-05* *Owner: Deployment Team*