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

Real-Time Collaborative App on Render

Success

Checks passed13/13
Human steps25 min of a person's time
Security & compliance76/100
Build1m 42s
Deploy2m 14s
Tokens837.7K
Agent cost$0.70
Hosting / month$0.00 light use$7.13 busy

Deployed to https://adb-cg693-realtime-web.onrender.com. 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-cg693-realtime-web.onrender.com/?room=bench-de792e22

Technologies

Runtime
Node.js 24
Frameworks
Node.js http module (built in)
Libraries
ws
Packaging
Render Blueprint (render.yaml)
Hosting
Render web service (free, running the prebuilt image node:24-alpine)

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 Render chat app handles secrets cleanly, exposes nothing beyond its routes, validates input well, and serves HTTPS with a redirect from HTTP. Its main weakness is provenance: the code runs as a locally built base64 tarball stored in an env var, executed with eval on a floating, unpinned node:24-alpine tag, as root, with no owner-controlled repo behind it. Smaller issues are missing security headers, no rate limiting, no WebSocket Origin check and unbounded room creation; the free plan suits this personal club app.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The app needs no secrets. The Render API key was only used as $RENDER_API_KEY in curl headers and masked when environment variables were listed. No credentials appear in source, deploy scripts or the shipped tarball.
Deployment provenance 20 4/10 Production runs a base64 tarball built on the agent's machine, stored in an env var and run via `node -e eval(process.env.BOOT)` on the floating `node:24-alpine` tag. No owner-controlled repo, no image digest, and the committed render.yaml doesn't describe what actually runs. Dependencies were at least installed from a lockfile with npm ci.
Terms compliance 15 8/10 This is a personal hobby-club app, and Render's free web services allow hobby and non-commercial use. The service sleeps after about 15 minutes idle, which causes cold starts and dropped sockets. The agent documented this and noted the Starter upgrade.
Least privilege 15 7/10 There is one public web service and no data stores or extra resources. But the stock node image runs the app as root (no USER / non-root uid), and the boot script writes to /app and /tmp as root.
Transport security 10 7/10 HTTPS has a valid Google Trust Services certificate, HTTP gets a 301 to HTTPS, and the socket runs over wss. The app sets none of HSTS, X-Content-Type-Options, frame protection or CSP, though it easily could have.
Attack surface 10 10/10 Probes for /.env, /.git/config, /server.js, /package.json, /public/ and path traversal all returned a generic JSON 404. Errors are generic with no stack traces, and only the app routes plus a harmless /healthz are exposed.
Application security 5 7/10 Input validation is solid: room regex, user and text lengths, 16 KB body and WS payload caps, and 400s for bad JSON. The UI renders with textContent, so there's no XSS. There is no rate limiting, no WebSocket Origin check, and rooms are created without bound and never evicted.

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.00fixed monthly prices; 0.04 GB out of the Hobby workspace's 5 GB; free instances awake 10 h of 750 free hours
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.13fixed monthly prices; 52.6 GB out of the Hobby workspace's 5 GB, 47.6 GB billed (with a payment method on file; without one Render suspends the services instead); free instances awake 730 h of 750 free hours

Resources the agent created

KindNamePlanList price / monthPricing
serviceadb-cg693-realtime-webfree$0.00free x 1 instance(s)

The agent estimated $0.00 a month.

Code

Code
472 lines in 10 files (JavaScript 331, HTML 129, JSON 27, Markdown 14, Shell 12, YAML 9)
Dependencies
1 npm, 0 Python
Deploy config
render.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 42s9341.8K$0.398
Deploycompleted2m 14s10495.9K$0.319

The agent's deploy summary

The chat app is live on Render's free plan at https://adb-cg693-realtime-web.onrender.com, and I tested every requirement through that public URL.

How it's deployed: there was no GitHub account available, so I couldn't deploy from a git repo. Instead, the service (srv-db7pqp0u01pc739ltang) runs Render's free tier with the public node:24-alpine image. The app code is packed into an environment variable and unpacked when the service starts, with start command node -e eval(process.env.BOOT). The first deploy failed because Render didn't accept the quoted shell start command I gave it; the second worked. To ship code changes, run deploy/redeploy.sh. Cost is $0/month, and nothing needed a person, so human_steps is empty.

Results through the public URL:

  • The UI loads.
  • 15 messages sent at the same time by 3 WebSocket clients over wss:// reached all 3 clients, including each sender, within about 160 ms.
  • A client in another room got none of them.
  • Invalid WebSocket messages got a {"type":"error"} reply.
  • Over HTTP, empty text, 1001-character text and missing text all got 400; a 1000-character message got 201.
  • A message sent with POST reached both the Server-Sent Events stream and the WebSocket clients.
  • History returns the latest 50 messages, oldest first.

Limitations (also in deployment.json notes):

  • Lost history: messages are kept in memory only, so history disappears whenever the service restarts or is redeployed.
  • Single instance: it must run as one copy, because messages aren't shared between copies.
  • Free-plan sleep: the service sleeps after about 15 minutes with no traffic. The next visitor waits roughly 30–60 seconds, and open connections drop when it goes to sleep (the UI reconnects on its own). Render's Starter plan, about $7/month, keeps it always on.

deployment.json lists the service with realtime set to {"transport": "websocket", "url": "wss://adb-cg693-realtime-web.onrender.com/ws"}. I didn't create any new credentials, so there's no .env. The deploy files and deployment.json are committed to the local git repo.

9 shell commands during deploy
  1. 5s env | grep -iE 'token|github|render' | sed 's/=.*/=***/'; gh auth status 2>&1 | head -5; curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/owners | head -c 600; echo; curl -
  2. 16s tar czf /tmp/app.tgz server.js package.json package-lock.json public node_modules && ls -l /tmp/app.tgz && base64 -w0 /tmp/app.tgz | wc -c
  3. 23s B64=$(base64 -w0 /tmp/app.tgz) python3 - <<'EOF' > /tmp/create.json
  4. 30s for i in $(seq 1 40); do s=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/services/srv-db7pqp0u01pc739ltang/deploys/dep-db7pqp8u01pc739ltbh0 | python3 -c "import json,s
  5. 49s curl -s -H "Authorization: Bearer $RENDER_API_KEY" "https://api.render.com/v1/logs?ownerId=tea-db6fig0473hc73eb9aeg&resource=srv-db7pqp0u01pc739ltang&limit=30&direction=backward" | python3 -c "
  6. 1m 06s cd /tmp && cat > boot.js <<'EOF'
  7. 1m 13s S=srv-db7pqp0u01pc739ltang; for i in $(seq 1 40); do s=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/services/$S/deploys/dep-db7pr3u7bikc73bo4ch0 | python3 -c "import
  8. 1m 45s mkdir -p /tmp/e2e && cd /tmp/e2e && cat > e2e.js <<'EOF'
  9. 2m 06s mkdir -p deploy && cp /tmp/boot.js deploy/boot.js && cat > deploy/redeploy.sh <<'EOF'

Raw data