What Ledge can access when you connect a site, and how it's protected.
Connecting a site to Ledge gives it some access to that site, its repository and, if you turn them on, copies of its database. This page sets out what each part can reach, what leaves your server, and what we do to protect it, so you can decide what to turn on for each client.
| Feature | What Ledge can access | Off by default? |
|---|---|---|
| Health checks | Versions, check results and counts. No content, no secrets. | No |
| Uptime checks | Your public pages, like any visitor. | No |
| Advisories | Installed package names and versions. | No |
| Backups | An encrypted copy of the database. | Yes |
| Update runs | Write access to a branch in your repository. For Craft sites, also an encrypted copy of the database and environment variables. | Yes |
| MCP | Whatever the token or connection you create allows. | Yes |
The plugin and package add a few endpoints under /_ledge/. They only answer requests carrying your site's secret key, which is generated on your server and compared in constant time. Responses are marked not to be cached.
What they report: Craft, plugin and PHP versions, pending updates, licence status, check results such as queue counts and disk usage, the names of missing environment variables, and installed Composer packages.
What they never report: entries, users, form submissions, environment variable values, licence keys or passwords. Failed-login checks send a count, not usernames or IP addresses.
The endpoints only report. They can't run commands, change settings or write to your site. The one exception is starting a database copy, which is off until you turn on backups or update runs, and needs a signed request from Ledge on top of the secret key (see below).
What you should do:
generate-key writes it to .env.Backups and update runs both start from a copy of the database. It's taken on your server and never through a direct database connection.
An update run needs a working copy of your site, so this is where Ledge has the most access. It's worth reading this section before you turn update runs on.
For Craft sites, the sandbox gets:
Ledge replaces the database credentials, points site URLs at the sandbox, turns off email and empties the queue, so the copy can't send mail or run your queued jobs. It doesn't block every outbound request: code that calls a payment provider, CRM or other API during a page load or migration can still reach it with the keys it finds.
Laravel runs get your code only, and run your test suite with test settings instead of production data.
acquireEnvDenylist. If the site needs a value to boot, add a test or read-only key as an environment override in Ledge.Ledge only ever pushes to its own new branch and opens a pull request. It never pushes to your default branch. A pull request can only change composer.json, composer.lock and files in config/project/. Nothing is merged unless you merge it, or turn on auto-merge. Auto-merge is off by default, and you can turn it on for a site's scheduled runs, for one run when you approve it, or for a campaign. It waits for your CI checks to pass. On a repository with no checks, it merges straight away. Your branch protection rules still apply.
See MCP server for what each tool can do.
| Service | What for |
|---|---|
| Fly.io | Update-run sandboxes, uptime probes and encrypted object storage for backups |
| GitHub, GitLab, Bitbucket | The repositories you connect |
| Stripe | Billing |
| Sentry | Error tracking for the Ledge app |
| Anthropic | Explaining why a failed update run failed. Ledge sends the end of the run log, after removing anything that looks like a token, password or key. |
| Postmark | Email alerts and account emails |
| Slack, Discord | The alerts you set up |
If you think you've found a vulnerability in Ledge, please email us before sharing it publicly.