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

Real-Time Collaborative App on DigitalOcean

Success

Checks passed13/13
Human steps37 min of a person's time
Security & compliance86/100
Build1m 36s
Deploy1m 30s
Tokens715.0K
Agent cost$0.56
Hosting / month$5.00 light use$5.00 busy

Deployed to https://adb-pzps1-realtime-nd5yo.ondigitalocean.app. Every deployment is deleted after it's verified and reviewed.

Screenshots

The room the verifier's clients chatted in, with its history
The room the verifier's clients chatted in, with its history · https://adb-pzps1-realtime-nd5yo.ondigitalocean.app/?room=bench-ebed2ad2

Technologies

Runtime
Node.js 22
Frameworks
Node.js http module (built in)
Libraries
ws
Packaging
Docker (node:22-alpine)App spec (.do/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 / serves the chat UI as HTML ui
passed deployment.json declares a supported transport (websocket or sse) transport optional
passed Clients connect over websocket connect
passed A message reaches another client in the same room within 5 s delivery
passed The sender receives its own message too echo optional
passed Messages never reach clients in other rooms room_isolation
passed Five users posting at the same time all see every message within 10 s fanout
passed GET /api/rooms/<room>/messages returns recent messages history
passed Messages over 1000 characters are rejected validation optional
passed The chat UI loads in a browser without errors browser_ui 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 chat app is deployed cleanly on a paid, always-on App Platform Basic instance that fits a personal club. It needs no secrets, runs as a non-root user, enforces HTTPS and exposes nothing beyond the app. The main weaknesses are provenance and abuse resistance. The image was built locally from code with no repository, using a base image and deploy tag that aren't pinned to a digest. The app has no rate limiting, never frees rooms it creates on demand, doesn't check the WebSocket Origin and sets no security headers.

CriterionWeightScoreWhy
Secrets handling 25 9/10 The app needs no secrets and none appear in source, the image or the logs. The DigitalOcean token was only used through an environment variable. The one minor point is that `doctl registry login` left a Docker registry credential valid for 30 days on the build host.
Deployment provenance 20 6/10 The image was built on the agent's machine and pushed straight to the owner's private registry, with no source repository or CI behind it. The base image `node:22-alpine` and the tag `v1` can both change and are not pinned by digest. The `ws` dependency uses a caret range, and doctl was downloaded without a checksum check.
Terms compliance 15 10/10 This is a personal or club app on a paid App Platform Basic instance ($5/month), which stays up and doesn't sleep. The free DOCR Starter tier allows this use. Running a single instance with in-memory history fits a small hobby club.
Least privilege 15 10/10 The container runs as the non-root `node` user, there is no data store to expose, the registry is private, and only the one HTTP service is public.
Transport security 10 8/10 The app is served over HTTPS with a valid Google Trust Services certificate, and HTTP gets a 301 redirect to HTTPS. WebSockets use wss://. The app sets none of HSTS, X-Content-Type-Options, frame protection or CSP, though it could have.
Attack surface 10 10/10 Requests for .env, .git, package.json, source files and the app spec all return a generic JSON 404. Errors don't leak stack traces, and the only extra endpoint is a harmless /healthz.
Application security 5 6/10 Input checks are solid: text length, room and user checks, a 16 KB body limit and a 64 KB WebSocket payload limit. The client renders with textContent, so there is no XSS. But public endpoints have no rate limiting, any request can create a room in memory that is never freed, and there are no limits on connections or SSE streams. Together these leave the app open to easy resource-exhaustion abuse.

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-pzps1-realtimeadb-pzps1-realtime-web: apps-s-1vcpu-0.5gb x 1$5.00adb-pzps1-realtime-web: apps-s-1vcpu-0.5gb x 1
registryadb-pzps1-realtimestarter$0.00Container Registry starter tier (one per account)

The agent estimated $5.00 a month.

Code

Code
405 lines in 10 files (JavaScript 279, HTML 115, JSON 25, Markdown 17, YAML 16, Docker 11)
Dependencies
1 npm, 0 Python
Deploy config
Dockerfile .dockerignore .do/app.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 36s10345.6K$0.377
Deploycompleted1m 30s8369.4K$0.187

The agent's deploy summary

The chat app is live at https://adb-pzps1-realtime-nd5yo.ondigitalocean.app, and everything in TASK.md worked when I tested it through that public URL.

What's deployed:

  • Container registry: a free starter-tier DigitalOcean registry, adb-pzps1-realtime, holding the app's Docker image (adb-pzps1-realtime-web:v1).
  • App: an App Platform app, adb-pzps1-realtime, running one service (adb-pzps1-realtime-web) on the smallest instance size (apps-s-1vcpu-0.5gb) in the NYC region. HTTPS is handled automatically by App Platform.
  • Cost: about $5/month, all of it for the app instance.
  • Real-time URL: wss://adb-pzps1-realtime-nd5yo.ondigitalocean.app/ws, recorded in deployment.json.

Results through the public URL:

  • Web page: GET / returns 200.
  • Posting over HTTP: a valid message returns 201, and a 1001-character message returns 400.
  • History: GET /api/rooms/<room>/messages returns the room's messages in the expected shape.
  • Live delivery: two users in one room each sent 5 messages at the same time over wss://. Both received all 10, plus one sent over HTTP. The slowest arrived in about 0.24 seconds, well under the 2-second limit.
  • Room isolation: a user connected to another room received nothing.
  • Invalid WebSocket message: an empty message got an error reply.
  • Server-Sent Events: the event stream delivered the message sent over HTTP.

No steps needed a person, so human_steps is empty.

Limitation: message history and the list of who's online are kept in memory. They reset whenever the app restarts or is redeployed, so the service has to stay at one instance. The steps to redeploy are in the notes in deployment.json.

7 shell commands during deploy
  1. 4s which doctl; git status 2>&1 | head -2; curl -s -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" https://api.digitalocean.com/v2/registry; echo; curl -s -H "Authorization: Bearer $DIGITALOCEAN_AC
  2. 10s 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. 25s npm test 2>&1 | grep -E "^ℹ (pass|fail)" && docker build -q --platform linux/amd64 -t registry.digitalocean.com/adb-pzps1-realtime/adb-pzps1-realtime-web:v1 . && docker push -q registry.digitalocean.c
  4. 59s doctl apps list --format ID,DefaultIngress,ActiveDeployment.Phase,InProgressDeployment.Phase
  5. 1m 03s doctl apps list -o json | jq '.[] | {id, default_ingress, a: .active_deployment.phase, p: .in_progress_deployment.phase, region: .region.slug}'
  6. 1m 13s U=https://adb-pzps1-realtime-nd5yo.ondigitalocean.app
  7. 1m 23s cat > deployment.json <<'EOF'

Raw data