2.6.2
Appwrite 2.6.2 is a security release for self-hosted deployments on the 2.6 line. It brings the hardening work merged since 2.6.1 across auth, API keys, databases, realtime, GraphQL, storage, VCS, functions, sites, migrations and messaging. It also moves the runtime to a patched Brotli and the Console to 1.2.108. There are no schema changes: upgrading from 2.6.1 keeps migration V25. A few fixes tighten defaults that a self-hosted install may rely on, so read Upgrade before you upgrade.
Highlights
- Brotli CVE-2026-111497.
appwrite/base2.2.7 updatesbrotli-libsfrom 1.2.0-r1 to 1.2.1-r0. 1.2.1 fixes an out-of-bounds write in the Brotli decoder. Appwrite is exposed through its outbound HTTP client: when a caller doesn't setAccept-Encoding, the cURL adapter accepts and decodesbr, and the avatar image and favicon endpoints fetch user-supplied URLs that way. The Swoole HTTP client links the same library (#14765). - API keys and JWTs stay within their grant. A key can no longer create or change a key with scopes it doesn't hold or an expiry later than its own, a JWT can't manage keys, account keys are limited to the console, and ephemeral keys are bound to their project (#14888, #14795, #14755, #14825).
- Realtime and GraphQL follow the HTTP rules. Realtime connections apply the same session checks as HTTP, admin mode needs project access, and console tails need project read. GraphQL keeps the caller's identity across the fields of one request (#14803, #14790, #14797, #14820).
- Outbound requests stay on public addresses. Project SMTP, messaging SMTP providers, migration sources and certificate checks now refuse private and reserved hosts unless you list them in
_APP_ALLOWED_INTERNAL_ADDRESSES, the same rule webhooks already use (#14811, #14794, #14870). - Console 1.2.108, which includes the DOMPurify sanitization fixes (#14892).
Fixes
Auth and accounts
- Clear pending recovery tokens when the account email changes (#14347).
- Authenticate API keys before skipping Origin checks (#14352).
- Apply project email policies to ID-token sign-in (#14633).
- Match existing users by exact email on OAuth and ID-token sign-in (#14778).
- Revoke prior credentials when OAuth links an unverified account (#14801).
- Tie phone tokens and invite verification to the address they reached (#14819).
- Rate limit token session attempts per account (#14880).
- Accept admin mode only from console origins (#14813).
- Read the onboarding project from the request params (#14807).
- Compare MFA codes in constant time (#14762).
Users and keys
- Hide credentials from impersonators (#14818).
- Require
users.writeto read MFA recovery codes (#14872). - Bound imported password hash costs, fix updates that send an empty password, and require a purpose for ephemeral keys (#14825).
- Keep key and webhook secrets out of the 1.9.4 project response format (#14815).
Databases
- Enforce permissions when staging transaction operations, and accept
documents.readon legacy transaction reads (#14754). - Gate increment and decrement, fix upsert permissions, and remove
embeddings.writefrom guests (#14755). - Rate limit guest transaction writes (#14762).
- Scope the list cache to collection permissions and gate inline encryption (#14791).
- Build transaction delete events from the stored document (#14781).
- Read staged increments on top of the stored value (#14751).
- Read transaction logs and the tenant database once per request (#14749).
- Fix writes to columns with dots in their names (#14753).
- Match exact searches without InnoDB phrase positions, and tighten function name and cast parsing in queries (#14871, #14866).
Organizations and projects
- Accept only keys and console sessions on organization routes (#14779).
- Evaluate project roles against the project in the path (#14787).
- Require owner on both organizations to transfer a project (#14780).
Realtime and GraphQL
- Apply the HTTP session rules to realtime connections (#14803).
- Require project access for admin mode connections (#14790).
- Authorize console tails with project read access (#14797).
- Send migration events only to project readers (#14805).
- Validate presence message IDs and metadata like the HTTP endpoint (#14829).
- End MQTT connections with the session or token they were opened with, and close the connection on a second CONNECT (#14804, #14809).
- Keep the caller's identity across the fields of one GraphQL request (#14820).
- Cost invalid list limits at the GraphQL complexity cap (#14821).
Storage
- Build
Content-Dispositionper RFC 6266 and validate file names (#14789). - Refuse reads of files whose upload isn't complete (#14876).
- Keep the platform default bucket private (#14814).
- Neutralize client-controlled upload paths in the Swoole adapter (#14897).
- S3 gateway: require
x-amz-*request headers to be signed, bound multipart parts, reclaim expired staging, honour bucket antivirus, send the same content headers as storage view, and reclaim replaced object versions (#14799, #14796, #14634, #14766).
Functions and sites
- Validate reserved
x-appwriteexecution headers case-insensitively (#14185). - Strip Appwrite session cookies from function and site requests (#14874).
- Keep console auth cookies out of runtimes and tighten preview access (#14823).
- Bind branch preview rules and build state to their project (#14786).
- Confine repository root directories and build artifact reads (#14808).
- Commit a deployment's ready status with its activation (#14525).
- Keep runtime and build job IDs unambiguous when IDs contain hyphens (#14817).
- Delete build cache with its function, site or runtime, and delete screenshot rows with their deployment (#14761, #14764).
- Reject an empty or default
_APP_JOBS_SECRET(#14116).
VCS
- Verify the GitHub webhook signature before fanning out the event (#14771).
- Verify GitHub installation access before connecting it (#14783).
- Bind installation state to the console user and an expiry (#14793).
- Match event repositories to their provider's installations (#14800).
- Verify TLS for Git provider API calls (#14802).
- Stop printing request params on provider errors (#14826).
- Name lost installation access in the repository-not-found error (#14650).
Migrations
- Confine migration and certificate check connections to public addresses (#14794).
- Apply the outbound host policy to migration sources (#14870).
- Validate NHost migration host parts (#14798).
- Check source key scopes for DocumentsDB and VectorsDB (#14868).
- Expose Migrations to server SDKs (#14882).
- Clean up expired and failed CSV/JSON exports, and remove import copies on project delete and refused imports (#14758, #14759).
Messaging and mails
- Require public SMTP hosts for project and messaging SMTP (#14811).
- Stop returning secret messaging provider credentials (#14875).
- Require
files.readto attach files to emails (#14827). - Escape the redirect variable in email templates (#14822).
- Expire push image tokens fifteen days after delivery (#14782).
- Use spam-safe characters for email preview padding, and decode HTML entities in the plain-text part (#14111, #14118).
Platform
- Key rate limits on the route path rather than the request host, and keep them on requests with an out-of-range
Content-Range(#14785, #14816). - Cap remote fetch size and rate limit avatar image and favicon requests (#14788).
- Reject CR, LF and NUL in header values (#14806).
- Resolve only registered param defaults as callables (#14792).
- Gate development-only routes on an explicit development environment (#14824).
- Never fall back to the platform database for a missing project in deletes (#14812).
- Report an unavailable cache as a 503 (#14863).
- Keep one copy of identical records in a DNS zone (#14371).
- Refuse CDN cache paths with dot segments (#14887).
- Check collector certificates on HTTPS OTLP exports, and serialize overlapping telemetry collects (#14865, #14732).
- Keep derived image crop sides at least one pixel (#14828).
- Set PHP precision to -1 so floats keep every digit (#14381).
- Make the PHP memory limit configurable with
_APP_MEMORY_LIMIT(#14522). - Tick the maintenance scheduler every second, start it on the next interval step, and release its claim on SIGTERM (#14734, #14885).
Under the hood
appwrite/base2.2.7 also updatesgit-remote-entirefor golang.org/x/net 0.61.0 (#14765).appwrite/browser0.3.8,appwrite/defender0.1.1 and ExifTool 0.1.1 (#14864, #14886).- The PostgreSQL container runs with
init, so orphaned health check processes no longer crash it (#14769). - Install generates the executor secret (#14770).
- 2.6.2 maps to migration V25, the same class as 2.6.1.
- Not in this release: the transaction
typeattribute (#14350), the MQTT topic instance ledger (#14867) and Orchestrator 4.0.0 (#14893). They need a schema change or a major dependency bump, so they wait for 2.7.0.
Removed
- The guests role no longer has
embeddings.write(#14755). - Messaging provider secrets and key/webhook secrets are no longer returned by the API (#14875, #14795, #14815).
Install
docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:2.6.2
Upgrade
Back up your data first. Upgrading from 2.6.1 uses the same command with --entrypoint="upgrade". Then run the migration, which has nothing to change in this release:
cd appwrite
docker compose exec appwrite migrate
Check these before upgrading:
_APP_JOBS_SECRET: an empty value or the shipped placeholderyour-secret-keyis now refused, and function job callbacks fail.upgradegenerates a secret for you. If you manage.envyourself, set a random value;doctorwarns while it's insecure.- Private hosts: project SMTP, messaging SMTP providers and migration sources that point at a private or reserved address now need that address in
_APP_ALLOWED_INTERNAL_ADDRESSES. Operator_APP_SMTP_*settings are not affected. - Self-hosted Git providers: Git provider API calls now verify TLS certificates. Clone and OAuth always did, so a provider behind a self-signed certificate never worked end to end.
- API keys: account keys work only from the console, a key can't grant scopes it doesn't hold, and stored keys can't be created with an API key or a JWT.
- New optional variable:
_APP_MEMORY_LIMIT(default512M) sets the PHP memory limit of every Appwrite process.
Contributors
Thank you to everyone who contributed to 2.6.2:
@ChiragAgg5k @HarshMN2345 @abnegate @ArnabChatterjee20k @Meldiron @atharvadeosthale @adityaoberai @loks0n @TorstenDittmann
Full Changelog: https://github.com/appwrite/appwrite/compare/2.6.1...2.6.2