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

Static Content Site on DigitalOcean

Success

Checks passed16/16
Human steps37 min of a person's time
Security & compliance90/100
Build1m 28s
Deploy2m 08s
Tokens688.5K
Agent cost$0.61
Hosting / month$5.00 light use$5.00 busy

Deployed to https://adb-6gtad-static-7z8zf.ondigitalocean.app. Every deployment is deleted after it's verified and reviewed.

Screenshots

The home page
The home page · https://adb-6gtad-static-7z8zf.ondigitalocean.app/
The contact page after the verifier submitted the form
The contact page after the verifier submitted the form · https://adb-6gtad-static-7z8zf.ondigitalocean.app/contact

Technologies

Runtime
Node.js 22
Frameworks
Node.js http module (built in)
Packaging
Docker (node:22-alpine)App spec (app.yaml)
Hosting
App Platform service (apps-s-1vcpu-0.5gb)DigitalOcean Container Registry (starter)

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

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 zero-dependency Node.js server, built into a non-root container and run on a paid, always-on DigitalOcean App Platform Basic plan, which fits commercial use. It has no secrets, serves HTTPS only, and exposes nothing beyond the allowlisted routes. The weaknesses are minor: unpinned build inputs (floating base image tag, the mutable `v1` image tag, a doctl download with no checksum check), no security headers, and no rate limiting on the contact endpoint.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The app needs no secrets and contains none. The DigitalOcean token was only used through the $DIGITALOCEAN_ACCESS_TOKEN environment variable and never printed, and no .env or credential file was created or copied into the image.
Deployment provenance 20 7/10 A zero-dependency image was built from the reviewed source and pushed to a private DOCR registry the owner controls. But the base image is an unpinned floating tag (node:22-alpine), the app spec deploys the mutable tag v1 rather than a digest, and the doctl binary was installed with curl | tar without checksum verification.
Terms compliance 15 10/10 For commercial use the app runs on a paid, always-on App Platform Basic instance ($5/month) plus the free Starter registry. DigitalOcean's standard terms allow commercial use, and nothing here expires or sleeps.
Least privilege 15 10/10 The container runs as the non-root node user. There is no data store, the registry is private, and the only public surface is the App Platform HTTPS ingress to the one service.
Transport security 10 7/10 HTTPS works with a valid Google Trust Services certificate, and HTTP returns a 301 redirect to HTTPS. However, the app sends none of HSTS, X-Content-Type-Options, frame protection or CSP, and it could easily have set them in server.js.
Attack surface 10 10/10 Only the whitelisted pages, assets, /healthz and /api/contact are served. Probes for .env, .git, server.js, package.json, Dockerfile, app.yaml and path traversal all returned 404 or 400, with no stack traces or directory listings.
Application security 5 8/10 Input is type-checked and validated, the body is capped at 64 KB (413 above that), malformed JSON gets a clean 400, and the client renders server messages with textContent, so there is no XSS. The public POST endpoint has no rate limiting or anti-spam control of its own.

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.
$5.00fixed monthly prices; 0.0373 GiB out of a 50 GiB allowance
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.
$5.00fixed monthly prices; 49 GiB out of a 50 GiB allowance

Resources the agent created

KindNamePlanList price / monthPricing
appadb-6gtad-staticadb-6gtad-static-web: apps-s-1vcpu-0.5gb x 1$5.00adb-6gtad-static-web: apps-s-1vcpu-0.5gb x 1
registryadb-6gtad-staticstarter$0.00Container Registry starter tier (one per account)

The agent estimated $5.00 a month.

Code

Code
402 lines in 15 files (JavaScript 198, HTML 160, CSS 35, JSON 24, YAML 16, Markdown 10, Docker 9)
Dependencies
0 npm, 0 Python
Deploy config
Dockerfile .dockerignore app.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 28s8296.0K$0.437
Deploycompleted2m 08s9392.5K$0.188

The agent's deploy summary

The site is live at https://adb-6gtad-static-7z8zf.ondigitalocean.app, and every requirement in TASK.md passed when I tested it through that URL.

What I deployed:

  • Container registry adb-6gtad-static (free starter tier): the app's Docker image is stored there as adb-6gtad-static-web:v1.
  • App Platform app adb-6gtad-static (ID dd8c3ea9-fd28-417d-bcc1-1a4c58189fcb): it runs that image as one small instance (apps-s-1vcpu-0.5gb, about $5/month). DigitalOcean provides HTTPS on its default ondigitalocean.app address, and the platform checks /healthz to confirm the app is up.
  • Total cost: about $5/month.

Tested on the public URL:

  • /, /about and /contact return 200 as text/html, with the required headings, title, viewport tag, navigation and form fields.
  • Unknown paths and .html versions of the pages, like /about.html, return 404.
  • POST /api/contact returns 200 with {"ok":true} for valid input. A missing name, a bad email or an empty message each return 400 with an error message.
  • In headless Chrome, submitting the form showed the confirmation message without reloading the page.

Nothing needed a person, so human_steps in deployment.json is empty. I didn't create any credentials, so there's no .env. The app's deployment settings are in app.yaml. To redeploy, push a new image tag, update app.yaml, then run doctl apps update <id> --spec app.yaml.

8 shell commands during deploy
  1. 5s which doctl; docker --version; curl -s -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" https://api.digitalocean.com/v2/registry | head -c 500; echo; curl -s -H "Authorization: Bearer $DIGITALOCE
  2. 9s cd /tmp && curl -sL https://github.com/digitalocean/doctl/releases/download/v1.146.0/doctl-1.146.0-linux-amd64.tar.gz | tar xz && sudo mv doctl /usr/local/bin/ && doctl version && doctl registry creat
  3. 26s doctl registry login >/dev/null && docker build -q --platform linux/amd64 -t registry.digitalocean.com/adb-6gtad-static/adb-6gtad-static-web:v1 . && docker push -q registry.digitalocean.com/adb-6gtad-
  4. 1m 36s doctl apps list --format ID,Spec.Name,DefaultIngress,ActiveDeployment.Phase,InProgressDeployment.Phase
  5. 1m 40s doctl apps list -o json | python3 -c 'import sys,json
  6. 1m 45s U=https://adb-6gtad-static-7z8zf.ondigitalocean.app
  7. 1m 52s mkdir -p /tmp/pw && cd /tmp/pw && npm i -s playwright-core@1 >/dev/null 2>&1; CH=$(which google-chrome || which chromium); echo $CH; cat > t.js <<'EOF'
  8. 2m 01s cat > deployment.json <<'EOF'

Raw data