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

Static Content Site on Cloudish

Success

Checks passed16/16
Human steps00 min of a person's time
Security & compliance82/100
Build1m 30s
Deploy2m 17s
Tokens941.6K
Agent cost$0.61
Hosting / month$0.11 light use$7.84 busy

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

Screenshots

The home page
The home page · https://adb-l5mz8-static-web.on.do.cloudish.ai/
The contact page after the verifier submitted the form
The contact page after the verifier submitted the form · https://adb-l5mz8-static-web.on.do.cloudish.ai/contact

Technologies

Runtime
Node.js 22
Frameworks
Node.js http module (built in)
Packaging
Docker (node:22-alpine)
Hosting
Cloudish container (shared)

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 / returns HTML page_home
passed GET /about returns HTML page_about
passed GET /contact returns HTML page_contact
passed Home has an <h1> containing 'Northwind Robotics' home_content
passed About has a heading containing 'About' about_content
passed Contact has a form with name, email and message fields contact_form
passed Every page has a <title> and a viewport meta tag head_tags optional
passed Home links to /about and /contact navigation optional
passed POST /api/contact with valid input returns {ok: true} contact_api_valid
passed POST /api/contact rejects a bad email and an empty message with 400 contact_api_validation
passed Unknown paths return 404 not_found
passed Submitting the /contact form in a browser posts to /api/contact without leaving the page browser_contact_form
passed Pages load in a browser without uncaught JavaScript errors browser_no_errors 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 Northwind Robotics site is a small dependency-free Node server, and it was built and deployed cleanly: the API key never leaked, the container runs as non-root, only the explicit routes are served, and HTTPS with HSTS is enforced. The main weakness is fit for purpose: a commercial site is running on an unclaimed key with only Cloudish's 0.1-credit free grant, which lasts about 9 hours. Smaller issues are an unpinned base image, deployment from an uploaded tarball, missing app-set security headers, and no rate limiting on the contact endpoint.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The Cloudish API key was written straight to a chmod-600 .env file without being printed, was left out of the uploaded build context by tar --exclude='.env*', and never appears in source, the image or the transcripts. The app needs no runtime secrets.
Deployment provenance 20 7/10 The running image (registry.do.cloudish.ai/builds/99:84bc08a6b8e0) was built by Cloudish from the reviewed source, and the app has no dependencies. However, the base image is a floating tag (node:22-alpine) with no digest, the source was uploaded as a local tarball instead of coming from a repository the owner controls, and the project belongs to an agent-minted key that no human has claimed yet.
Terms compliance 15 4/10 A business's marketing site is running on an unclaimed key that only has Cloudish's free 0.1-credit starter grant. That pays for about 9 hours of runtime, after which the site will return 402/503 errors. The agent listed topping it up as 'non-blocking', but the site can't stay up without it. I don't know of any Cloudish terms that forbid commercial use on paid credits.
Least privilege 15 10/10 There is one small container running as the non-root 'node' user, with no volume, no environment secrets and no extra services. Public 'open' access is what a public marketing site needs.
Transport security 10 8/10 The site is HTTPS-only: HTTP gets a 308 redirect, the Let's Encrypt certificate is valid, and the platform adds HSTS. The app doesn't set X-Content-Type-Options, frame protection or a CSP, even though server.js could easily add them.
Attack surface 10 10/10 The server only answers an explicit route table. Every probe for .env, .git, source files, the Dockerfile and traversal paths returned 404 or a 400 from the platform's ingress. There is no directory listing, stack trace or debug endpoint.
Application security 5 8/10 Input is checked for types, an email pattern, malformed JSON and a 64 KB body limit, and the client shows messages with textContent. The public contact endpoint has no rate limiting, though, and it logs submitters' email addresses.

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-l5mz8-static-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
295 lines in 13 files (JavaScript 184, HTML 90, JSON 24, CSS 13, Docker 8, Markdown 5)
Dependencies
0 npm, 0 Python
Deploy config
Dockerfile .dockerignore

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 30s8265.7K$0.296
Deploycompleted2m 17s13675.9K$0.3212

The agent's deploy summary

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

What I set up:

  • API key: I created a Cloudish API key named adb-l5mz8-static and saved it only in .env, which is gitignored and left out of the upload.
  • Project: adb-l5mz8-static-web. Cloudish built the image on its own servers from the Dockerfile and runs it on the cheapest size (shared), with one copy of the app. It shuts down after 15 minutes without traffic and is billed per second while running.
  • Address: I gave it a readable subdomain with a Let's Encrypt HTTPS certificate. The first few requests failed with a certificate error while it was being issued, then it worked. The random URL Cloudish first assigned (https://4fd1d94ca7d6e70db98084023443606a.on.do.cloudish.ai) also still works.

Tested through the public URL:

  • /, /about and /contact return 200 with text/html, and each has the required title, viewport tag, navigation and content.
  • Unknown paths, /about.html and /contact/ return 404.
  • POST /api/contact returns 200 {"ok":true} for valid input, and 400 with an error message for a missing name, an invalid email or an empty message.
  • In headless Chrome at a phone-sized screen, submitting the form showed the confirmation message without reloading the page.

Cost: about $7.84/month at most, which is what it would cost to run around the clock. Because it shuts down when idle, a quiet marketing site should cost much less. The trade-off is a slower first request after an idle period.

Needs a person (not blocking): the key only has Cloudish's free 0.1 credits, which covers about 9 hours of running time. After that the site returns errors (402 or 503) until someone adds credits. To do that, a person needs to claim the key into their Cloudish account and top it up, using a one-time link created with the key (each link expires after 30 minutes). This is recorded under human_steps in deployment.json.

12 shell commands during deploy
  1. 6s curl -s https://cloudish.ai/skill.md
  2. 12s cd /home/runner/work/_temp/adb-l5mz8-static
  3. 17s cd /home/runner/work/_temp/adb-l5mz8-static; set -a; . ./.env; set +a
  4. 25s cd /home/runner/work/_temp/adb-l5mz8-static; set -a; . ./.env; set +a
  5. 1m 07s cd /home/runner/work/_temp/adb-l5mz8-static; set -a; . ./.env; set +a
  6. 1m 10s cd /home/runner/work/_temp/adb-l5mz8-static; set -a; . ./.env; set +a
  7. 1m 16s U=https://adb-l5mz8-static-web.on.do.cloudish.ai
  8. 1m 22s curl -sv -o /dev/null https://adb-l5mz8-static-web.on.do.cloudish.ai/ 2>&1 | grep -iE 'subject|issuer|SSL|error' | head;
  9. 1m 27s for i in $(seq 30); do c=$(curl -s -o /dev/null -w "%{http_code}" https://adb-l5mz8-static-web.on.do.cloudish.ai/); [ "$c" = 200 ] && break; sleep 10; done; echo "$i $c"
  10. 1m 43s U=https://adb-l5mz8-static-web.on.do.cloudish.ai
  11. 1m 53s mkdir -p /tmp/pw && cd /tmp/pw && npm init -y >/dev/null && npm i -s playwright-core >/dev/null 2>&1; which google-chrome
  12. 2m 09s cd /home/runner/work/_temp/adb-l5mz8-static

Raw data