Skip to content

Incident report: Sites & Functions runtime vulnerability, November 1st, 2026_

How Appwrite detected and fixed a Sites and Functions runtime vulnerability exploited on November 1st, 2026, who is affected, and which secrets to rotate.

6 min read

On November 1st, 2026, an attacker running code in an Appwrite project exploited a vulnerability in our function runtime. The vulnerability revealed credentials held by one of our internal services in the Frankfurt region. For around 11 hours, from 03:53 to 14:46 UTC, one of those credentials could have been used to read the environment variables of Functions and Sites running on our Frankfurt compute. A proactive security scan we ran detected the attack the next day, and we contained it within hours.

We found no evidence that the attacker read any variables using that credential. In all cases, their activity was aimed at a single customer. However, as a precaution, we are notifying every customer whose Functions or Sites could have been exposed. If you received our email, it lists the projects and variable names affected.

The vulnerability is fixed, we have replaced every exposed credential, and the attacker's accounts are blocked. We apologise for any concern this incident may cause and appreciate your continued trust.

What happened

Starting on October 30th, the attacker targeted one customer's application. They got in through weaknesses in that customer's own code and setup, not through a vulnerability in Appwrite. We have worked with that customer directly.

On November 1st, the attacker created functions in a project of their own and used them to attack the platform. Our function runtime is meant to keep each customer's code away from the internal services around it. One of those services, which routes traffic to functions and sites in Frankfurt, handled files written by customer code unsafely. The attacker used that to read the service's configuration, which held several internal credentials.

The attacker used three of those credentials, mostly against the customer they were already targeting, who has the full details.

This report is about a different credential, one we have no record of anyone using. It was an access token that could read the configuration of every function and site on that Frankfurt compute, environment variables included.

In the same session, the attacker used a second, unrelated vulnerability in our storage API, which let a request write outside its project's storage. The only file written through it was the attacker's own test file. We deleted it and fixed the vulnerability.

Timeline

When (UTC)What happened
October 30th–31stThe attacker compromises one customer's application through that customer's own code and setup.
November 1st, 03:53From their own project, the attacker exploits the runtime vulnerability and gets the Frankfurt service's credentials.
November 1st, 04:06–13:33The attacker uses some of the credentials against the one customer and to probe our storage and content delivery setup.
November 1st, 14:46A routine release replaces the affected service. The access token stops working.
November 2ndOur automated security monitoring detects the attack on the platform. Within hours, we block the attacker's accounts and projects, ship the fix, and replace every exposed credential.
November 2nd–4thWe work out which customers had secrets in reach and check those projects for signs of misuse.
November 4thWe start notifying affected customers.

How we responded

We blocked the attacker's accounts, projects, and keys and replaced every credential the service held. The old values are now refused everywhere.

We then checked each exposed credential against our own logs and our providers' logs, in all six regions, for any use we couldn't explain. Those logs show what the attacker did with the credentials:

  • Read one customer's function code and logs, ran one of that customer's functions, and swapped the function's code for 31 seconds before putting it back.
  • Listed our internal storage and wrote test files into their own project. Our storage provider's request log shows they read no other customer's files.
  • Made one change to our content delivery setup, which was never activated and had no effect.

The same checks gave us the list of customers whose secrets were in reach.

For the access token, our infrastructure provider's audit log for the window is incomplete because of a fault on their side, and the missing records can't be recovered. So we checked the other records the token would have touched: every change to the compute's configuration, every workload started, and every interactive session opened. All of it traces back to our own systems and staff. These records can't show a read.

What we're changing

We've fixed the root cause. Our services no longer follow links that customer code plants, and they skip any file from a runtime or build that isn't a plain file.

Beyond the fix, we've shipped a series of changes across the platform since November 1st. Most of them limit what customer code can reach and what each internal service can access, so a similar vulnerability would expose very little.

  • Every runtime and build is confined. A mandatory security profile on every container that runs customer code, and on the services around it, stops any process from reading another process's configuration.
  • Customer code runs unprivileged, on its own machines. In every region, functions and sites now run as a restricted user, and platform services no longer share machines with customer code.
  • Each service has its own credentials. We split secrets that several services used to share, so a leak in one place unlocks only that place. In all six regions, our main services, caches, database proxies, and network tunnels no longer carry credentials for the system that runs our workloads.
  • New signing and encryption keys. Everything we sign or encrypt now uses a new key version, and the old version is refused everywhere. We are re-encrypting stored data with the new key.
  • Fast rotation is built in. Our main internal secrets now accept an old and a new version side by side, so we can replace them without downtime.
  • Blocking a project stops its code at once. That now includes copies of its functions and sites that are already running.

Who is affected

We are notifying the owners of 1,022 projects. A project is on the list if all of these are true:

  • It had a function or site running on our Frankfurt compute between 03:53 and 14:46 UTC on November 1st. This compute also serves sites for projects in our other regions, so some of those projects are included.
  • That function or site had an environment variable that was set before 14:46 that day.
  • The variable's name marks it as a secret, such as a key, token, password, or database connection string. We left out variables that are public by design, such as publishable keys and public client IDs.

If you weren't contacted, none of your projects met these conditions.

What you should do

If we emailed you, treat the listed variables as exposed and rotate them. Most of them belong to services outside Appwrite, so we can't rotate them for you.

  1. Create new values at the service that issued each secret (payment provider, email provider, AI provider, database, and so on), then revoke the old ones.
  2. For Appwrite API keys, create a new API key with only the scopes the function needs, update the variable, redeploy, then delete the old key.
  3. Update the variable in each listed function or site and redeploy it, so running copies pick up the new value. See environment variables for Functions and Sites.
  4. Check each service's own logs for activity you don't recognise since 03:53 UTC on November 1st.

This incident happened on Appwrite Cloud. If you self-host Appwrite, upgrade to the 2.7.1 patch release, which brings the same root-cause fix to self-hosted installs. Follow the updates and migrations guide to upgrade.

If you find anything unexpected or need help, reply to our email. If you didn't receive one and have questions about this incident, contact us at security@appwrite.io.

Only if you received a notification email from Appwrite. We notified the owners of 1,022 projects that had a function or site running on our Frankfurt compute between 03:53 and 14:46 UTC on November 1st, 2026, with an environment variable set before 14:46 that day whose name marks it as a secret. If you weren't contacted, none of your projects met these conditions.

We found no evidence that the attacker read any environment variables. We can't fully rule it out, because our infrastructure provider's audit log for the window is incomplete and the missing records can't be recovered. As a precaution, we are notifying every customer whose variables were in reach.

Rotate every variable listed in our email. Create a new value at the service that issued each secret and revoke the old one. For Appwrite API keys, create a new key with only the scopes the function needs, update the variable, redeploy, then delete the old key. Redeploy each listed function or site so running copies pick up the new value.

Read next

Ready to build?_