Skip to content

Latest commit

 

History

History
191 lines (142 loc) · 6.37 KB

File metadata and controls

191 lines (142 loc) · 6.37 KB

1mins.in — Project Specification

Overview

1mins.in is a multi-tenant SaaS platform for deploying OpenClaw AI agents. Users sign up, pick a plan, pay via Dodo Payments, and get a dedicated OpenClaw instance provisioned automatically on shared cloud workers. Each instance is accessible via a unique subdomain ({name}-{id}.1mins.in).


Tech Stack

Component Technology
Framework Rails 8 (Ruby 3.4.3)
Database PostgreSQL 16
Cache/Queue Redis 6, SolidQueue
Containers Docker (direct docker run, no orchestrator)
Web Server Nginx + Let's Encrypt (wildcard SSL)
Cloud AWS EC2 + Hetzner (cloud-agnostic)
Payments Dodo Payments
Auth Devise + OmniAuth (Google OAuth)
Frontend Hotwire (Turbo + Stimulus), Tailwind CSS

Authentication

  • Primary: Google OAuth (auto-creates User + Tenant on first login)
  • Secondary: Email/password with Devise :confirmable (requires email verification)
  • Gateway auth: Per-project token (gateway_token) for OpenClaw WebChat/API access

Plans & Pricing

Plan Price RAM Memory Limit CPU Node.js Heap Dodo Product
Standard $49/mo 2GB server 1536MB 0.75 vCPU 1280MB DODO_PRODUCT_STARTER
Pro $65/mo 4GB server 3584MB 2.0 vCPU 3072MB DODO_PRODUCT_PRO

Prices synced from Dodo Payments API with 1-hour cache (ServerPlan.sync_prices!).


Infrastructure

Management Server

  • Runs Rails app, PostgreSQL, Redis, Nginx
  • Nginx reverse-proxies *.1mins.in to the correct worker:port
  • Routes updated dynamically by Containers::NginxUpdater

Worker Pool

  • Shared cloud servers running Docker containers
  • Managed via SSH from the management server
  • Auto-provisioned by Workers::NodeProvisioner
  • Auto-terminated by WorkerDrainJob when empty (5-min delay)
  • No Docker Swarm, no Kubernetes

Cloud Providers

  • AWS EC2: c7i-flex.large (4GB, 2 vCPU) — fits 2 Standard or 1 Pro
  • Hetzner: CX22 (4GB), CX32 (8GB) — cheaper alternative
  • Provider selection via CloudProvider model, region-configurable

Domain & Routing

  • Main domain: 1mins.in
  • Wildcard: *.1mins.in with Let's Encrypt wildcard SSL
  • Tenant URLs: https://{project-name}-{project-id}.1mins.in/
  • API URLs: https://{project-name}-{project-id}.1mins.in/api?token={gateway_token}

Nginx config generated at /etc/nginx/tenant-ports.conf:

"mybot-42.1mins.in" 13.200.40.172:18789;
"probot-43.1mins.in" 13.200.40.172:18790;

Container Naming

  • Local workers: openclaw-{subdomain}-{project_id} (e.g., openclaw-devsan-44)
  • Remote workers: openclaw-{project_id} (e.g., openclaw-44)
  • Volumes: {container_name}-data mounted at /home/node/.openclaw

Provisioning Flow

  1. User creates project, selects plan
  2. Redirected to Dodo Payments checkout
  3. Dodo webhook fires on payment → project.mark_paid!
  4. ContainerProvisionJob enqueued
  5. PlacementScheduler.find_slot(plan) finds worker with capacity (best-fit bin-packing)
  6. If no capacity → NodeProvisioner launches new EC2/Hetzner instance
  7. Container deployed via docker run (local) or SSH (remote)
  8. Config injected: docker cp + chown + restart
  9. Nginx routing updated
  10. Project status → running

Key Models

Model Purpose
User Devise user, OAuth provider
Tenant Organization/workspace with subdomain
Project One OpenClaw instance, belongs to Tenant
Container Docker container record, belongs to Project + WorkerNode
WorkerNode Cloud server running containers (AASM state machine)
CloudProvider AWS/Hetzner credentials and config
ServerPlan Plan definitions with Dodo product IDs
LlmConfig Per-project LLM API key storage
UserApiKey Per-user API keys (Anthropic, OpenAI, etc.)

Key Services

Service Purpose
Workers::PlacementScheduler Best-fit bin-packing with FOR UPDATE SKIP LOCKED
Workers::NodeProvisioner Launch EC2/Hetzner, install Docker, pull image
Containers::ConfigBuilder Generate openclaw.json + auth-profiles.json
Containers::Launcher Start/restart containers (local or SSH)
Containers::Stopper Stop containers (local or SSH)
Containers::Destroyer Remove containers, deallocate resources, trigger drain
Containers::HealthChecker Check container health via Docker inspect + HTTP
Containers::NginxUpdater Regenerate nginx routing config
Cloud::AwsProvider EC2 lifecycle (create/destroy/status)
Cloud::HetznerProvider Hetzner lifecycle
Dodo::Client Dodo Payments API (products, subscriptions)

Key Jobs

Job Purpose
ContainerProvisionJob Full provisioning: scheduler → deploy → config → nginx
ContainerHealthJob Per-container health check, self-rescheduling (5 min)
WorkerDrainJob Terminate empty remote workers after 5-min delay
ContainerCleanupJob Clean up old stopped/error containers
ContainerRestartJob Restart a specific container
HealthCheckSchedulerJob Schedule health checks across all running containers

Project States (AASM)

pending_payment → pending → provisioning → running ⇄ stopped
                                              ↓
                                            error → pending (recover)

Worker States (AASM)

provisioning → ready ⇄ full
                 ↓
              draining → terminated
                 ↓
               error

warm_stopped → starting → ready

Current Infrastructure

Node Type Provider Status
#1 Local running
#5 EC2 AWS warm_stopped

Key Services (Additional)

Service Purpose
Workers::WarmPoolManager Maintain 1 stopped EC2 for fast provisioning
Containers::ConfigUpdater Live-update API keys + Telegram tokens without restart

Key Jobs (Additional)

Job Purpose
IntentDetectionJob Pre-warm instance when user visits New Project page
IdleReaperJob Clean up idle warm instances after 5 minutes