PutThrough Product engineering studio
Book a build call

Service 01

Rescue and hardening for AI-built apps

The app works in a demo. We make it survive real users, real money and real load.

Prefer to write? Send your requirements

An AI coding tool has produced a working application, and the distance between that and a live product is not features — it is access control, correctness under failure, and the operational layer nobody writes until something breaks at two in the morning. We close that distance on a fixed scope agreed from an audit, in your repository, in branches you review.

Security

  • Row-level security and database access policies, written and tested per table
  • Secrets and service keys audited, moved out of client bundles, and rotated
  • Authentication and session handling: token lifetimes, refresh, logout, password reset
  • Authorization checks moved server-side where the client was trusted to enforce them
  • Multi-tenant data separation, where one customer could reach another’s records
  • File and storage bucket permissions, including what is already public

Money

  • Payment flow traced end to end against the provider’s own event log
  • Webhook signature verification, idempotency and retry handling
  • Subscription state reconciled between your database and the payment provider
  • Failure, refund, chargeback and cancellation paths — the ones demos never exercise

Operations

  • Environment separation, configuration and secret management
  • Build and deployment pipeline, with a rollback that has been tested
  • Error tracking and uptime monitoring, configured and handed to you
  • Database migrations, backups and a restore you have watched work
  • Rate limiting and abuse controls on the endpoints that need them

Correctness

  • The failure paths: empty states, network loss, concurrent edits, partial writes
  • Input validation and error handling on the server, not only in the form
  • Tests around the parts that handle money, auth and customer data

Works with

  • Lovable
  • Bolt
  • Replit
  • Cursor
  • v0
  • Base44
  • Next.js
  • React
  • Astro
  • Supabase
  • Firebase
  • Neon
  • Postgres
  • Node
  • Python

What this is not

  • Not a penetration test. We review code, configuration and access rules. Where a finding needs confirming, we make limited, non-destructive requests only with your written permission, and anything that writes data is reproduced on a local or staging copy, never on production.
  • Not a redesign. We change what is broken, not what you would like to look different.
  • Not a rebuild. Where the honest answer is that the app should be rebuilt rather than repaired, the audit says so and we quote that separately.

01Self-assessment

Signs your app needs this

Not a benefits list — things that are either true of your situation or not. If three or more are true, the audit will find something.

  • The app was built mostly by an AI coding tool and no engineer has reviewed it since.
  • You cannot say, confidently, which tables have access policies on them.
  • A key beginning `service_role`, `sk_` or `admin` appears anywhere in code that runs in a browser.
  • You have never opened the network tab and tried reading data as a signed-out visitor.
  • Your payment provider’s dashboard and your own database have never been reconciled against each other.
  • When something breaks in production you find out from a customer rather than from an alert.
  • There is one database, and staging and preview deployments point at it.
  • Nobody has ever restored from a backup.
  • Admin actions are hidden in the interface, and you have not checked whether the endpoints behind them are protected.
  • You are about to take real payments, or real customer data, for the first time.

02Approach

How we approach it

Why AI-built apps fail in the same few ways

An AI coding assistant optimises for the request in front of it. Asked for a dashboard that shows a user their orders, it will produce exactly that: a query filtered by the current user, rendered in a table. The result works. What it does not do — because nobody asked — is decide what happens when a different user changes the identifier in the request, or when the same webhook arrives twice, or when the database has fifty thousand rows instead of fifty.

These are not bugs in the ordinary sense. Nothing is broken on the path the tool was asked about. The failures live on the paths nobody described: the adversarial one, the concurrent one, the one where a third-party service returns a 500. That is why the same four or five problems appear in app after app regardless of who built them or what they do.

It is also why the work is predictable enough to sell at a fixed price. We are not exploring an unknown codebase hoping to find something. We are checking a known list against a shape we have seen before, and the list is published on this site.

Access control is the whole game

Almost every serious finding reduces to one question: where is the decision made about whether you are allowed to do this? In an app that came out of a builder tool, the answer is usually "in the browser" — the interface hides what you cannot do, and the endpoint behind it assumes the interface did its job.

That is not a security model. It is a user-experience convenience that has been mistaken for one. The fix is unglamorous and mostly mechanical: every privileged action checks the caller on the server; every tenant identifier is resolved from the authenticated session rather than from what the client sent; every table has a policy, and every policy has a test that asserts a user of one tenant cannot reach another’s rows.

We insist on the tests because access control decays silently. A migration adds a table, nobody adds a policy, and the gap does not announce itself — the application keeps working perfectly for every honest user. A test that fails the build is the only thing that catches it.

Money needs a reconciliation, not a webhook

The common pattern is: a customer pays, the payment provider sends an event, a handler writes a row. Three things go wrong with it. The handler does not verify the event came from the provider, so anyone who finds the URL can grant themselves a subscription. It is not idempotent, so the provider’s ordinary retries create duplicate records. And it handles one event — the successful checkout — so cancellations, refunds and failed renewals never reach your database at all.

The deeper problem is treating your own database as the source of truth about what somebody has paid for. It is not. The payment provider is. What you hold is a cache, and like any cache it needs a way to be rebuilt. We add signature verification and idempotency because they stop new damage, and a reconciliation because it repairs what has already happened.

This matters more than it sounds. By the time a founder notices, there is usually a set of customers whose access does not match their billing in both directions — people paying for something they cannot use, and people using something they stopped paying for.

The operational layer is not optional

Error tracking, uptime checks, environment separation, migrations, backups and a tested rollback are the parts most often missing, and they are the parts that decide whether a bad day lasts twenty minutes or a weekend. None of them are visible to a user, which is exactly why a tool asked to build a product never produces them.

The test we apply to a backup is whether it has been restored. An export schedule nobody has ever recovered from is an assumption, and assumptions fail at the worst moment. We execute a restore into a scratch environment during the sprint, so the answer to "can we recover" is a thing that happened rather than a setting that was enabled.

Where the authorization decision is made Two request paths from a browser. The upper path reaches the database directly with no check. The lower path passes through a server-side check before reaching the database. Browser Your server Database No check runs here Check Hidden in the UI Request from anywhere Your data
Hiding a control in the interface decides what is rendered. It decides nothing about what an endpoint will do when called directly.

03Choices

Decisions you will have to make

Fix in place, or move off the platform?

Fix in place
The platform lets you reach the code, the database and the deploy pipeline. True for most Lovable, Bolt and Cursor projects on Supabase or Postgres.
Migrate off
The platform owns part of the stack you need to change — usually the server layer or the auth model — so a finding cannot be fixed without leaving.

Where we start Fix in place. Migration is a bigger, riskier project and we will not recommend it unless the audit shows a finding that cannot be closed any other way.

Row-level security, or server-side authorization?

Database policies (RLS)
The client talks to the database directly, as it does in a typical Supabase build. The policy is then the only thing standing between a browser key and your data.
Server-side checks
You have an API layer of your own. Authorization lives in code where it can be read, tested and logged more easily than a policy expression.

Where we start Both, in that order. Policies as the floor that cannot be bypassed, server-side checks for anything with business logic in it. Defence that depends on one layer is defence that fails completely.

Repair the data, or start the counters again?

Reconcile
Real customers are affected — duplicate subscriptions, wrong entitlements, orphaned records. The work is a script plus a review, and it is the honest option.
Cut over
The corrupt data is test noise from before launch, where reconciling costs more than it returns.

Where we start Reconcile anything a customer can see or has been billed for. Nobody thanks you for a clean schema that still disagrees with the payment provider.

04Price

What moves the price within the range

A hardening sprint is $1,800 to $2,500. The audit decides where in that range it lands, and it is the only way either of us can know. These are the factors that move it.

$1,800 from — range $1,800–$2,500 See what is included

Number of tables needing policies

A handful is an afternoon. Forty tables with overlapping roles is most of a week, because each one needs a policy and a test.

Multi-tenancy

The single biggest factor. Retrofitting tenant isolation touches every query, every policy and every test.

Payment state to reconcile

Adding verification is quick. Repairing months of drift between your database and the provider is not.

Whether the client talks to the database directly

A direct-to-database app has no server layer to put checks in, so some of the work is building one.

Test coverage that already exists

Changing access control without tests means writing them first. That is worth doing and it takes time.

How much has been hand-edited

Generated code is consistent and quick to reason about. Half-rewritten generated code is slower than either.

Platform constraints

Where the platform owns the server layer, some fixes need a workaround rather than a correction.

Live customers

Fixes that would be a single migration on an empty database become a staged change with a backfill.

05Process

How a project runs

  1. Audit

    Two business days

    We work through security, authentication, payments, deployment and — for mobile — store readiness, then write a report that ranks every finding by severity with the evidence it came from and an estimate in hours.

    What we need
    Read-only access: your repository, and viewer access to hosting and the database.
    What you get
    A written report, a recorded walkthrough, and a fixed-price sprint quote drawn from it.
  2. Scope

    Same day

    We agree in writing which findings the sprint covers and which it does not. Anything excluded stays in the report so you can act on it later or elsewhere.

    What we need
    A decision, and a signed testing authorization if any confirmation needs live access.
    What you get
    A scope document and a fixed price. The audit fee comes off it.
  3. Fix

    One to two weeks

    Work lands as branches and pull requests you review. Criticals first, in dependency order — an exposed admin key is resolved before the policies it would otherwise bypass.

    What we need
    Write access for the duration, and someone available to review a pull request.
    What you get
    The fixes, the tests that keep them fixed, and monitoring wired up.
  4. Handover

    Half a day

    A written record of every change, a walkthrough of the monitoring, and the restore rehearsal if backups were in scope.

    What we need
    An hour of your time.
    What you get
    A handover document, and write access revoked the same day.

06Deliverables

What you actually end up with

The audit report

Every finding ranked by severity, with the evidence it came from, its impact, the recommended fix and an estimate in hours. Yours to keep and to show anyone, including another engineer.

A recorded walkthrough

Twenty minutes talking through the report, so the findings are understood rather than merely delivered.

Reviewed pull requests

Every change as a branch you approve before it merges. Nothing appears in your codebase that you have not seen.

Tests that hold the fixes

Access-control tests asserting cross-tenant reads fail, webhook idempotency tests, and a build-time scan for keys in client output.

Monitoring you own

Error tracking with releases and source maps, uptime checks on the paths that matter, and alerts routed somewhere a human reads.

A restore that happened

Where backups are in scope, the restore is executed into a scratch environment rather than configured and assumed.

A written handover

Every change described in plain language, so the next engineer — or you — can pick it up without us.

07Stack

What we build it with

Rescue work is mostly database policies, server-side authorization and the deployment layer, so Postgres, the server framework already in place, and observability are where the time goes. We work in whatever the app was built with rather than rewriting it into our preferences.

Data Primary here

Databases and storage

Postgres unless there is a specific reason otherwise, migrations in the repository, and a restore that has been executed rather than merely configured.

We default to

  • Postgres
  • Supabase
  • Redis
  • Migrations
  • Backups and restore

We also work in

  • Neon
  • PlanetScale
  • MySQL
  • SQLite
  • Turso
  • Firebase Firestore
  • Upstash
  • Drizzle ORM
  • Prisma
  • Kysely
  • pgvector
  • Row-level security
  • Point-in-time recovery
  • Meilisearch
  • Typesense
  • Elasticsearch
  • S3
  • Cloudflare R2
  • Read replicas
  • Connection pooling
API Primary here

Backend and services

Boring, typed and synchronous until something measurably needs to be a queue. Most systems need fewer moving parts than they are given.

We default to

  • Node.js
  • Python
  • REST
  • tRPC
  • Background jobs
  • Queues

We also work in

  • Hono
  • Fastify
  • Express
  • NestJS
  • FastAPI
  • Django
  • GraphQL
  • OpenAPI
  • WebSockets
  • Server-sent events
  • BullMQ
  • Celery
  • Inngest
  • Trigger.dev
  • Cron and scheduled work
  • Stripe
  • Paddle
  • Razorpay
  • Resend
  • Postmark
  • Twilio
  • Webhooks in both directions
Ops Primary here

Infrastructure and observability

Managed platforms until scale or cost argues otherwise. The parts we insist on are a deploy you can roll back and an error that reaches a human.

We default to

  • Vercel
  • Cloudflare
  • Docker
  • GitHub Actions
  • Sentry

We also work in

  • AWS
  • Cloudflare Workers
  • Cloudflare R2
  • Cloudflare D1
  • Fly.io
  • Railway
  • Render
  • Terraform
  • Nginx
  • Caddy
  • GitLab CI
  • Grafana
  • Prometheus
  • Axiom
  • Better Stack
  • Checkly
  • Uptime checks
  • Structured logging
  • Staged rollouts
  • Tested rollback
Web

Frontend and full-stack

TypeScript everywhere, and a framework that renders on the server unless the product is purely an application behind a login.

We default to

  • TypeScript
  • React
  • Next.js
  • Astro
  • Tailwind CSS
  • Vite

We also work in

  • JavaScript
  • Remix
  • SvelteKit
  • Vue 3
  • Nuxt
  • shadcn/ui
  • Radix UI
  • TanStack Query
  • TanStack Table
  • React Hook Form
  • Zod
  • Zustand
  • Redux Toolkit
  • Framer Motion
  • MDX
  • Storybook
  • Vitest
  • Playwright
  • Testing Library
  • ESLint
  • Prettier
  • Recharts
  • D3
App

Mobile

Expo unless the product is the platform experience. One codebase is worth a great deal when the maintenance bill outlasts the project.

We default to

  • React Native
  • Expo
  • EAS Build
  • Swift
  • Kotlin

We also work in

  • Capacitor
  • Flutter
  • SwiftUI
  • Jetpack Compose
  • Expo Router
  • React Navigation
  • Reanimated
  • MMKV
  • WatermelonDB
  • StoreKit
  • Google Play Billing
  • RevenueCat
  • APNs
  • Firebase Cloud Messaging
  • Expo Notifications
  • Fastlane
  • App Store Connect
  • Google Play Console
  • Detox
  • Maestro
  • Privacy manifests
AI

AI systems

Hosted models behind an interface you can swap, with the eval set deciding which one you use this quarter rather than a contract.

We default to

  • Claude
  • OpenAI
  • MCP
  • Evals
  • Tracing
  • Cost control

We also work in

  • Gemini
  • OpenRouter
  • LiteLLM
  • Vercel AI SDK
  • Anthropic SDK
  • Tool and function calling
  • Structured output
  • Prompt caching
  • Batch APIs
  • Model routing
  • pgvector
  • Qdrant
  • Pinecone
  • Chroma
  • Hybrid retrieval and re-ranking
  • Langfuse
  • LangSmith
  • Braintrust
  • Helicone
  • Ollama
  • vLLM
  • OAuth 2.1 for MCP

Split into what we default to and what we also work in. If your system is outside this, tell us on the call — we would rather say no than learn a stack on your budget.

08Pitfalls

Mistakes we see most

Turning on row-level security and calling it done

Enabling policies while a service-role key is still in the client bundle changes nothing — that key bypasses every policy by design. The key has to be rotated and moved server-side first, or the work is theatre.

Trusting an identifier the client sent

A tenant or organisation ID arriving in the request body is a suggestion, not a fact. It has to be resolved from the authenticated session, and the client-supplied version rejected rather than quietly ignored.

Hiding admin controls instead of blocking them

A conditional in a React component decides what is rendered. It decides nothing about what an endpoint will do when called directly with a normal user session.

Writing subscription state straight from the webhook payload

Events arrive out of order, more than once, and occasionally forged. Treat the provider as the source of truth and reconcile against it rather than writing whatever the last event said.

Using one database for every environment

It means preview deployments write to live customer data, migrations are tested in production, and there is nowhere safe to rehearse a restore.

Building no admin tooling

Without it every support request — refund a customer, fix a typo in a record, unblock an account — needs an engineer with direct database access. That is both a bottleneck and a second security problem.

Catching errors and returning a generic 500

It makes the application look tidy and makes production undiagnosable. The cause has to go somewhere a human will see it.

09Honestly

When you should hire somebody else

  • Your app is a prototype you intend to throw away. Harden the thing you are keeping, not the thing you are learning from.
  • You need a security certificate or a penetration test for a customer or an auditor. That is a different discipline and a specialist firm should do it.
  • You want the app redesigned. We fix what is broken; we do not art-direct.
  • The application needs rebuilding rather than repairing. If the audit concludes that, we will tell you, and the sprint is the wrong purchase.

10Questions

Questions

Something we haven’t covered?

Ask on a call
01 How do I know whether my app needs this?

If it was built largely by an AI coding tool and it has not been reviewed by an engineer since, it almost certainly has at least one of the four critical findings in our sample report — open database policies, a key in the client bundle, an unverified payment webhook, or authorization enforced only in the interface. The $349 audit exists to answer exactly this question, and if it finds nothing material you get the fee back.

02 Can you work on an app built with Lovable, Bolt, Replit or Cursor?

Yes. Those four plus v0 and Base44 are most of what we see, and the backends behind them are usually Supabase, Firebase, Neon or plain Postgres. Some platforms make parts of the stack unreachable — typically the server layer — and where that is the case the audit says which findings cannot be fixed without moving off, and what moving would involve.

03 Will you break my app while fixing it?

Everything lands as a branch and a pull request you review before it merges, so there is no point at which you first see a change that is already live. Access-control changes ship with tests that assert the intended behaviour, and anything touching payments is reconciled against the provider rather than assumed. Where a fix has to run against live data, it goes out as a staged change with a backfill rather than a single migration.

04 Do I need the audit, or can you just start fixing?

The audit is required, because it is the only way either of us knows what the work is. Quoting a fixed price for an unknown codebase means padding the number to cover the worst case, which is worse for you. The fee is credited in full against the sprint, so it costs nothing extra if you go ahead.

05 What access do you need, and when?

Read-only for the audit — your repository plus viewer access to hosting and the database. No production credentials and no customer data. Write access is requested only when a sprint starts and is revoked the day it ends. If a finding needs confirming with a live request, we ask for written permission first and anything that writes data is reproduced on a local or staging copy.

06 How long does it take?

The audit is two business days from the moment access works. A sprint is one to two weeks depending on what the audit found. The largest single factor is whether tenant isolation has to be retrofitted, because that touches every query rather than a handful of files.

07 What happens to the findings you do not fix?

They stay in the report with their severity and estimate, so you can schedule them, hand them to another engineer, or leave them knowingly. Nothing is quietly dropped because it was out of scope.

08 Do you offer ongoing support afterwards?

Optionally. A care retainer covers monitoring, dependency and security updates, and the store deadline work that comes round every year. Plenty of apps will not need one, and we would rather say so than sell it.

09 Who owns the code?

You do, throughout. It lives in your repository on your accounts under your billing from the first commit, so there is nothing to transfer at the end and no layer of ours left behind that you would need us to maintain.

11Also

The other services

Mobile & Store Launch

Your app packaged, compliant and submitted — through review, not just uploaded.

Web development

New applications and new features, built production-ready from the first commit.

App development

Mobile apps built to ship — iOS and Android, through review and into the stores.

Every engagement starts with the audit.

It is the only way we will quote a sprint, because it is the only way either of us knows what the work actually is. Fixed price, two business days, and credited in full if you go ahead.

Prefer to write? Send your requirements

What do you need?

What it should do, who will use it, the must-haves, and any links. A few sentences is plenty.

Budget, if you have one in mind
When would you like to start?