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

API / Backend Service on Cloudish

Success

Checks passed20/20
Human steps00 min of a person's time
Deploy without a human Deployed
Deploy with a human Not needed
Security & compliance76/100
Stack fit87/100
Build2m 30s
Deploy5m 10s
Tokens1.23M
Agent cost$0.86
Hosting / month$0.11 light use$7.84 busy

Deployed to https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai. Every deployment is deleted after it's verified and reviewed.

Screenshots

The API documentation at /docs
The API documentation at /docs · https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai/docs

Technologies

Runtime
Python 3.12
Frameworks
FastAPI
Data
SQLite (on a persistent volume)
Libraries
Uvicorn
Dev tools
HTTPXpytest
Packaging
Docker (python:3.12-slim)
Hosting
Cloudish container (shared) with a persistent volume

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

None: the agent got everything it needed on its own.

Stack fit

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

Python, FastAPI and SQLite on a Cloudish persistent volume is a lean, well-reasoned fit for a small authenticated notes API, and the build follows STACK.md exactly.

CriterionWeightScoreWhy
Fit for the job 40 9/10 FastAPI's built-in OpenAPI and docs cover the /openapi.json and /docs requirements. SQLite in WAL mode on a persistent volume is a real durable database that the agent confirmed survives a redeploy, and an in-process limiter is accurate for the single replica a volume forces.
Proportion 30 9/10 There are two direct dependencies plus the standard-library sqlite3 and no extra services, which is about as small as the requirements allow without resorting to a fake store.
Platform fit 30 8/10 This matches Cloudish's model of a container built from a Dockerfile with a volume, encrypted secrets and scale-to-zero, and STACK.md explains why: Cloudish has no managed database. The cost is cold starts and a single-replica ceiling, which is acceptable at this scale.

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 on Cloudish handles secrets well: API_KEYS is an encrypted project secret and nothing leaked into the transcripts, the source or the build context. It is HTTPS-only with a valid certificate and exposes nothing beyond the app. The main weaknesses: the container runs as root, the base image and transitive dependencies are unpinned, and the service runs on a nearly spent $0.10 credit grant with cold-start 503s, so it will stop serving unless the owner adds credits.

CriterionWeightScoreWhy
Secrets handling 25 9/10 API_KEYS was piped from the environment through jq into an encrypted Cloudish project secret, the Cloudish key lives only in a gitignored .env excluded from the build tarball, and no secret value appears in either transcript. The one minor issue is that the app stores the raw API key as the owner of each note in the SQLite file.
Deployment provenance 20 7/10 The image was built server-side from a tarball of the agent's own sources, the agent listed the tarball's contents before upload, and the running image tag matched the build. However, the base image is a floating tag with no digest, and only fastapi and uvicorn are pinned: transitive dependencies such as pydantic and starlette are unpinned and have no hashes.
Terms compliance 15 6/10 A pay-as-you-go prepaid-credit plan suits a personal project, and I know of nothing in Cloudish's terms that forbids this use, though I can't verify the terms in detail. The weakness is availability: only about $0.098 of a $0.10 grant remains, roughly 9 container-hours, after which the API returns 402, and cold starts return 503 for 10 to 40 seconds. The agent disclosed both and listed topping up credits as a human step.
Least privilege 15 6/10 The volume is private to the container and only the app port is exposed. Open project access is necessary for a public API. The container runs as root because the Dockerfile has no USER instruction.
Transport security 10 8/10 Served over HTTPS with a valid Let's Encrypt certificate, HTTP redirects to HTTPS with a 308, and the platform adds HSTS. The app sets no X-Content-Type-Options, frame protection or CSP, though it could easily have done so for /docs.
Attack surface 10 9/10 Probes for .env, .git, the Dockerfile, source files and the database file all return a JSON 404, and errors show no stack traces. Minor: FastAPI's default /redoc page is exposed although the spec doesn't ask for it, and the platform's nginx error page names its server software.
Application security 5 8/10 All SQL is parameterized, every note query is scoped to its owner, strict Pydantic types validate input, and errors come back as consistent JSON. Abuse protection is thin: requests with invalid keys are not rate limited, there are no limits on body or field sizes, and the in-memory limiter resets whenever the container restarts or scales to zero.

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.11containers awake 10 h at their size's hourly rate; no bandwidth charges
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.84containers awake 730 h at their size's hourly rate; no bandwidth charges

Resources the agent created

KindNamePlanList price / monthPricing
projectadb-24cth-api-webshared$7.840.1 CPU / 0.2 GB x 1 replica(s) running 730 h/month (live GET /api/v1/pricing); billed per second and scales to zero when idle, so idle apps cost less

The agent estimated $7.84 a month.

Code

Code
324 lines in 14 files (Python 314, Markdown 43, JSON 13, Docker 10)
Dependencies
0 npm, 4 Python
Deploy config
Dockerfile .dockerignore

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted2m 30s12447.9K$0.4510
Deploycompleted5m 10s13786.4K$0.4112

The agent's deploy summary

The Notes API is live on Cloudish over HTTPS at https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai, and every requirement in TASK.md passed when I tested it through that URL with the benchmark keys.

What's deployed (all names start with adb-24cth-api):

  • Cloudish API key adb-24cth-api: created without a signup. It's saved in .env, which is now in .gitignore, and no secret value was printed or written to any committed or deployed file.
  • Project adb-24cth-api-web: Cloudish built it from the Dockerfile. It runs on the cheapest size (shared) and goes to sleep after 15 minutes without requests.
  • 1 GB disk volume at /data: this holds the SQLite database file. Volumes are free.
  • API_KEYS: stored as an encrypted project secret, set to exactly the keys in BENCH_API_KEYS.

What I tested through the public URL:

  • /health, /openapi.json (OpenAPI 3.1) and the /docs HTML page.
  • 401 for a missing or unknown key.
  • Creating, listing (newest first, with the tag filter), reading, updating and deleting notes.
  • 400 for invalid input.
  • 404 when one key tries to read, list or delete another key's note.
  • 429 with Retry-After on the 61st request in a minute, while the other key kept working.
  • Persistence: I created a note, rebuilt and redeployed the app, and the note was still there.

Cost: about $0.0107 per hour while the container is running, so at most $7.84/month if it never sleeps.

Limitations:

  • Credits are the one human step. The key came with $0.10 of free credit, about 9 hours of running time, and I've spent about $0.0015 so far. Once it runs out, the API answers 402 instead of serving requests. To keep it up longer, a person needs to claim the key and add credits; this is listed as a non-blocking human_steps entry in deployment.json.
  • Cold starts: the first request after the app has been asleep can get a Cloudish 503 "starting" page for roughly 10–40 seconds. I kept the sleep setting because staying always on would use up the free credit within about 9 hours.
  • Rate-limit counts reset when the app restarts or sleeps. They're kept in memory, which is accurate because the disk volume limits the app to a single instance.
12 shell commands during deploy
  1. 4s cd /home/runner/work/_temp/adb-24cth-api; curl -s https://cloudish.ai | head -c 300; echo; for p in llms.txt docs api/v1 api/v1/docs openapi.json; do echo "== $p"; curl -s -o /dev/null -w "%{http_code
  2. 8s curl -sL https://cloudish.ai/llms.txt
  3. 13s curl -sL https://cloudish.ai/skill.md; echo ======; curl -sL https://cloudish.ai/credits.md; echo =====; curl -sL https://cloudish.ai/instances.md
  4. 21s cd /home/runner/work/_temp/adb-24cth-api; grep -q '^CLOUDISH_API_KEY=' .env 2>/dev/null || {
  5. 27s cd /home/runner/work/_temp/adb-24cth-api; set -a; . ./.env; set +a
  6. 32s cd /home/runner/work/_temp/adb-24cth-api; set -a; . ./.env; set +a
  7. 1m 14s cd /home/runner/work/_temp/adb-24cth-api; set -a; . ./.env; set +a; sleep 5
  8. 1m 23s U=https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai; for i in $(seq 1 24); do c=$(curl -s -o /tmp/h -w "%{http_code}" -m 30 $U/health); echo "$c $(cat /tmp/h | head -c 100)"; [ "$c" = 200 ] &
  9. 3m 04s cd /home/runner/work/_temp/adb-24cth-api; U=https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai
  10. 3m 27s U=https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai; IFS=, read -ra K <<< "$BENCH_API_KEYS"; echo "keys: ${#K[@]}"
  11. 3m 37s cd /home/runner/work/_temp/adb-24cth-api; set -a; . ./.env; set +a; U=https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai; IFS=, read -ra K <<< "$BENCH_API_KEYS"; B="${K[1]}"
  12. 4m 53s cd /home/runner/work/_temp/adb-24cth-api; U=https://b1ee376d0b00d40974484d7d740a086a.on.do.cloudish.ai; IFS=, read -ra K <<< "$BENCH_API_KEYS"

Raw data