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

API / Backend Service on DigitalOcean

Success

Checks passed20/20
Human steps37 min of a person's time
Security & compliance82/100
Build2m 41s
Deploy9m 52s
Tokens943.4K
Agent cost$0.90
Hosting / month$12.00 light use$12.00 busy

Deployed to https://adb-eeen0-api-x7cct.ondigitalocean.app. Every deployment is deleted after it's verified and reviewed.

Screenshots

The API documentation at /docs
The API documentation at /docs · https://adb-eeen0-api-x7cct.ondigitalocean.app/docs

Technologies

Runtime
Node.js 22
Frameworks
Express
Data
PostgreSQL (App Platform dev database)
Libraries
node-postgres
Dev tools
SuperTest
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 /health 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 /health returns {status: ok} health
passed GET /openapi.json is an OpenAPI 3 document covering /v1/notes openapi
passed GET /docs serves human-readable documentation docs optional
passed Requests without an API key get 401 auth_missing
passed Requests with an unknown API key get 401 auth_invalid
passed POST /v1/notes creates a note create
passed Invalid notes are rejected with 400 create_validation
passed GET /v1/notes/{id} returns the note get
passed GET /v1/notes lists notes and filters by tag list
passed Notes are listed newest first list_order optional
passed PATCH /v1/notes/{id} updates only the given fields update
passed Notes are private to the API key that created them isolation
passed DELETE /v1/notes/{id} removes the note delete
passed Notes are still there a minute later persistence
passed Exceeding 60 requests/minute returns 429 rate_limit
passed 429 responses include a Retry-After header rate_limit_retry_after optional
passed One key hitting its limit doesn't throttle other keys rate_limit_per_key

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 Notes API is well built: secrets come from App Platform's encrypted secret mechanism, the container runs as non-root, the database connection uses verified TLS, queries are parameterized, ownership is enforced, and the live site exposes nothing beyond the API, with HTTPS enforced. The main weakness is provenance: the image was built locally and pushed under the mutable tag v1, from an unpinned base image, with no source-controlled pipeline. Smaller gaps are missing security headers and a dev database that can't be firewalled and has no backups.

CriterionWeightScoreWhy
Secrets handling 25 9/10 API_KEYS is stored as an encrypted App Platform SECRET and DB credentials come from bindable variables, so nothing is baked into the image or source. The only small issue is that the deploy step wrote the real keys into a temporary spec file in /tmp (deleted right after) and left a 30-day registry login on the runner.
Deployment provenance 20 6/10 The image was built from the agent's local workspace and pushed to a registry the account owns, with npm ci against a lockfile. But there is no git source, the base image node:22-alpine and the deployed tag v1 are mutable rather than pinned by digest, and doctl was downloaded without verifying its checksum.
Terms compliance 15 9/10 This is a personal project on paid App Platform Basic plus a dev Postgres database, which fits DigitalOcean's standard terms; nothing sleeps or expires. The dev database has no backups and DigitalOcean doesn't recommend it for production, which is acceptable here. The account plan couldn't be read, so this is judged from standard terms.
Least privilege 15 8/10 The container runs as the non-root node user, there is one public service, and DB TLS is verified against the CA. The App Platform dev database can't be restricted to trusted sources, so it is reachable from the internet with credentials (as I understand the platform).
Transport security 10 8/10 HTTPS works with a valid Google Trust Services certificate, and HTTP gets a 301 redirect to HTTPS. Neither the app nor the spec sets HSTS, X-Content-Type-Options, frame protection or CSP.
Attack surface 10 9/10 Probes for .env, .git, source files, admin and debug paths all return a JSON 404. Errors don't include stack traces, and only the endpoints the spec requires are public. The platform adds an x-do-app-origin header.
Application security 5 9/10 Queries are parameterized, each note is scoped to a SHA-256 hash of its owner's key, input types and lengths are checked, docs output is HTML-escaped, and the per-key rate limit works. Minor gaps: key lookup isn't constant-time, and failed-auth and public endpoints aren't throttled by the app.

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.
$12.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.
$12.00fixed monthly prices; 49 GiB out of a 50 GiB allowance

Resources the agent created

KindNamePlanList price / monthPricing
appadb-eeen0-apiadb-eeen0-api-web: apps-s-1vcpu-0.5gb x 1, adb-eeen0-api-db: dev database$12.00adb-eeen0-api-web: apps-s-1vcpu-0.5gb x 1; adb-eeen0-api-db: dev database
registryadb-eeen0-apistarter$0.00Container Registry starter tier (one per account)

The agent estimated $12.00 a month.

Code

Code
522 lines in 13 files (JavaScript 512, JSON 33, YAML 31, Markdown 26, Docker 10)
Dependencies
3 npm, 0 Python
Deploy config
Dockerfile .dockerignore .do/app.yaml

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted2m 41s11460.7K$0.5310
Deploycompleted9m 52s9482.7K$0.378

The agent's deploy summary

The API is live at https://adb-eeen0-api-x7cct.ondigitalocean.app, and every requirement in TASK.md worked when I tested it through that URL.

What's deployed:

  • Web service: a DigitalOcean App Platform app (adb-eeen0-api) running one Basic instance (apps-s-1vcpu-0.5gb), about $5/month. It runs the Docker image I built and pushed to a free DigitalOcean Container Registry (adb-eeen0-api).
  • Database: a PostgreSQL 16 dev database (adb-eeen0-api-db) attached to the app, about $7/month. The app connects to it over an encrypted, certificate-checked connection.
  • API keys: API_KEYS is set to the keys from BENCH_API_KEYS and stored as an encrypted secret in the app.

The total is about $12/month.

What I tested through the public URL:

  • /health, /openapi.json and /docs return 200.
  • A missing or wrong API key gets 401.
  • Create, list, tag filter, get, update and delete all return the right responses. Bad input gets 400.
  • Another key gets 404 on a note it doesn't own.
  • After 60 requests in a minute, one key gets 429 with a Retry-After header, while the other key still gets 200.
  • A note I created was still there after a forced redeploy, so the data persists.

Limitations:

  • Rate limiting: the counters are kept in the app's memory. The app has to stay at one instance, and the counters reset whenever it restarts or redeploys.
  • Database: the dev database has no backups or standby copy. A full managed PostgreSQL database (about $15/month) would add those.
  • Updates: deploys don't run automatically from git. To change the app, push a new image to the registry and redeploy.

Nothing needed a person, so human_steps is empty. I didn't create any credentials, so there's no .env. The resource IDs and these notes are in deployment.json.

8 shell commands during deploy
  1. 6s which doctl; echo "keys: $(echo -n "$BENCH_API_KEYS" | tr ',' '\n' | wc -l)"; curl -s -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" https://api.digitalocean.com/v2/account | python3 -c 'import
  2. 14s 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. 30s docker build -q -t registry.digitalocean.com/adb-eeen0-api/adb-eeen0-api-web:v1 . && docker push -q registry.digitalocean.com/adb-eeen0-api/adb-eeen0-api-web:v1 && echo "API_KEYS_SET_FROM=BENCH_API_KE
  4. 45s doctl apps create --spec /tmp/adb-spec.yaml --wait --format ID,DefaultIngress,Phase 2>&1 | tail -3; rm -f /tmp/adb-spec.yaml
  5. 7m 14s doctl apps list -o json | python3 -c 'import sys,json;[print(a["id"],a.get("default_ingress"),a.get("active_deployment",{}).get("phase"),a.get("last_deployment_active_at")) for a in json.load(sys.stdi
  6. 7m 27s B=https://adb-eeen0-api-x7cct.ondigitalocean.app; K1=$(echo "$BENCH_API_KEYS"|cut -d, -f1); K2=$(echo "$BENCH_API_KEYS"|cut -d, -f2); J='Content-Type: application/json'
  7. 7m 41s B=https://adb-eeen0-api-x7cct.ondigitalocean.app; sleep 60; K1=$(echo "$BENCH_API_KEYS"|cut -d, -f1)
  8. 9m 44s cat > deployment.json <<'EOF'

Raw data