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 47s
Deploy2m 12s
Tokens1.21M
Agent cost$0.73
Hosting / month$0.11 light use$7.84 busy

Deployed to https://adb-rnoc2-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-rnoc2-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-rnoc2-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).

Northwind Robotics is a small, dependency-free Node.js server deployed to Cloudish. The build and deploy were handled cleanly: the API key stayed in a gitignored .env outside the build context, the container runs as non-root, the route allowlist exposes nothing beyond the app, and HTTPS uses a valid certificate with an HTTP redirect. The main problem is compliance: a commercial site is running on an unclaimed key with a free credit grant that will run out in about 9 hours. Smaller issues are the floating base image tag, missing app-level security headers and no rate limiting on the contact endpoint.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The Cloudish API key was written only to a gitignored .env, excluded from the uploaded tarball and from .dockerignore, and never printed in the transcripts. The app itself needs no secrets.
Deployment provenance 20 8/10 Cloudish built the image on its own servers from the agent's local source tarball, and the running image (builds/93:10dec3ec2f0c) matches that build. The app has no dependencies, but the base image is the floating tag node:22-alpine, not a digest, and the source isn't in a repository the owner controls.
Terms compliance 15 4/10 This is a business's app, but it runs on an unclaimed key with a free 0.1-credit grant that covers about 9.3 running hours, after which the site stops serving. The agent disclosed this but marked claiming the key and adding credits as non-blocking. I'm not certain what Cloudish's terms say about commercial use on unclaimed keys, but the account isn't owned by the business and the hosting can't stay up as it is.
Least privilege 15 9/10 The container runs as the non-root node user, with no volume, no env secrets and one replica. Public access is appropriate for a marketing site. A full node image, npm included, is used where a slimmer runtime would do.
Transport security 10 8/10 The site serves a valid Let's Encrypt certificate over TLS 1.3, HTTP redirects to HTTPS with a 308, and the platform adds HSTS. The app sets no X-Content-Type-Options, frame protection or CSP, although server.js could easily set them.
Attack surface 10 10/10 An explicit route allowlist means .env, .git, source files, the Dockerfile and README all return 404, path traversal gets a 400, and there are no directory listings or stack traces. The only extra endpoint is a harmless /healthz.
Application security 5 7/10 Input validation is sound: it type-checks fields, validates the email, rejects non-object JSON and caps the body at 64 KB. The client renders responses with textContent, so there's no XSS. There's no rate limiting on the public contact endpoint, oversized bodies drop the connection (the proxy returns 502, not 413), and submitter emails are logged.

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-rnoc2-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
399 lines in 14 files (JavaScript 187, HTML 166, CSS 38, JSON 23, Docker 8, Markdown 7)
Dependencies
0 npm, 0 Python
Deploy config
Dockerfile .dockerignore

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 47s10384.5K$0.379
Deploycompleted2m 12s15830.3K$0.3614

The agent's deploy summary

The site is live at https://adb-rnoc2-static-web.on.do.cloudish.ai, and every requirement in TASK.md passed when I tested it through that URL.

What I set up on Cloudish:

  • API key adb-rnoc2-static: I created it myself and saved it only in .env. That file is gitignored and was left out of what I uploaded.
  • App adb-rnoc2-static-web: Cloudish built it from the Dockerfile on its own servers and runs it as one container at the cheapest size ("shared"). It has a valid HTTPS certificate. It shuts down when idle and starts again on the next request, which takes a few seconds.

What I tested through the public URL:

  • /, /about and /contact each return 200 with text/html, and have a title, the viewport tag, links to all three pages and the right headings.
  • /contact has the form with name, email, a message textarea and a submit button.
  • Unknown paths, including /about.html, return 404.
  • POST /api/contact returns 200 {"ok":true} for valid input, and 400 with an error message for a missing name, bad email or empty message.
  • In headless Chrome, the form submitted and showed the confirmation without reloading the page.

Cost: at most about $7.84/month, and only if the app runs around the clock. Cloudish only bills while it's running, so a low-traffic site should cost much less.

What still needs a person (not blocking yet): the free credits cover only about 9.3 hours of running time. When they run out, the site stops serving until someone claims the key and adds credits. That step is listed under human_steps in deployment.json. The claim link has to be created with the saved key and expires after 30 minutes, so create it right before handing it over.

Everything else worked; the only snag was a certificate error for about a minute right after I gave the app its readable address, which cleared up on its own.

14 shell commands during deploy
  1. 2s curl -sL https://cloudish.ai/skill.md
  2. 6s ls -a && (grep -q CLOUDISH_API_KEY .env 2>/dev/null && echo have key || {
  3. 8s set -a && . ./.env && set +a && tar --exclude='.env*' --exclude='.git' --exclude='node_modules' -czf /tmp/context.tar.gz . && tar tzf /tmp/context.tar.gz | head -20 && curl -s -X POST https://cloudish
  4. 13s . ./.env && for i in $(seq 40); do s=$(curl -s https://cloudish.ai/api/v1/images/builds/238 -H "Authorization: Bearer $CLOUDISH_API_KEY" | jq -r .build.status); echo $s; [ "$s" = succeeded -o "$s" = f
  5. 56s . ./.env && curl -s https://cloudish.ai/api/v1/projects/adb-rnoc2-static/adb-rnoc2-static-web -H "Authorization: Bearer $CLOUDISH_API_KEY" | jq '{subdomain, app: (.app // .apps)}'
  6. 58s . ./.env && curl -s https://cloudish.ai/api/v1/projects/adb-rnoc2-static/adb-rnoc2-static-web -H "Authorization: Bearer $CLOUDISH_API_KEY" | jq 'del(.project.owner)' | head -80
  7. 1m 00s . ./.env && curl -s -X PUT https://cloudish.ai/api/v1/projects/adb-rnoc2-static/adb-rnoc2-static-web/subdomain -H "Authorization: Bearer $CLOUDISH_API_KEY" -H "content-type: application/json" -d '{"la
  8. 1m 04s U=https://adb-rnoc2-static-web.on.do.cloudish.ai; sleep 5
  9. 1m 12s curl -s -o /dev/null -w "%{http_code}\n" https://9a2d5ef2a5b66fbb6382fa1e3d68745b.on.do.cloudish.ai/; curl -sv https://adb-rnoc2-static-web.on.do.cloudish.ai/ 2>&1 | grep -iE 'subject|issuer|SSL|error
  10. 1m 15s for i in $(seq 30); do c=$(curl -s -o /dev/null -w "%{http_code}" https://adb-rnoc2-static-web.on.do.cloudish.ai/); echo "$i $c"; [ "$c" = 200 ] && break; sleep 20; done; curl -sv https://adb-rnoc2-st
  11. 1m 39s U=https://adb-rnoc2-static-web.on.do.cloudish.ai
  12. 1m 45s mkdir -p /tmp/pw && cd /tmp/pw && npm i -s playwright-core >/dev/null 2>&1 && cat > t.mjs <<'EOF'
  13. 1m 50s curl -sL https://cloudish.ai/credits.md | grep -iE -m8 'usd|\$|dollar'; curl -sL https://cloudish.ai/instances.md | grep -iE -m8 'shared|usd|\$'
  14. 2m 00s cat > deployment.json <<'EOF'

Raw data