Projects

Client project · ChainGPT

Dotfun Agents Platforms

Multi-tenant agent factory on EKS — GitOps provision, in-cluster mesh, DNS at the edge.

Thousands

concurrent isolated agents

1 chart, N apps

Helm + Argo CD API

ExternalDNS

Cloudflare records, no tickets

HPA 5–50

control plane on the same cluster

Overview

Dotfun Agents Platforms let anyone create an AI agent from a persona, turn it on, and get a live instance with its own public URL. Agents could chat, post, and bind Twitter, Telegram, or Discord. Activation was credit-gated: pay to run, deactivate to tear the instance down.

Users did not wait on a ticket or a shared bot. Each request became an isolated Kubernetes workload. I designed and ran the path from “user clicked Create” to a healthy, addressable agent — and the reverse on deactivate. I sat with backend so the NestJS control plane could drive Argo CD as an API, not as a console. Control plane and agents ran on the same EKS cluster. The API talked to pods over ClusterIP. Public traffic went Ingress → Cloudflare.

The problem

Thousands of tenants only works if create and destroy are APIs, not git commits and DNS tickets. Without that, the factory becomes a queue.

  • A git commit per agent would storm the GitOps repo as tenant count grew
  • Hand-rolled DNS and TLS would not keep up with unique hostnames
  • A shared bot or a ticket-based provisioner would not isolate noisy neighbors
  • If the control plane sat off-cluster, readiness, character push, and chat would depend on the public edge
  • Deactivate that does not prune Ingress and DNS leaks compute and records
  • Credits had to map to real CPU and memory or the cluster is an open fork-bomb

The solution

Two planes, one cluster. The NestJS API is a GitOps app. Each agent is an Argo CD Application pointing at one shared Helm chart. No per-agent YAML in git. Desired state in Argo, cluster as the source of truth, APIs as the user-facing trigger.

Control

NestJS API — users, credits, character store, activate/deactivate. Kustomize, ECR, HPA 5–50

Data

One ElizaOS process per agent. Argo CD Application per tenant, same cluster

Provision

API POSTs an Argo Application with a service token; automated sync, prune, self-heal

Helm

Shared eliza-agent chart. Per-tenant name, id, image, backend URL. ClusterIP Service; Ingress is the only public door

Runtime

On the order of 2 CPU / 2–3Gi, probes on :3000, rolling update maxUnavailable: 0. Character JSON pulled on boot

DNS

Ingress host {slug}.<zone> → ExternalDNS → Cloudflare. Prune Ingress, record goes away

Edge

Wildcard TLS, nginx terminates HTTPS, Cloudflare in front

CI

OIDC → Secrets Manager → ECR → Argo image set. Same GitOps model as the agents

Architecture

lifecycle.txt

Create (metadata + credits)
  → Activate
      → Argo Application (Helm)
      → Deployment + Service + Ingress
      → ExternalDNS → Cloudflare
      → Pod boot → pull character → POST runtime id
      → ACTIVE (in-cluster + public URL)
  → Live: chat, social bind, character patch via ClusterIP
  → Deactivate
      → DELETE Application
      → prune workload + Ingress
      → ExternalDNS drops Cloudflare record

What I implemented

  • Path from activate to a healthy, addressable agent — and teardown on deactivate
  • Backend integration so NestJS drives Argo CD as an API (Application CR, service token), not a console
  • Shared Helm chart with per-tenant params; automated sync, prune, and self-heal — no per-agent YAML in git
  • Same-cluster placement: API polls readiness, pushes character updates, and proxies chat over ClusterIP / in-cluster DNS
  • ExternalDNS watching Ingress so unique FQDNs appear and disappear in Cloudflare with the tenant
  • Wildcard TLS at nginx; Cloudflare at the edge
  • Control plane GitOps: OIDC to AWS, ECR, kustomize overlays, HPA 5–50 so the API can absorb activate/status/chat at tenant scale
  • Credit gate mapped to real CPU/memory so unused agents do not sit on the cluster

Results

  • Thousands of concurrent agents, each with its own hostname, TLS, and runtime
  • Isolation: one Deployment / Service / Ingress / DNS name per tenant — noisy neighbor stays in its slice
  • No git storm: Applications created via API; the chart stays one repo path
  • Teardown is cheap: delete Application; prune and ExternalDNS reclaim compute and DNS
  • In-cluster control loop: readiness and config do not depend on the public edge
  • Self-heal: Argo reapplies drift; agents come back with the same hostname

Stack

AWS EKSArgo CDHelmKustomizeExternalDNSCloudflarenginx IngressNestJSElizaOSGitHub Actions

Have a project in mind? Let's build something together.