Architecture
Component interactions, data locations, and request flows.
Service topology
Slack ───────────────────┐
GitHub Issues ──────┐ │
Jira / Shortcut ─┐ │ │
Custom webhooks ─┼──┼────┤
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ Platform Backend │ ◀──────▶│ Platform Frontend │
│ (Express, :8888) │ REST │ (React, Amplify) │
└─────┬──────┬───────┘ └────────────────────┘
│ │
│ └──────────────┐
│ │
invoke│ │ persist
▼ ▼
┌─────────────────────┐ ┌──────────────┐
│ Worker Execution │ │ PostgreSQL │
│ Service │ │ (RDS) │
│ ─ ECS / Lambda / │ └──────────────┘
│ Docker invokers │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Viberator Worker │
│ (agent harness + │
│ git + SCM API) │
└─────────┬───────────┘
│
▼
GitHub / GitLab
The frontend never talks directly to workers. Workers never talk directly to the frontend. Everything goes through the backend, which is the single source of truth.
Technology stack
Backend (apps/platform-backend)
- Node.js 24+, TypeScript 5.3.
- Express 4 with Helmet,
express-rate-limit, Multer, Joi, Passport (passport-local+ a custom strategy). - Kysely 0.27 for type-safe Postgres queries; migrations in
src/migrations/. - AWS SDK v3 clients for ECS, Lambda, S3, SSM, and CloudWatch.
@chat-adapter/slack4.22 with@chat-adapter/state-pg4.22 for Slack chat state.- Winston with
winston-daily-rotate-filefor logging. node-cronfor the in-process scheduler that drives claws (for deployments that don't use an external scheduler).
Frontend (apps/platform-frontend)
- Vite 6, TypeScript 5.8, React 19, React Router 7.
- Tailwind CSS 4, Radix UI Themes and Dialog primitives,
motionfor animation,sonnerfor toasts. @heroicons/reactand@radix-ui/react-iconsfor iconography.- Jest +
@testing-library/reactfor tests.
Worker (apps/viberator)
- Node.js 24+, TypeScript 5.0, built with
tsup. - Agent SDK:
@anthropic-ai/claude-code2.x for Claude Code; the other agents (Qwen, Gemini, Codex, OpenCode, Mistral, Kimi) are CLIs installed globally inside the Docker images. simple-git3.x for clone/commit/push.- AWS SDK clients for S3 and SSM (credential resolution).
- Winston for structured logs.
Slack package (packages/chat-slack)
Vercel Chat SDK 4.22 wrapped with project-specific handlers for:
slashCommand—/viberatormodal entry point.modalSubmit— ticket creation + agent session launch.threadReply— forwards user replies to the agent.threadMention— handles@viberatorcallouts.approvalAction— handles Approve / Reject button presses on phase approval cards.
Infrastructure (infra/)
- Pulumi (TypeScript) with state stored in S3.
- Three stacks (
base,platform,workers) deployed in order. - AWS services: VPC, KMS, CloudWatch Logs, ECS Fargate, RDS PostgreSQL multi-AZ, S3, Amplify, ALB, Lambda, ECR, SSM Parameter Store, IAM (with OIDC for GitHub Actions).
Request flow: filing a ticket from the web UI
- The frontend or an integration POSTs to
/api/ticketswith title, description, severity, category, starting phase, and optional media. - The backend validates the body, persists the ticket, and (if auto-fix is on or starting phase ≠ research) immediately calls
TicketExecutionService.runTicket(). TicketExecutionServiceresolves the project's clanker, builds a bootstrap payload (repo URL, branch, credentials, prompt template), creates ajobsrow, and hands off toWorkerExecutionService.invokeWorker().WorkerExecutionServicelooks up the clanker's deployment strategy and routes to the appropriate invoker (LambdaInvoker,EcsInvoker, orDockerInvoker).- The worker boots, fetches credentials from SSM, runs the agent harness, streams events back to the backend via a callback token, and on success writes a phase document.
- The frontend polls (or subscribes to) the job and ticket and updates the UI as new events arrive.
Request flow: filing a ticket from Slack
- User runs
/viberatorin a Slack channel. - Slack POSTs to the platform's
/api/slack/commandsroute. The chat handler returns a modal definition. - User submits the modal. Slack POSTs to
/api/slack/interactions. The handler callscreateTicket, thenlaunchSession, then posts the thread root and starts aChatSessionBridgeService. - The bridge polls
agent_session_eventsevery ~2 s and posts new events to the Slack thread. Approval requests become button cards. Thread replies go back throughreplyToSession. - Approve / Reject button clicks become
approvalActioncalls that updateticket_phase_documents.approval_stateandticket_phase_approvals, and (on approve) launch the next phase.
Local vs production
Locally everything runs in docker-compose: PostgreSQL, the backend, the frontend, and a Docker-based worker. In production, the same backend runs on ECS Fargate, the same worker code runs on Lambda or ECS Fargate (or both, depending on which clankers you provision), and the same frontend code is served from Amplify.
The boundary between local and production is the deployment strategy on each clanker. Switch a clanker from docker to aws_lambda_container and it will start running on Lambda the next time it is invoked, with no application changes.