{
  "schema": 6,
  "names": {
    "cloud": "Vercel",
    "model": "Opus 5.5",
    "app": "Database-Backed CRUD App"
  },
  "run_id": "38582810085-1",
  "cloud": "vercel",
  "model": "opus-5-5",
  "app": "crud",
  "trial": 1,
  "prefix": "adb-q6cdt-crud",
  "agent": {
    "name": "Claude Code",
    "version": "2.1.296",
    "model": "claude-opus-5-5",
    "effort": "medium",
    "hipaa": true
  },
  "status": "partial",
  "failure_stage": "human",
  "url": "https://adb-q6cdt-crud-web.vercel.app",
  "url_source": "deployment.json",
  "score": 0.3158,
  "checks_passed": 6,
  "checks_total": 19,
  "required_failed": [
    "signup",
    "signup_duplicate",
    "login",
    "login_wrong_password",
    "auth_required",
    "create",
    "create_validation",
    "list",
    "update",
    "isolation",
    "delete",
    "persistence"
  ],
  "human": {
    "steps": [
      {
        "id": "signup",
        "description": "Sign up for an account and verify the email address",
        "minutes": 3,
        "evidence": [
          "prerequisite: account setup"
        ]
      },
      {
        "id": "api_token",
        "description": "Create an API token and hand it to the agent",
        "minutes": 2,
        "evidence": [
          "prerequisite: account setup"
        ]
      },
      {
        "id": "accept_integration_terms",
        "description": "Install a Marketplace integration and accept its terms in a browser",
        "minutes": 3,
        "evidence": [
          "agent: Open https://vercel.com/ai-deploy-bench/~/integrations/accept-terms/neon?source=cli while logged into the ai-deploy-bench team and accept the Neon marketplace terms. Then run (from this directory): `vercel --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID integration add neon --name adb-q6cdt-crud-db --",
          "tool output: > Installing Neon by Neon under ai-deploy-bench { \"status\": \"action_required\", \"reason\": \"integration_terms_acceptance_required\", \"message\": \"Accept marketplace terms for \\\"Neon\\\" in your browser before this install can finis"
        ]
      }
    ],
    "count": 3,
    "minutes": 8,
    "blockers": [
      {
        "id": "accept_integration_terms",
        "source": "agent",
        "detail": "Open https://vercel.com/ai-deploy-bench/~/integrations/accept-terms/neon?source=cli while logged into the ai-deploy-bench team and accept the Neon marketplace terms. Then run (from this directory): `vercel --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID integration add neon --name adb-q6cdt-crud-db --",
        "blocking": true
      },
      {
        "id": "accept_integration_terms",
        "source": "tool output",
        "detail": "> Installing Neon by Neon under ai-deploy-bench { \"status\": \"action_required\", \"reason\": \"integration_terms_acceptance_required\", \"message\": \"Accept marketplace terms for \\\"Neon\\\" in your browser before this install can finis",
        "blocking": true
      }
    ],
    "blocked": true,
    "succeeded": false
  },
  "security": {
    "reviewer": {
      "model": "claude-opus-5-5",
      "effort": "high",
      "hipaa": true,
      "status": "completed",
      "wall_time_s": 67.8,
      "tokens": 357677,
      "cost_usd": 0.392604
    },
    "status": "completed",
    "score": 65.5,
    "criteria": [
      {
        "id": "secrets",
        "name": "Secrets handling",
        "weight": 25,
        "score": 9,
        "rationale": "The Vercel token was only ever referenced as $VERCEL_TOKEN, the CLI-generated .env.local (OIDC token) was excluded by .vercelignore and is not served, and the app reads DATABASE_URL/POSTGRES_URL from the environment. The only credential in source is a local-only dev fallback (postgres:pg@localhost), which is disabled on Vercel."
      },
      {
        "id": "provenance",
        "name": "Deployment provenance",
        "weight": 20,
        "score": 6,
        "rationale": "Production was deployed straight from the agent's local working directory with `vercel deploy --prod` (no git source or CI the owner controls), using an unpinned `npx -y vercel@latest`. Dependencies use caret ranges, though a package-lock.json was generated and uploaded."
      },
      {
        "id": "compliance",
        "name": "Terms compliance",
        "weight": 15,
        "score": 2,
        "rationale": "This is a commercial app on a Vercel Hobby team, and Vercel's Hobby plan is limited to personal, non-commercial use, so a Pro plan is required. On top of that, no database was provisioned, so the deployed app can't actually work (signup, login and todos all return 503)."
      },
      {
        "id": "least_privilege",
        "name": "Least privilege",
        "weight": 15,
        "score": 8,
        "rationale": "There is one public serverless function and no data store yet. Vercel's deployment protection keeps the deployment-specific URL private (302 to auth). The DB client is set up with TLS `rejectUnauthorized: false`, which weakens database connection security once a DB is attached."
      },
      {
        "id": "transport",
        "name": "Transport security",
        "weight": 10,
        "score": 9,
        "rationale": "Served over HTTPS with a valid *.vercel.app certificate, HTTP gets a 308 redirect to HTTPS, and the platform sends HSTS with preload. The app sets no X-Content-Type-Options, frame protection or CSP, though it easily could have (e.g. via vercel.json headers or Express middleware)."
      },
      {
        "id": "exposure",
        "name": "Attack surface",
        "weight": 10,
        "score": 4,
        "rationale": "Vercel serves files from the project root as static assets ahead of the rewrite, so the server-side source (/server.js, /lib/app.js, /lib/db.js, /lib/page.js) can be downloaded publicly. No secrets are in those files, and .env, .git, .vercel, tests and the package files return 404."
      },
      {
        "id": "application",
        "name": "Application security",
        "weight": 5,
        "score": 6,
        "rationale": "The code is solid: parameterized SQL, bcrypt, random session tokens stored as SHA-256 hashes, per-user ownership checks, DOM rendering with textContent, and a JSON size limit. But signup and login have no rate limiting or lockout, tokens never expire, the bearer token is kept in localStorage, and the live app doesn't work because the database is missing."
      }
    ],
    "findings": [
      {
        "severity": "high",
        "criterion": "compliance",
        "source": "deployment",
        "title": "Commercial app hosted on Vercel Hobby (non-commercial) plan",
        "evidence": "process/context.json: use=commercial, team_plans.team_U2tHOV4fpUx18nl9EpcRQ4hO=hobby; process/deployment.json resource plan \"Hobby\"",
        "recommendation": "Deploy the business app on a Vercel Pro (or Enterprise) team. Vercel's Hobby terms only allow personal, non-commercial use."
      },
      {
        "severity": "high",
        "criterion": "compliance",
        "source": "deployment",
        "title": "No database provisioned; the app cannot do anything",
        "evidence": "GET /api/health -> 503 {\"error\":\"Database not configured\"}; process/deploy.md: `integration add neon` returned integration_terms_acceptance_required and Blob create-store failed with the usage threshold limit",
        "recommendation": "Provision a managed Postgres (accept the Neon terms, or use a paid plan/provider), set DATABASE_URL as an environment variable and redeploy."
      },
      {
        "severity": "medium",
        "criterion": "exposure",
        "source": "deployment",
        "title": "Server-side source code served as static files",
        "evidence": "GET /lib/app.js -> 200 application/javascript (5563 bytes, full Express app); GET /lib/db.js -> 200; GET /server.js -> 200; GET /lib/page.js -> 200",
        "recommendation": "Move server code out of the publicly served root (e.g. keep it under api/ or a directory listed in .vercelignore and bundled only into the function), or use a framework preset/outputDirectory so only intended assets are public."
      },
      {
        "severity": "medium",
        "criterion": "provenance",
        "source": "deployment",
        "title": "Production deployed from local working directory with an unpinned CLI",
        "evidence": "process/deploy.md: `npx -y vercel@latest --token $VERCEL_TOKEN ... deploy --prod --yes` run from the agent's local directory; no git repository connected",
        "recommendation": "Deploy from a git repo the owner controls (Vercel Git integration or CI), pin the Vercel CLI version, and commit the lockfile."
      },
      {
        "severity": "medium",
        "criterion": "application",
        "source": "app",
        "title": "No rate limiting on authentication endpoints",
        "evidence": "app/lib/app.js lines 63-92: signup/login handlers have no throttling, lockout or CAPTCHA",
        "recommendation": "Add per-IP and per-account rate limiting (e.g. Vercel Firewall rate-limit rules or a KV-backed limiter) on /api/auth/*."
      },
      {
        "severity": "low",
        "criterion": "provenance",
        "source": "app",
        "title": "Dependency ranges are not pinned",
        "evidence": "app/package.json lines 15-17: \"bcryptjs\": \"^3.0.3\", \"express\": \"^5.3.0\", \"pg\": \"^8.23.1\"",
        "recommendation": "Commit package-lock.json and install with `npm ci`, or pin exact versions."
      },
      {
        "severity": "low",
        "criterion": "application",
        "source": "app",
        "title": "Session tokens never expire and are kept in localStorage",
        "evidence": "app/lib/db.js lines 26-30: sessions table has no expiry and auth() at app/lib/app.js line 41 doesn't check age; app/lib/page.js line 119: localStorage.setItem('token', token)",
        "recommendation": "Add a session expiry check and periodic cleanup. Consider HttpOnly, Secure, SameSite cookies for the browser UI."
      },
      {
        "severity": "low",
        "criterion": "least_privilege",
        "source": "app",
        "title": "Database TLS certificate verification disabled",
        "evidence": "app/lib/db.js line 13: ssl: { rejectUnauthorized: false }",
        "recommendation": "Verify the server certificate (rejectUnauthorized: true with the provider CA / sslmode=verify-full)."
      },
      {
        "severity": "low",
        "criterion": "transport",
        "source": "app",
        "title": "Missing security headers",
        "evidence": "curl -I https://adb-q6cdt-crud-web.vercel.app/ shows strict-transport-security (platform) but no Content-Security-Policy, X-Content-Type-Options or X-Frame-Options",
        "recommendation": "Add these headers in vercel.json `headers` or with Express middleware such as helmet."
      },
      {
        "severity": "info",
        "criterion": "secrets",
        "source": "app",
        "title": "Local dev DB credential fallback in source",
        "evidence": "app/lib/db.js line 6: 'postgres://postgres:pg@localhost:5432/postgres' (used only when VERCEL is unset)",
        "recommendation": "Read the local connection string from an untracked .env file instead of hardcoding it."
      },
      {
        "severity": "info",
        "criterion": "transport",
        "source": "platform",
        "title": "HTTPS enforced with a valid certificate",
        "evidence": "curl -I http://adb-q6cdt-crud-web.vercel.app/ -> 308 Location https://...; certificate CN=*.vercel.app issued by Google Trust Services WR1, valid until Nov 27 2026",
        "recommendation": "None."
      }
    ],
    "summary": "The Express/Postgres todo app is sound in code: parameterized queries, bcrypt, hashed random session tokens and ownership checks, with secrets handled through environment variables. But it is a commercial app on a Vercel Hobby (non-commercial) plan, it has no database so every account and todo endpoint returns 503, and its server-side source files (/lib/app.js, /lib/db.js, /server.js) are publicly downloadable. It was also deployed from a local directory with an unpinned CLI, and the auth endpoints have no rate limiting."
  },
  "timing": {
    "build_s": 110.0,
    "deploy_s": 152.0,
    "agent_s": 262.0,
    "ready_after_deploy_s": 0.0,
    "verify_s": 2.4
  },
  "tokens": {
    "build": {
      "input": 3660,
      "output": 10300,
      "cache_read": 318523,
      "cache_write": 18654,
      "total": 351137
    },
    "deploy": {
      "input": 860,
      "output": 7969,
      "cache_read": 753078,
      "cache_write": 15240,
      "total": 777147
    },
    "total": {
      "input": 4520,
      "output": 18269,
      "cache_read": 1071601,
      "cache_write": 33894,
      "total": 1128284
    }
  },
  "cost": {
    "agent_usd": 0.767251,
    "agent_build_usd": 0.377615,
    "agent_deploy_usd": 0.389636,
    "agent_cost_estimated": false,
    "hosting_monthly_usd": 0.0,
    "hosting_usage": {
      "light": {
        "usd": 20.0,
        "incomplete": false,
        "basis": "Hobby is for non-commercial use only, so priced on Pro: $20 platform fee including $20 of usage credit, $0.00 of usage"
      },
      "busy": {
        "usd": 20.0,
        "incomplete": false,
        "basis": "Hobby is for non-commercial use only, so priced on Pro: $20 platform fee including $20 of usage credit, $5.75 of usage"
      }
    },
    "hosting_unpriced": [],
    "hosting_agent_estimate_usd": 0
  },
  "activity": {
    "turns": {
      "build": 9,
      "deploy": 15
    },
    "tool_calls": {
      "Bash": 22
    },
    "shell_commands": {
      "build": 8,
      "deploy": 14
    },
    "tool_errors": 1,
    "web_searches": 0,
    "web_fetches": 0,
    "subagents": 0
  },
  "phases": {
    "build": {
      "status": "completed",
      "detail": "success",
      "started": true,
      "resumed": false,
      "started_at": "2026-10-14T14:47:53+00:00",
      "wall_time_s": 110.0,
      "api_time_s": 94.5,
      "turns": 9,
      "tokens": {
        "input": 3660,
        "output": 10300,
        "cache_read": 318523,
        "cache_write": 18654,
        "total": 351137
      },
      "tokens_estimated": false,
      "cost_usd": 0.377615,
      "cost_source": "claude-code",
      "model_usage": {
        "claude-opus-5-5": {
          "input": 3660,
          "output": 10300,
          "cache_read": 318523,
          "cache_write": 18654,
          "cost_usd": 0.37761459999999997,
          "web_searches": 0
        }
      },
      "tool_calls": {
        "Bash": 8
      },
      "tool_errors": 1,
      "compactions": 0,
      "api_retries": 0,
      "result_subtype": "success",
      "terminal_reason": "completed",
      "permission_denials": 0,
      "model": "claude-opus-5-5",
      "claude_code_version": "2.1.296",
      "lingered": false
    },
    "deploy": {
      "status": "completed",
      "detail": "success",
      "started": true,
      "resumed": true,
      "started_at": "2026-10-14T14:49:44+00:00",
      "wall_time_s": 152.0,
      "api_time_s": 90.4,
      "turns": 15,
      "tokens": {
        "input": 860,
        "output": 7969,
        "cache_read": 753078,
        "cache_write": 15240,
        "total": 777147
      },
      "tokens_estimated": false,
      "cost_usd": 0.389636,
      "cost_source": "claude-code",
      "model_usage": {
        "claude-opus-5-5": {
          "input": 860,
          "output": 7969,
          "cache_read": 753078,
          "cache_write": 15240,
          "cost_usd": 0.3896355999999999,
          "web_searches": 0
        }
      },
      "tool_calls": {
        "Bash": 14
      },
      "tool_errors": 0,
      "compactions": 0,
      "api_retries": 0,
      "result_subtype": "success",
      "terminal_reason": "completed",
      "permission_denials": 0,
      "model": "claude-opus-5-5",
      "claude_code_version": "2.1.296",
      "lingered": false
    }
  },
  "final_messages": {
    "build": "The todo app is built and works locally against a real PostgreSQL database. The automated tests pass, and I checked it by hand with curl: a todo created before a server restart was still there afterwards.\n\n**Stack:** Node.js with Express 5, PostgreSQL through `pg`, and `bcryptjs` for password hashing. On Vercel it runs as one serverless function.\n- `api/index.js` loads the app from `lib/app.js`, and `vercel.json` sends every path to it. That includes `GET /`, which serves the web page from `lib/page.js`.\n- `lib/db.js` reads the database address from `DATABASE_URL`, or `POSTGRES_URL` if that isn't set. It creates the tables (users, sessions, todos) on the first request.\n- Login tokens are random strings, stored hashed in a sessions table, so there's no signing secret to configure.\n- Users only ever see or change their own todos: touching someone else's returns 404. Password hashes are never returned.\n- The web page covers sign up, log in, log out, and create, list, rename, complete/uncomplete and delete. It keeps the token in the browser and sends it as `Authorization: Bearer`.\n\n**Tests:** `test/api.test.js` covers every endpoint and error code in the spec, plus one user trying to edit another user's todo.\n\n**Run locally:**\n```\ndocker run -d --name pg -e POSTGRES_PASSWORD=pg -p 5432:5432 postgres:16\nnpm install\nnpm start      # http://localhost:3000\nnpm test\n```\nWithout `DATABASE_URL` set, it uses `postgres://postgres:pg@localhost:5432/postgres`.\n\n**For step 2:** the deployment needs a managed Postgres database (for example Neon from the Vercel Marketplace), with its connection string set as `DATABASE_URL` or `POSTGRES_URL`.\n\nI've stopped the app server and the Postgres container.",
    "deploy": "The app is live at **https://adb-q6cdt-crud-web.vercel.app**, but signup, login and every todo action fail with a 503 because no database is connected. I couldn't create one without a person.\n\n**What works through the public URL**\n- `GET /` serves the web page over HTTPS (200).\n- `GET /api/todos` without a token returns 401.\n- Signup, login and all todo actions return 503 `Database not configured` because there's no database.\n\n**Why there's no database**\n- **Postgres:** every Postgres provider in Vercel's Marketplace (Neon, Prisma Postgres, Supabase) requires a person to accept its terms in a browser before it can be installed. I hit that block twice for Neon, the second time just now.\n- **Blob storage:** I tried Vercel's own Blob storage as a fallback. Creating the store failed with \"Cannot create another store when usage threshold limit is reached\", which is an account limit I can't change.\n\n**The human step** (also in `deployment.json` under `human_steps`)\n1. Accept the Neon terms at https://vercel.com/ai-deploy-bench/~/integrations/accept-terms/neon?source=cli.\n2. Run `vercel integration add neon --name adb-q6cdt-crud-db --non-interactive`. This creates the database and sets `DATABASE_URL` on the project.\n3. Run `vercel deploy --prod --yes`.\n\nNo code changes are needed after that; the app creates its tables on the first request. I haven't been able to run the full test against the public URL, so it should be rerun once the database is in place. The same tests pass locally against Postgres 16.\n\n**Changes since step 1**\n- I added a `.vercelignore` so the `.env*` files and the tests aren't deployed.\n- The deployed app now returns that 503 when no database address is set, instead of trying to reach a local database.\n\n**Resources:** the Vercel project `adb-q6cdt-crud-web` (`prj_dPoCC6uCgFFFWf9cTdQfOFxYCjTj`, Hobby plan, $0/month). The Neon free database is planned but not created. I made no credentials, so there's no `.env`; the CLI wrote its own `.env.local` with a short-lived token, which is excluded from deploys."
  },
  "deployment_manifest": {
    "url": "https://adb-q6cdt-crud-web.vercel.app",
    "resources": [
      {
        "type": "web service (Vercel project, Node.js serverless function + Express)",
        "name": "adb-q6cdt-crud-web",
        "id": "prj_dPoCC6uCgFFFWf9cTdQfOFxYCjTj",
        "plan": "Hobby",
        "monthly_cost_usd": 0
      },
      {
        "type": "PostgreSQL database (Neon via Vercel Marketplace) - NOT YET CREATED, blocked on terms acceptance",
        "name": "adb-q6cdt-crud-db",
        "id": "",
        "plan": "Neon Free",
        "monthly_cost_usd": 0
      }
    ],
    "estimated_monthly_cost_usd": 0,
    "human_steps": [
      {
        "step": "Open https://vercel.com/ai-deploy-bench/~/integrations/accept-terms/neon?source=cli while logged into the ai-deploy-bench team and accept the Neon marketplace terms. Then run (from this directory): `vercel --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID integration add neon --name adb-q6cdt-crud-db --non-interactive` followed by `vercel --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID deploy --prod --yes`.",
        "why": "Every Vercel Marketplace Postgres provider (Neon, Prisma Postgres, Supabase) requires terms to be accepted in a browser before installing, and the team's Vercel Blob usage limit is reached, so no durable storage could be created by the agent. The integration sets DATABASE_URL on the project; the app creates its tables on first request. Until this is done, signup/login and all todo endpoints return 503 'Database not configured'.",
        "blocking": true
      }
    ],
    "notes": "Deployed and publicly reachable over HTTPS: GET / serves the web UI (200), and unauthenticated /api/todos returns 401. Endpoints that need the database return 503 until Neon is provisioned (see human_steps). No code changes are needed after that: the app reads DATABASE_URL or POSTGRES_URL. The full API was tested locally against PostgreSQL 16 (npm test). I also tried a Vercel Blob store as a fallback, but creating it failed with 'Cannot create another store when usage threshold limit is reached'."
  },
  "verification": {
    "readiness": {
      "ready": true,
      "seconds_to_ready": 0.1,
      "attempts": 1,
      "first_response_ms": 131
    },
    "performance": {
      "samples": 10,
      "p50_ms": 30,
      "p90_ms": 139
    },
    "checks": [
      {
        "id": "ready",
        "description": "GET / answers 2xx within 1 min",
        "required": true,
        "status": "passed",
        "detail": "",
        "duration_ms": 0
      },
      {
        "id": "https",
        "description": "The app is served over HTTPS with a valid certificate",
        "required": true,
        "status": "passed",
        "detail": "",
        "duration_ms": 28
      },
      {
        "id": "public_access",
        "description": "The app is public: no redirect to another host such as a login page",
        "required": true,
        "status": "passed",
        "detail": "",
        "duration_ms": 65
      },
      {
        "id": "ui",
        "description": "GET / serves the web app as HTML",
        "required": true,
        "status": "passed",
        "detail": "",
        "duration_ms": 55
      },
      {
        "id": "signup",
        "description": "POST /api/auth/signup creates an account and returns a token",
        "required": true,
        "status": "failed",
        "detail": "POST /api/auth/signup returned 503, expected 201 or 200: {\"error\":\"Database not configured\"}",
        "duration_ms": 149
      },
      {
        "id": "signup_validation",
        "description": "Signup rejects an invalid email and a short password with 400",
        "required": true,
        "status": "passed",
        "detail": "",
        "duration_ms": 62
      },
      {
        "id": "signup_duplicate",
        "description": "Signing up twice with the same email returns 409",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on signup",
        "duration_ms": 0
      },
      {
        "id": "login",
        "description": "POST /api/auth/login returns a token",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on signup",
        "duration_ms": 0
      },
      {
        "id": "login_wrong_password",
        "description": "Login with a wrong password returns 401",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on signup",
        "duration_ms": 0
      },
      {
        "id": "auth_required",
        "description": "/api/todos requires a valid token (401)",
        "required": true,
        "status": "failed",
        "detail": "GET /api/todos returned 503, expected 401 or 403: {\"error\":\"Database not configured\"}",
        "duration_ms": 52
      },
      {
        "id": "create",
        "description": "POST /api/todos creates a todo",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on login",
        "duration_ms": 0
      },
      {
        "id": "create_validation",
        "description": "An empty title is rejected with 400",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on login",
        "duration_ms": 0
      },
      {
        "id": "list",
        "description": "GET /api/todos lists the caller's todos",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on create",
        "duration_ms": 0
      },
      {
        "id": "list_order",
        "description": "Todos are listed oldest first",
        "required": false,
        "status": "failed",
        "detail": "not run: depends on create",
        "duration_ms": 0
      },
      {
        "id": "update",
        "description": "PATCH /api/todos/{id} renames and completes a todo",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on create",
        "duration_ms": 0
      },
      {
        "id": "isolation",
        "description": "Users can't see or modify each other's todos (404)",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on create",
        "duration_ms": 0
      },
      {
        "id": "delete",
        "description": "DELETE /api/todos/{id} removes a todo",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on create",
        "duration_ms": 0
      },
      {
        "id": "persistence",
        "description": "Data is still there a minute later, with a fresh login",
        "required": true,
        "status": "failed",
        "detail": "not run: depends on create",
        "duration_ms": 0
      },
      {
        "id": "browser_ui",
        "description": "The web UI renders a login or signup form",
        "required": false,
        "status": "passed",
        "detail": "",
        "duration_ms": 696
      }
    ],
    "browser": true,
    "duration_s": 2.4
  },
  "screenshots": [
    {
      "name": "signin",
      "caption": "The web app before signing in",
      "url": "https://adb-q6cdt-crud-web.vercel.app/",
      "file": "crud-t1/signin.jpg"
    }
  ],
  "resources": [
    {
      "kind": "project",
      "id": "prj_dPoCC6uCgFFFWf9cTdQfOFxYCjTj",
      "name": "adb-q6cdt-crud-web",
      "plan": null,
      "url": "https://adb-q6cdt-crud-web.vercel.app",
      "monthly_usd": 0.0,
      "pricing": "no fixed fee; usage within the plan's included allowance",
      "matched_by": "prefix",
      "created_at": 1791989409909,
      "details": {
        "team_id": "team_U2tHOV4fpUx18nl9EpcRQ4hO",
        "framework": null
      }
    }
  ],
  "scratch_resources": [],
  "possible_leaks": [],
  "account": {
    "team_plans": {
      "team_U2tHOV4fpUx18nl9EpcRQ4hO": "hobby"
    }
  },
  "inventory_errors": [],
  "teardown": {
    "skipped": null,
    "kept": [],
    "deleted": 1,
    "failed": [],
    "leftovers": []
  },
  "code": {
    "files": 12,
    "lines": {
      "JavaScript": 379,
      "JSON": 41,
      "Markdown": 12
    },
    "code_lines": 379,
    "dependencies": {
      "npm": 3,
      "python": 0
    },
    "frameworks": [
      "bcryptjs",
      "express",
      "pg"
    ],
    "deploy_config": [
      "vercel.json"
    ],
    "git_repo": false,
    "packages": {
      "npm": [
        "bcryptjs",
        "express",
        "pg"
      ],
      "npm_dev": [],
      "python": [],
      "python_dev": []
    },
    "base_images": [],
    "runtime_hints": {
      "node": ">=20"
    },
    "builtins": [],
    "manifests": [
      "package.json"
    ],
    "dirs": [
      "api",
      "lib",
      "test"
    ]
  },
  "technologies": {
    "Runtime": [
      "Node.js 20+"
    ],
    "Frameworks": [
      "Express"
    ],
    "Libraries": [
      "node-postgres",
      "bcrypt.js"
    ],
    "Packaging": [
      "vercel.json"
    ],
    "Hosting": [
      "Vercel project",
      "Vercel Functions"
    ]
  }
}
