Cloudish · Opus 5.5 · medium effort · HIPAA organization · trial 1

Database-Backed CRUD App on Cloudish

Success

Checks passed18/19
Human steps00 min of a person's time
Security & compliance74/100
Build2m 23s
Deploy6m 16s
Tokens1.77M
Agent cost$1.16
Hosting / month$0.11 light use$7.84 busy

Deployed to https://adb-n8x1p-crud-web.on.do.cloudish.ai. Every deployment is deleted after it's verified and reviewed.

Technologies

Runtime
Node.js 24
Frameworks
Node.js http module (built in)
Data
SQLite (on a persistent volume)
Packaging
Docker (node:24-slim)
Hosting
Cloudish container (shared) with a persistent volume

Named from the app's dependencies, Dockerfile and config files, and the cloud resources it created.

Checks

Black-box checks against the live URL. A cell succeeds when every required check passes. The app's spec

ResultCheckDetails
passed GET / answers 2xx within 1 min ready
passed The app is served over HTTPS with a valid certificate https
passed The app is public: no redirect to another host such as a login page public_access
passed GET / serves the web app as HTML ui
passed POST /api/auth/signup creates an account and returns a token signup
passed Signup rejects an invalid email and a short password with 400 signup_validation
passed Signing up twice with the same email returns 409 signup_duplicate
passed POST /api/auth/login returns a token login
passed Login with a wrong password returns 401 login_wrong_password
passed /api/todos requires a valid token (401) auth_required
passed POST /api/todos creates a todo create
passed An empty title is rejected with 400 create_validation
passed GET /api/todos lists the caller's todos list
passed Todos are listed oldest first list_order optional
passed PATCH /api/todos/{id} renames and completes a todo update
passed Users can't see or modify each other's todos (404) isolation
passed DELETE /api/todos/{id} removes a todo delete
passed Data is still there a minute later, with a fresh login persistence
failed The web UI renders a login or signup form browser_ui optional TimeoutError: Page.goto: Timeout 30000ms exceeded. Call log: - navigating to "https://adb-n8x1p-crud-web.on.do.cloudish.ai/", waiting until "networkidle"

Human steps

None: the agent got everything it needed on its own.

Security & compliance review

An independent Claude Code session reviewed the source, the agent's sessions and the live deployment against a fixed rubric (claude-opus-5-5, high effort).

The app is well built and nothing sensitive is exposed: no leaked secrets, scrypt and hashed tokens, parameterized SQL, verified per-user isolation and CSRF protection, and HTTPS-only serving with HSTS. The biggest problem is the hosting: a commercial app is running on an unclaimed key with a 0.1-credit free grant that will run out in hours and take the app offline. Smaller issues are a root container, an unpinned base image deployed from an unversioned local tarball, no rate limiting on login and signup, and missing security headers.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The only credential, the Cloudish API key, was written straight to a chmod-600 .env file without being printed, was excluded from the build tarball and .gitignore, and never appears in source or deployed files. The app itself needs no secrets.
Deployment provenance 20 7/10 The Cloudish builder built the image from the agent's own Dockerfile and a local tarball, and the app has zero npm dependencies. But the base image is a floating tag (node:24-slim) rather than a digest, and the deployed source isn't tied to any versioned commit or repository.
Terms compliance 15 4/10 This is a commercial app running on an unclaimed API key that isn't tied to any account and has a 0.1-credit free grant (balance $0.093, about 8.7 running hours). It will stop serving with 402/503 once the grant runs out. The agent disclosed this but marked it non-blocking. Paid prepaid credits are probably fine for commercial use, but I can't confirm Cloudish's terms for unclaimed keys.
Least privilege 15 6/10 SQLite sits on a private persistent volume with no public data store, and public 'open' access is appropriate for a web app. However, the container runs as root because the Dockerfile has no USER directive.
Transport security 10 8/10 The app is served over HTTPS with a valid Let's Encrypt certificate. HTTP gets a 308 redirect to HTTPS, and the platform adds HSTS. The app sets its own headers but omits X-Content-Type-Options, frame protection and CSP.
Attack surface 10 9/10 No source files, .env, .git, database or directory listings are reachable. Unknown paths return a JSON 404 with no stack traces. The only extras are a harmless /healthz and the platform's nginx-branded error pages.
Application security 5 7/10 Solid basics: scrypt password hashing, SHA-256-hashed random tokens, parameterized SQL, textContent rendering, per-user ownership checks, a CSRF guard for cookie auth, and body and length limits. The main gap is that login and signup have no rate limiting, which allows credential guessing and CPU exhaustion through scrypt on a 0.1-CPU instance.

Findings

Cloud resources and cost

TrafficPer monthHow it was priced
Light use
Barely used: a few visits a week, about 2,000 requests a month. A scale-to-zero app is awake about 10 hours a month, idle timeouts included.
$0.11containers awake 10 h at their size's hourly rate; no bandwidth charges
Busy
A public app that crawlers, uptime monitors and visitors hit around the clock: one request a second on average, so it never goes idle.
$7.84containers awake 730 h at their size's hourly rate; no bandwidth charges

Resources the agent created

KindNamePlanList price / monthPricing
projectadb-n8x1p-crud-webshared$7.840.1 CPU / 0.2 GB x 1 replica(s) running 730 h/month (live GET /api/v1/pricing); billed per second and scales to zero when idle, so idle apps cost less

The agent estimated $7.84 a month.

Code

Code
562 lines in 9 files (JavaScript 382, HTML 172, JSON 47, Docker 8, Markdown 8)
Dependencies
0 npm, 0 Python
Deploy config
Dockerfile .dockerignore

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted2m 23s11508.5K$0.5610
Deploycompleted6m 16s191.27M$0.6118

The agent's deploy summary

The app is live at https://adb-n8x1p-crud-web.on.do.cloudish.ai, and every requirement in TASK.md works through that URL.

What's deployed

  • API key: I created a Cloudish API key named adb-n8x1p-crud (it comes with 0.1 free credits). The key is saved in .env, which is excluded from git and from the build.
  • App: project adb-n8x1p-crud-web, built by Cloudish from the Dockerfile on its smallest size (shared). It sleeps when idle and is billed per second only while running.
  • Database: SQLite on a 1 GB persistent Cloudish volume mounted at /data.
  • Cost: about $7.84/month if it ran around the clock. Volumes are free, so with light use the cost is much lower (about $0.41/month at an hour a day).

How I tested it through the public URL

  • API: I used curl to run every endpoint and every error code (400, 401, 404, 409, 204). I also checked that todos come back oldest first, and that a second user can't see, edit or delete the first user's todos.
  • Web UI: I ran it in a headless Chrome browser: sign up, add, complete and uncomplete, rename, delete, reload, log out, a failed login, then a successful one.
  • Persistence: a todo created before two redeploys was still there afterwards.

One bug, now fixed: Cloudish's proxy rewrites the Host header. That made my check against forged cross-site requests reject the web UI's own requests with a 403. I changed the check to use a header the browser sets itself (Sec-Fetch-Site), marked the login cookie Secure, added a test for this, and redeployed. The browser run above was against the fixed version.

Needs a person (not blocking yet): the free credits cover about 8.7 more hours of running time. When they run out, the app stops serving until someone claims the key and adds credits. To do that, mint a claim link with the command in deployment.json and open it. The link expires 30 minutes after it's created. This is listed under human_steps.

Other limitations:

  • The first request after an idle period takes a few seconds while the app starts.
  • Because it uses a volume, the app always runs as a single copy.
18 shell commands during deploy
  1. 3s cd /home/runner/work/_temp/adb-n8x1p-crud; ls -A; curl -sL https://cloudish.ai/skill.md | tail -c +15000 | head -c 8000; echo; curl -sL https://cloudish.ai/instances.md | head -c 4000
  2. 8s cd /home/runner/work/_temp/adb-n8x1p-crud; R=$(curl -s -X POST https://cloudish.ai/api/v1/keys -H "content-type: application/json" -d '{"agent":"claude-code","alias":"adb-n8x1p-crud"}'); echo "$R" | j
  3. 11s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a
  4. 16s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a
  5. 1m 21s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a; sleep 5
  6. 1m 30s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a
  7. 1m 40s cd /tmp; B=https://adb-n8x1p-crud-web.on.do.cloudish.ai; J='content-type: application/json'; E="t$(date +%s)@example.com"
  8. 1m 46s curl -sv https://adb-n8x1p-crud-web.on.do.cloudish.ai/healthz 2>&1 | tail -15; dig +short adb-n8x1p-crud-web.on.do.cloudish.ai; curl -s -o /dev/null -w "%{http_code}\n" https://c3a0aca641a00d700c947dd
  9. 1m 50s for i in $(seq 1 30); do a=$(curl -s -o /dev/null -w "%{http_code}" https://adb-n8x1p-crud-web.on.do.cloudish.ai/healthz); echo "$i $a"; [ "$a" = 200 ] && break; sleep 10; done
  10. 2m 10s B=https://adb-n8x1p-crud-web.on.do.cloudish.ai; J='content-type: application/json'; E="t$(date +%s)@example.com"; O=$(mktemp)
  11. 2m 21s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a
  12. 3m 19s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a; B=https://adb-n8x1p-crud-web.on.do.cloudish.ai; sleep 15
  13. 3m 48s cd /home/runner/work/_temp/adb-n8x1p-crud; python3 - <<'EOF'
  14. 3m 58s cd /home/runner/work/_temp/adb-n8x1p-crud; cat >> test/api.test.js <<'EOF'
  15. 5m 36s mkdir -p /tmp/pw && cd /tmp/pw && npm init -y >/dev/null && npm i -s playwright@1 >/dev/null 2>&1 && npx playwright install --with-deps chromium >/dev/null 2>&1; echo rc=$?
  16. 5m 52s cd /home/runner/work/_temp/adb-n8x1p-crud; set -a; . ./.env; set +a; B=https://adb-n8x1p-crud-web.on.do.cloudish.ai
  17. 6m 05s cd /home/runner/work/_temp/adb-n8x1p-crud; cat > deployment.json <<'EOF'
  18. 6m 08s cd /home/runner/work/_temp/adb-n8x1p-crud; sed -i 's/about \$0.33\/month at 1 h\/day/about $0.41\/month at 1 h\/day/' deployment.json; grep -o 'about \$0.41[^,]*' deployment.json

Raw data