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 & compliance70/100
Stack fit74/100
Build2m 28s
Deploy4m 54s
Tokens1.59M
Agent cost$1.23
Hosting / month$0.00 light use$7.13 busy

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

Technologies

Runtime
Node.js 22
Frameworks
Node.js http module (built in)
Data
PostgreSQL (Render Postgres, free)
Libraries
wsnode-postgres
Packaging
Render Blueprint (render.yaml)
Hosting
Render web service (free, running the prebuilt image node:22-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

Stack fit

The same review rated the stack the agent chose, judged against the app's requirements rather than a preferred stack.

A minimal Node + ws + Postgres stack on a single Render web service fits a hobby-club chat app well. The odd image-runtime deployment and the expiring free database are where it falls short of the platform's strengths.

CriterionWeightScoreWhy
Fit for the job 40 8/10 Node's http module plus ws, with Postgres for history, suits a real-time chat app: WebSockets for live delivery and a durable store for history. Durability is undercut only by choosing the expiring free database plan.
Proportion 30 8/10 There are just two dependencies and one service plus a database, with no framework. The SSE endpoints and in-memory fallback add a bit of extra surface, but the spec allows SSE.
Platform fit 30 6/10 A long-running Render web service fits WebSockets well. But the build left STACK.md's plan of the native Node runtime with npm ci for a stock image with an env-var bundle and eval boot, which works against Render's build-and-deploy model. STACK.md also describes an in-memory ring alongside Postgres that the code doesn't implement.

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 solid: input is validated, SQL is parameterized, the UI is XSS-safe, Postgres is private, it's served only over HTTPS and no secrets leaked. The main weakness is provenance: the code is a locally built bundle passed in an env var and run with eval on a floating node:22-alpine image as root, with no tie to a source repo or pinned inputs. The free Postgres plan also expires in 30 days, which will take the app's persistent history with it.

CriterionWeightScoreWhy
Secrets handling 25 9/10 No credentials in source, the bundle or committed files. The Render API key stayed in an env var, the DB connection string was masked in output and passed through a temp file that was then deleted, and the app reads DATABASE_URL from a Render service env var. The only gap is that it's a plain env var, not one linked through fromDatabase or an env group.
Deployment provenance 20 4/10 Production code doesn't come from a source the owner controls. It's a locally built esbuild bundle shipped as a base64 env var and run with `node -e eval(process.env.BOOT)`, so Render can't tie the running code to any commit. The base image is a floating tag (node:22-alpine), esbuild is fetched with `npx -y esbuild@0.24`, and dependencies use caret ranges with no lockfile in the delivered sources.
Terms compliance 15 6/10 Render's free tier is fine for a personal hobby-club app, and sleeping when idle is tolerable for this use. But the free Postgres instance expires on 2026-11-14 (30 days), and the app's persistent history depends on it, so the app will lose its data store unless someone upgrades. The agent did disclose this.
Least privilege 15 7/10 Postgres has an empty IP allow list and the app uses the internal connection string. The container, however, runs the stock node image as root and writes the app to /app at startup. The app also connects with the database owner role, and its pg client would set rejectUnauthorized:false if it were ever pointed at an external host.
Transport security 10 8/10 HTTPS is served with a valid Google Trust Services certificate, HTTP gets a 301 redirect to HTTPS, and the client uses wss://. The app sets none of HSTS, CSP, X-Content-Type-Options or frame protection, though it easily could have.
Attack surface 10 9/10 Probes for /.env, /.git/config, /package.json, /server.js and /admin all return JSON 404s, there are no stack traces, and only / and the API are served. /healthz is public but harmless.
Application security 5 7/10 Inputs are validated (1-1000 character text, name and room limits, 16 KB body and WebSocket payload caps), SQL is parameterized, and the UI renders messages with textContent. There is no rate limiting or flood protection on POST or WebSocket messages, and the WebSocket upgrade doesn't check Origin.

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-bklxk-realtime-webfree$0.00free x 1 instance(s)
postgresadb-bklxk-realtime-dbfree$0.00Postgres free

The agent estimated $0.00 a month.

Code

Code
746 lines in 14 files (JavaScript 578, HTML 141, Markdown 41, JSON 29, Shell 27, YAML 26)
Dependencies
2 npm, 0 Python
Deploy config
render.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted2m 28s12446.0K$0.527
Deploycompleted4m 54s12813.4K$0.4311

The agent's deploy summary

The chat app is live at https://adb-bklxk-realtime-web.onrender.com, and every TASK.md requirement passed when I tested through that public URL. The WebSocket address is wss://adb-bklxk-realtime-web.onrender.com/ws.

What I created (both free, $0 a month):

  • Web service adb-bklxk-realtime-web (srv-db81i9u89jmc73brdkeg).
  • Postgres database adb-bklxk-realtime-db (dpg-db81hodg1s2s738m5a9g-a). It accepts connections only from inside Render, and chat history is stored there.

How it's deployed: there were no GitHub credentials, so there was no Git repo for Render to build from. Instead, the service runs the official node:22-alpine image, which Render pulls itself; nothing was pushed to another registry. The app is packed into a single file and sent as the env var APP_BUNDLE, and a small startup script (scripts/boot.js, sent as BOOT) unpacks and starts it. To redeploy, run scripts/deploy.sh. render.yaml still works as a Git-based setup if a repo is connected later.

Tested on the public URL:

  • The page loads.
  • 20 messages sent at once over WebSocket reached every client in the room, including the sender, in under half a second.
  • Rooms stay separate.
  • Invalid messages get 400 over HTTP and are ignored over WebSocket.
  • POST returns 201 and the message is broadcast.
  • History returns at most 50 messages, oldest first, and was still there after a restart.
  • The SSE stream works.

What didn't work: my first start command was a quoted sh -c '…', which Render didn't parse correctly. Switching to the BOOT script fixed it.

Limits of the free tier:

  • The service sleeps after 15 minutes idle. The next visit takes about a minute to start, open chats drop, and the page reconnects by itself.
  • The free database expires after 30 days unless it's upgraded, at about $6 a month.

Nothing needed a person, so human_steps is empty. I didn't create any new credentials, so there was nothing to put in .env. No secrets are in committed files.

11 shell commands during deploy
  1. 8s cd /home/runner/work/_temp/adb-bklxk-realtime; test -n "$RENDER_API_KEY" && echo key-set; gh auth status 2>&1 | head -2; env | grep -iE 'github|gitlab|token' | sed 's/=.*/=***/'
  2. 29s cd /home/runner/work/_temp/adb-bklxk-realtime; mkdir -p /tmp/bundle && npx -y esbuild@0.24 server.js --bundle --platform=node --target=node22 --minify --external:pg-native --outfile=/tmp/bundle/server
  3. 37s cd /home/runner/work/_temp/adb-bklxk-realtime; H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Accept: application/json" -H "Content-Type: application/json")
  4. 48s cd /home/runner/work/_temp/adb-bklxk-realtime; cat > scripts/build-bundle.sh <<'EOF'
  5. 1m 46s cd /home/runner/work/_temp/adb-bklxk-realtime; H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Accept: application/json" -H "Content-Type: application/json")
  6. 1m 54s H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Accept: application/json")
  7. 2m 42s cd /home/runner/work/_temp/adb-bklxk-realtime; cat > scripts/boot.js <<'EOF'
  8. 2m 49s H=(-H "Authorization: Bearer $RENDER_API_KEY" -H "Accept: application/json")
  9. 3m 09s cd /home/runner/work/_temp/adb-bklxk-realtime; node scripts/smoke.js https://adb-bklxk-realtime-web.onrender.com; node scripts/smoke.js https://adb-bklxk-realtime-web.onrender.com | tail -1
  10. 3m 20s U=https://adb-bklxk-realtime-web.onrender.com; R=cap-$RANDOM
  11. 4m 46s cd /home/runner/work/_temp/adb-bklxk-realtime; cat > scripts/deploy.sh <<'EOF'

Raw data