It is 2 a.m. An advisory goes out for a package you have never heard of. It sits four levels down in a lockfile, pulled in by something that was pulled in by something else, and it is running in production right now, inside an app someone built in an afternoon with an AI agent and shipped before lunch.
Nobody is awake. Nobody remembers that app exists. That is how most of these stories start: not with a brilliant attacker, but with a forgotten dependency, a key pasted where it should never be, or an admin page that nobody thought to lock.
Three ways an internal app gets you
- The forgotten dependency. The app worked, so nobody touched it. Its lockfile froze the day it shipped, and the advisories kept coming.
- The leaked key. A vibe-coded app talks to Supabase straight from the browser, and somewhere in the bundle sits the service-role key, the one that bypasses Row Level Security on the whole database. Every visitor downloads it.
- The open door. The app has an
/adminpage. The agent that wrote it never checked who was asking.
None of this is exotic. On most platforms, each one is a developer's problem, alone, at 2 a.m. Here is what changes when the app runs on AppCrane.
Every deploy is scanned
Every deploy scans what it just shipped and records the result against that deployment. Apps built from source are read from their lockfiles (npm, yarn, pnpm, Composer, Go, Cargo, RubyGems, pip and Poetry) and checked against OSV. Apps deployed from an image are scanned with a pinned Trivy against the resolved digest: the bytes actually running, not whatever the tag points at today. Each finding carries the version that fixes it.
The report does not hide behind a reassuring word. It opens with arithmetic: how many app-and-stage rows have a usable scan result, out of how many, as a percentage, with the skipped and errored rows counted beside it. "Partial" can mean one per cent or ninety-nine, so it never travels alone.
A daily digest goes to each person scoped to what they are allowed to see: the whole fleet for a platform admin, their own apps for an app owner. The forgotten app is no longer forgotten. The daily scan re-checks what is running, and its owner hears about it in the next digest.
Production has a door, and it locks
The scan itself never blocks: a scanner that is missing, slow or wrong cannot fail your deploy. What stands at the door is the release check, which runs on every production deploy and promote. It looks at the app's intake, the dependency and image scan, secrets committed in the code, the required access rules, whether the single sign-on gate has been bypassed, and the health of the sandbox.
By default, a dependency with a known vulnerability and a published fix blocks production, for every app. The deploy is refused before it starts, and the version already in production keeps running. The other checks block apps classified confidential or restricted and warn for the rest.
Sometimes you need to ship anyway. An app owner or platform admin can record an exception for one check, with a reason and an expiry of at most 90 days, and the exception goes in the audit log. A platform admin can also turn dependency blocking off in Settings → Security.
The fix files itself
A finding that only lives in a report is a finding nobody fixes. So each advisory becomes an ordinary request in the app owner's queue, reading something like Upgrade proxy-addr 2.0.7 → 2.0.8 (GHSA-jqcg-44mw-7w3h), with the ecosystem, the environment it was found in and the advisory link. It is opened once per app, package and advisory, so the daily scan does not bury anyone in duplicates.
For an app whose code AppCrane hosts, the owner can tick the request and press Send to coder. The in-platform coder works on the sandbox and leaves its change for a person to review; nothing reaches production until someone releases it, promotes it and the release check passes.
A remote-code-execution advisory does not wait for anyone. With RCE auto-patch, on by default, AppCrane bumps only the vulnerable npm package to its fixed version, deploys it to the sandbox, runs the release check and promotes it to production on its own, under a written, audited policy. It only acts when the branch is exactly what production runs, waits a day after the fixed version is published, and rolls production back if its health check fails. Every other fix still goes to a person. When the latest successful scan of every environment no longer finds the advisory, the request closes itself. A failed scan never closes one.
The 2 a.m. job used to be: notice, triage, find the package, upgrade, test, ship. Here, it can shrink to reading a diff and pressing Release.
The doors the agent forgot
Dependencies are half the story. The other half is what an AI-built app was supposed to do and did not.
Sign-in it never wrote
Apps on AppCrane do not implement their own authentication. Every request passes through AppCrane's proxy first, which removes any X-AppCrane-* header the browser sent, checks the session (single sign-on over OIDC or SAML, or a local sign-in), and only then hands the app the verified identity. No session means a sign-in page, not the app. The identity headers are also signed per app and environment, so the app can reject any request that did not come through AppCrane.
The admin page it never locked
An app can declare in deployhub.json that /admin/* needs the admin role. AppCrane's gate then answers everyone else with a 403 before the request ever reaches the container, and the rule holds against method-override headers, .json suffixes and framework data routes. The app does not have to remember to check.
The secrets it would have leaked
Environment variables are encrypted with AES-256-GCM and shown masked. Revealing one is a deliberate press that is audited and alerts the app's owners; a production value needs the owner or a platform admin, and a platform admin who is not assigned to the app cannot read it at all. With the LLM gateway on, the app gets a gateway token instead of your Anthropic or OpenAI key, and the real key never enters the container.
Adopting the app someone else built
The scariest app is the one that already exists. Adopt an app takes a Lovable, Bolt, v0, Replit or Figma Make project, any GitHub repository or a zip, and reads the code without running it. It reports the framework, the environment variables to fill, its use of Supabase, Firebase, Clerk or Auth0, and any exposed keys. A Supabase service-role key in client code is flagged as critical and must be acknowledged before anything deploys, and the first deploy goes to the sandbox only, with the app's secrets stored encrypted. Rotating that key in Supabase is still yours to do; the report tells you so, plainly.
When PHP2Shell landed
On 21 October 2026 PHP shipped fixes for PHP2Shell, two critical remote code execution flaws in PHP itself (CVE-2026-109618 and CVE-2026-109619), after details leaked early and exploitation was reported in the wild (watchTowr). The fix is in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
This is the kind of flaw no lockfile scan catches: it is in the runtime, not in a dependency. So AppCrane now patches the runtime too. Every build pulls a fresh copy of its base image, so a rebuild picks up the fixed PHP instead of a stale cached copy. An hourly check reads the PHP version inside every running app, and when it is affected and a fixed image exists, AppCrane rebuilds the same code on it, tests it in the sandbox, confirms the new PHP is fixed, runs the release check and promotes it to production on its own, rolling back if production health fails. Apps it cannot rebuild, such as a PHP branch with no fix, are listed in Job Health for a person, and platform admins are e-mailed the list.
When something does happen
Every action is in the audit log, tagged as taken by an agent or a human. The log is sealed into a SHA-256 hash chain with signed checkpoints, and events and checkpoints can stream to your SIEM, so someone other than the server's operator holds a copy. AppCrane's own password sign-in takes an authenticator code for platform admins by default, and sessions end on the server after a fixed lifetime or idle time.
Back to 2 a.m.
The advisory still goes out in the middle of the night. AppCrane cannot stop that. But within a day the scheduled scan has recorded it against the app, the owner has a request naming the fixed version, the next production deploy that still carries it will be refused at the door, and one click sends it to the coder, which proposes the upgrade for someone to review over coffee.
You still care about security. You just get to sleep.
AppCrane is a self-hosted platform for internal and AI-built apps. AGPL-3.0. Free. Runs on any Ubuntu 22.04+ server.