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

Data / Visualization App on Render

Success

Checks passed12/12
Human steps25 min of a person's time
Security & compliance73/100
Build1m 56s
Deploy2m 25s
Tokens1.13M
Agent cost$0.74
Hosting / month$0.00 light use$7.13 busy

Deployed to https://adb-ysjix-dataviz-web.onrender.com. Every deployment is deleted after it's verified and reviewed.

Screenshots

The dashboard
The dashboard · https://adb-ysjix-dataviz-web.onrender.com/

Technologies

Runtime
Node.js 24
Frameworks
Express
Libraries
Chart.js
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 dashboard as HTML dashboard
passed GET /api/summary (no filters) matches the dataset summary_all
passed GET /api/summary (region=West) matches the dataset summary_region
passed GET /api/summary (category=Electronics) matches the dataset summary_category
passed GET /api/summary (region=East, category=Furniture, start=2025-03-01, end=2025-06-30) matches the dataset summary_combined
passed GET /api/summary (region=Atlantis) matches the dataset summary_empty
passed The dashboard renders a chart (SVG or canvas) in a browser browser_chart
passed The dashboard shows at least two charts browser_two_charts optional
passed The dashboard has filter controls browser_filters 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 dashboard is live over HTTPS, exposes no files or secrets, and the app code is clean (validated dates, textContent rendering, clamped pagination). The main weaknesses are in how it was deployed and hosted. The code is an opaque base64 tarball uploaded as a Render secret file and run on a floating node:24-alpine tag; the container runs as root and reinstalls packages from npm on every start. It is also a commercial app on Render's free plan, which sleeps when idle, and the app sets no security headers.

CriterionWeightScoreWhy
Secrets handling 25 10/10 The Render API key was only referenced as $RENDER_API_KEY and never printed. The env listing printed variable names only (`env | cut -d= -f1`). The app needs no credentials, and no secrets show up in the source, the tarball or the live site.
Deployment provenance 20 5/10 Production runs an opaque base64 tarball uploaded from the agent's machine as a Render secret file, not a build from a repository the owner controls. The base image is a floating tag (node:24-alpine) with no digest, and dependencies are reinstalled from npm on every container start. The committed render.yaml does not describe what is actually deployed.
Terms compliance 15 5/10 This is a business (commercial) dashboard on Render's free web-service plan. Render presents that plan as being for hobby and testing use rather than production, and it spins down when idle, causing cold starts of 30-60 s. As far as I know Render does not explicitly forbid commercial use on the free plan, but it is a poor fit for a business app that should stay available.
Least privilege 15 7/10 There is a single public web service and no data stores, which is minimal. However, the container runs the stock node image as root, because the dockerCommand never switches to the image's built-in `node` user.
Transport security 10 7/10 HTTPS works with a valid certificate (behind Cloudflare), and HTTP returns a 301 redirect to HTTPS. The app sets none of HSTS, X-Content-Type-Options, frame protection or CSP, and it advertises X-Powered-By: Express.
Attack surface 10 9/10 Probes for .env, .git, package.json, server.js, data/sales.csv, node_modules, directory listings and path traversal all returned 404, and no stack traces leaked. The only minor item is the X-Powered-By banner along with Express's default error page.
Application security 5 8/10 Date parameters are validated, pagination is clamped (limit capped at 500), all rendering uses textContent (no XSS), and there is no database to inject into. There is no rate limiting on the public API, and array-style parameters are silently ignored instead of rejected.

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-ysjix-dataviz-webfree$0.00free x 1 instance(s)

The agent estimated $0.00 a month.

Code

Code
337 lines in 11 files (JavaScript 240, HTML 91, JSON 35, Markdown 11, YAML 8, Shell 6)
Dependencies
2 npm, 0 Python
Deploy config
render.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted1m 56s15508.5K$0.4410
Deploycompleted2m 25s11622.1K$0.309

The agent's deploy summary

The dashboard is live at https://adb-ysjix-dataviz-web.onrender.com and every requirement in TASK.md works through that URL.

What's deployed: one free Render web service, adb-ysjix-dataviz-web (srv-db7brq2j9qps73brhrb0) in the Oregon region. It costs $0 a month and is the only resource I created.

An unusual setup: there were no GitHub credentials, so I couldn't connect a repository for Render to build from. Instead:

  • The service runs Node's official public image, node:24-alpine.
  • The app code and sales.csv (about 47 KB packed) are stored on the service as a Render "secret file".
  • A second secret file, a start script, unpacks the code, installs dependencies and starts the server. A copy is in deploy/start.sh.

To ship a change, re-upload the packed code through Render's API and trigger a new deploy. Pushing the repo to GitHub and connecting it to Render would allow normal builds and auto-deploy on push. That needs a person, so I listed it in deployment.json as an optional step; nothing is blocked without it. The secret files contain no credentials, and I didn't create any, so there's no .env.

What I tested through the public URL:

  • API: with no filters it returns $1,839,455.69 in revenue, 2,400 orders and 15,179 units. East + Software in March returns $20,333.64, 14 orders and 140 units. A region that doesn't exist returns zeros and empty arrays, and a badly formatted date gets a 400.
  • Dashboard: in a scripted browser, setting the region, category and date filters updated the KPI tiles, both charts and the order table without reloading the page.

What didn't work: my first deploy failed because Render didn't read the quoted start command the way I expected. Putting the commands in the start script fixed it.

Limitation: on the free plan the service goes to sleep when idle, so the first request after a quiet period can take about 30–60 seconds.

deployment.json is written, and I committed everything to the local git repo.

9 shell commands during deploy
  1. 4s env | cut -d= -f1 | grep -iE "git|render|token" ; git status --short | head; git remote -v; curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/owners | head -c 600; echo; cur
  2. 16s tar czf /tmp/app.tgz app.js server.js package.json package-lock.json public data && ls -l /tmp/app.tgz && base64 -w0 /tmp/app.tgz | wc -c; which render; npm view @render/cli 2>/dev/null | head -2
  3. 25s 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-db7brq2j9qps73brhrb0/deploys/dep-db7brqaj9qps73brhs50 | python3 -c "import json,s
  5. 48s curl -s -H "Authorization: Bearer $RENDER_API_KEY" "https://api.render.com/v1/logs?ownerId=tea-db6fig0473hc73eb9aeg&resource=srv-db7brq2j9qps73brhrb0&limit=50&direction=backward" | python3 -c "
  6. 58s S=srv-db7brq2j9qps73brhrb0; H="Authorization: Bearer $RENDER_API_KEY"
  7. 1m 49s U=https://adb-ysjix-dataviz-web.onrender.com
  8. 2m 01s mkdir -p /tmp/pw && cd /tmp/pw && npm i playwright@1 >/dev/null 2>&1 && cat > t.js <<'EOF'
  9. 2m 17s mkdir -p deploy && cp /tmp/start.sh deploy/start.sh && printf '.env\n' >> .gitignore && cat > deployment.json <<'EOF'

Raw data