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

Database-Backed CRUD App on Cloudish

Success

Checks passed19/19
Human steps00 min of a person's time
Security & compliance76/100
Build1m 51s
Deploy4m 09s
Tokens980.1K
Agent cost$0.73
Hosting / month$0.11 light use$7.84 busy

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

Screenshots

The web app before signing in
The web app before signing in · https://adb-w1uix-crud-web.on.do.cloudish.ai/
The todo list after signing in through the web UI as the test user
The todo list after signing in through the web UI as the test user · https://adb-w1uix-crud-web.on.do.cloudish.ai/

Technologies

Runtime
Node.js 24
Frameworks
Express
Data
SQLite (on a persistent volume)
Libraries
bcrypt.js
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
passed The web UI renders a login or signup form browser_ui optional

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 todo app was built and deployed carefully: the Cloudish key never left .env or reached the build context, the app runs the owner's own lockfile-installed code, the SQLite data sits on a private volume, and the code uses parameterized, owner-scoped queries over HTTPS with HSTS. The biggest problem is commercial fitness: the business app runs on an unclaimed free 0.1-credit grant that the agent estimates will run out in about 9 hours. Smaller issues are a root container, an unpinned base image, no rate limiting on login and signup, missing security headers, and platform CORS that reflects any origin with credentials, made worse by an auth cookie the app doesn't need.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The Cloudish API key was written straight to .env without being printed, .env was excluded from git, the Docker context and the uploaded tarball, and the app needs no runtime secrets (session tokens are random and stored only as SHA-256 hashes).
Deployment provenance 20 7/10 The live image is the server-side build of the agent's own tarball, with dependencies installed by `npm ci` from a lockfile. The base image `node:24-slim` is a mutable tag with no digest, and the account is an unclaimed agent-minted key that the owner doesn't control yet.
Terms compliance 15 4/10 This is a commercial app running on a free 0.1-credit grant on an unclaimed key. By the agent's own estimate that covers about 9 hours, after which the URL returns 402/503, yet the agent marked the top-up step as non-blocking. I know of no Cloudish term that rules out commercial use (I couldn't find published terms), so the main problem is that the hosting won't stay up.
Least privilege 15 7/10 The SQLite database sits on a private attached volume and the only public resource is the web app itself. The container runs as root because the Dockerfile has no USER directive.
Transport security 10 8/10 HTTPS works with a valid Let's Encrypt certificate, HTTP gets a 308 redirect and the platform adds HSTS. The app sets no X-Content-Type-Options, frame protection or CSP, and the platform documents these as the image's job.
Attack surface 10 9/10 No source files, .env, .git or directory listings are reachable, and errors return generic JSON. Unknown paths get Express's default 'Cannot GET' HTML page, and /healthz is a harmless public endpoint.
Application security 5 6/10 Queries are parameterized, every todo query is scoped to the owner, input is validated, the UI renders text with textContent and passwords use bcrypt. However, login and signup have no rate limiting, and the app accepts an unneeded auth cookie while the platform reflects any Origin with credentialed CORS.

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-w1uix-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
377 lines in 11 files (JavaScript 254, HTML 112, JSON 29, Docker 11, Markdown 7)
Dependencies
2 npm, 0 Python
Deploy config
Dockerfile .dockerignore

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 51s10394.9K$0.419
Deploycompleted4m 09s10585.3K$0.329

The agent's deploy summary

The todo app is live at https://adb-w1uix-crud-web.on.do.cloudish.ai, and every requirement in TASK.md works through that URL. The catch is money: the free credits only cover about 9 hours of running time.

What's deployed

  • API key adb-w1uix-crud: created myself with one unauthenticated call and saved in .env. I never printed it, and it isn't in the deployed code.
  • App adb-w1uix-crud-web: Cloudish built it from the Dockerfile on its own servers. It runs on the cheapest size (shared) as a single container, and shuts down after 15 minutes without traffic.
  • 1 GB disk at /data: this holds the SQLite database. Disks aren't billed.

What I tested through the public URL

  • / returns the web page.
  • Signup returns 201, and 400 for a bad email, a password under 8 characters, or an empty todo title.
  • Signup with an email that's already registered returns 409.
  • Login returns 200, and 401 for a wrong password; the response contains only the token.
  • Todo requests without a token return 401.
  • Creating, listing (oldest first), renaming, completing, uncompleting and deleting todos all work.
  • A second user can't see the first user's todos, and gets 404 when trying to change or delete them.
  • I redeployed a new build, and the earlier todos were still there afterwards, so the data survives redeploys.

Cost

  • Up to $7.84/month if the app runs all month. With scale-to-zero it costs less, depending on traffic.
  • The first request after an idle period takes a few seconds while the app starts.

Human step (listed in deployment.json, not blocking yet)

  • The key has about 0.095 credits left.
  • Once that runs out, the URL returns a 402/503 error until someone adds credits.
  • To add credits, a person needs to request a claim link with GET /api/v1/credits/claim, using the key in .env. The link expires after 30 minutes. They then open it in a browser, sign in and buy credits.
9 shell commands during deploy
  1. 2s curl -sL https://cloudish.ai/skill.md; echo ====; curl -sL https://cloudish.ai/instances.md
  2. 7s ls -a; grep -q CLOUDISH_API_KEY .env 2>/dev/null && echo HAVE || {
  3. 11s set -a && . ./.env && set +a && tar --exclude='.env*' --exclude='./node_modules' --exclude='./.git' --exclude='./test' --exclude='./TASK.md' -czf /tmp/context.tar.gz . && tar tzf /tmp/context.tar.gz &
  4. 14s . ./.env && for i in $(seq 40); do S=$(curl -s https://cloudish.ai/api/v1/images/builds/229 -H "Authorization: Bearer $CLOUDISH_API_KEY"); st=$(echo "$S" | jq -r .build.status); [ "$st" = succeeded -o
  5. 1m 28s . ./.env && curl -s https://cloudish.ai/api/v1/projects/adb-w1uix-crud/adb-w1uix-crud-web -H "Authorization: Bearer $CLOUDISH_API_KEY" | jq 'del(.project.owner)'
  6. 1m 32s . ./.env && curl -s -X PUT https://cloudish.ai/api/v1/projects/adb-w1uix-crud/adb-w1uix-crud-web/subdomain -H "Authorization: Bearer $CLOUDISH_API_KEY" -H "content-type: application/json" -d '{"label"
  7. 2m 18s B=https://adb-w1uix-crud-web.on.do.cloudish.ai; J='content-type: application/json'; E="u$RANDOM@ex.com"; E2="v$RANDOM@ex.com"
  8. 2m 27s . ./.env && BID=$(curl -s -X POST https://cloudish.ai/api/v1/projects -H "Authorization: Bearer $CLOUDISH_API_KEY" -F name=adb-w1uix-crud-web -F port=8080 -F volumeEnabled=true -F volumeSizeGb=1 -F vo
  9. 4m 01s cat > deployment.json <<'EOF'

Raw data