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 & compliance89/100
Build1m 28s
Deploy1m 38s
Tokens650.9K
Agent cost$0.61
Hosting / month$5.00 light use$5.00 busy

Deployed to https://adb-v5ocj-static-wvfsx.ondigitalocean.app. Every deployment is deleted after it's verified and reviewed.

Screenshots

The home page
The home page · https://adb-v5ocj-static-wvfsx.ondigitalocean.app/
The contact page after the verifier submitted the form
The contact page after the verifier submitted the form · https://adb-v5ocj-static-wvfsx.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 container that runs as a non-root user on a paid DigitalOcean App Platform service, which fits commercial use. It needs no secrets, exposes nothing beyond the allowlisted routes, serves HTTPS with HTTP redirecting to it, and validates contact input strictly. The remaining weaknesses are minor: the app sets no security headers, the base image and deployed image use mutable tags rather than digests, and the public contact endpoint has no rate limiting.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The app needs no secrets, and none appear in the source, Dockerfile, image or transcripts. The registry login used doctl's short-lived credential (`--expiry-seconds 3600`) with its output discarded.
Deployment provenance 20 7/10 The image was built locally from the reviewed sources and pushed to the owner's own DOCR registry, and it has no npm dependencies. But the base image `node:22-alpine` is a floating tag, the app deploys the mutable tag `v1` rather than a digest, and doctl was downloaded without a checksum check.
Terms compliance 15 10/10 This is a commercial app on a paid App Platform Basic service ($5/mo, always on) plus the free DOCR starter tier. DigitalOcean's standard terms allow commercial use on these plans, and nothing relies on an expiring or sleeping free tier.
Least privilege 15 10/10 There is one public web service running as the non-root `node` user, a private registry, no data stores and no extra components or credentials.
Transport security 10 7/10 HTTPS is served with a valid Google Trust Services certificate, and HTTP returns 301 to HTTPS. However, the responses carry no HSTS, X-Content-Type-Options, frame-protection or CSP headers, and the app's own server could easily have set them.
Attack surface 10 9/10 Only allowlisted routes are served. Probes for .env, .git, the source, package.json, the Dockerfile and path traversal all return 404 or 400, with no stack traces. The only extra endpoint is a harmless /healthz.
Application security 5 8/10 Input validation is strict (type checks, an email regex, length limits, a 64 KB body cap, JSON-object check), and output goes through textContent with no XSS sinks. The public contact endpoint has no rate limiting, 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.
$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-v5ocj-staticadb-v5ocj-static-web: apps-s-1vcpu-0.5gb x 1$5.00adb-v5ocj-static-web: apps-s-1vcpu-0.5gb x 1
registryadb-v5ocj-staticstarter$0.00Container Registry starter tier (one per account)

The agent estimated $5.00 a month.

Code

Code
425 lines in 15 files (JavaScript 211, HTML 173, CSS 33, JSON 24, YAML 15, Docker 8, Markdown 8)
Dependencies
0 npm, 0 Python
Deploy config
Dockerfile .dockerignore app.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 28s8297.8K$0.457
Deploycompleted1m 38s8353.1K$0.177

The agent's deploy summary

The site is live at https://adb-v5ocj-static-wvfsx.ondigitalocean.app, and every requirement in TASK.md worked when I tested it through that URL.

What's running:

  • Container Registry adb-v5ocj-static (free starter tier, region nyc3). It holds the Docker image built from the existing Dockerfile, as adb-v5ocj-static-web:v1.
  • App Platform app adb-v5ocj-static (ID 788d691f-88ff-4216-8bf4-f36d0c1b947f). It runs that image as one service, adb-v5ocj-static-web, on the smallest paid size (1 vCPU, 0.5 GB) in the nyc region. HTTPS comes from DigitalOcean's default ondigitalocean.app domain. The app's configuration is saved in app.yaml.
  • Estimated cost: about $5/month, all of it the App Platform service.

Why this setup: App Platform's free static-site option can't handle POST /api/contact, so one small service runs the whole site. I deployed from a container image rather than from a git repository because no GitHub account is connected to DigitalOcean.

Checks through the public URL:

  • /, /about and /contact return 200 as text/html, with the required headings and form fields.
  • Unknown paths and paths with .html (like /about.html) return 404.
  • POST /api/contact returns 200 with {"ok":true} for valid input. It returns 400 with an error message for a missing name, a bad email, or an empty message.
  • In a headless Chrome browser, filling in and submitting the form showed the confirmation message without reloading the page. That closes the gap I flagged in step 1.

Nothing needed a person, so human_steps in deployment.json is empty, and I didn't create any credentials, so there's no .env. To ship a new version, push a new image tag, update the tag in app.yaml, then run doctl apps update 788d691f-88ff-4216-8bf4-f36d0c1b947f --spec app.yaml.

7 shell commands during deploy
  1. 6s which doctl || (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 | head -
  2. 10s doctl registry create adb-v5ocj-static --subscription-tier starter --region nyc3 && doctl registry login --expiry-seconds 3600 >/dev/null && docker build -q -t registry.digitalocean.com/adb-v5ocj-stat
  3. 32s cat > app.yaml <<'EOF'
  4. 56s doctl apps list --format ID,Spec.Name,DefaultIngress,ActiveDeployment.ID,InProgressDeployment.ID
  5. 1m 00s U=https://adb-v5ocj-static-wvfsx.ondigitalocean.app
  6. 1m 07s mkdir -p /tmp/pw && cd /tmp/pw && npm init -y >/dev/null && npm i -s playwright@1 >/dev/null 2>&1 && npx playwright install chromium >/dev/null 2>&1 && cat > t.js <<'EOF'
  7. 1m 30s cat > deployment.json <<'EOF'

Raw data