GitHub Actions Deployment
EdgeChat's primary deployment method is GitHub Actions with Cloudflare Workers.
The workflow is located at .github/workflows/deploy-worker.yml and can complete the deployment automatically whenever code is pushed.
Required Configuration
| Setting | Purpose |
|---|---|
CLOUDFLARE_API_TOKEN | Allows Actions to call the Cloudflare API to create, inspect, and deploy resources. |
CLOUDFLARE_ACCOUNT_ID | Selects the target Cloudflare account. |
CFCHAT_ADMIN_USERNAME | Sets the administrator username created during the first deployment. |
CFCHAT_ADMIN_PASSWORD | Sets the administrator password created during the first deployment. |
Optional Configuration
| Setting | Purpose |
|---|---|
CFCHAT_ADMIN_DISPLAY_NAME | Sets the administrator display name. The username is used when this value is omitted. |
EDGECHAT_ENCRYPTION_KEYRING | Provides a complete server-side encryption keyring. One is generated automatically on the first deployment when omitted. |
Triggers
- Push to
masterormain. - Trigger
workflow_dispatchmanually from the GitHub Actions page.
Workflow Steps
- Check out the source code.
- Install dependencies.
- Build the frontend.
- Inspect or create Cloudflare resources.
- Generate the CI-specific
wrangler.ci.toml. - Initialize the database when D1 is created for the first time.
- Inspect and apply pending D1 migrations.
- Optionally create the administrator account.
- Inspect and prepare the Worker encryption Secret.
- Deploy the Worker.
R2 Attachment Support
The resource inspection step calls Cloudflare's official GET /accounts/{ACCOUNT_ID}/r2/buckets?per_page=1 endpoint to determine whether R2 is enabled for the target account.
- R2 is enabled: the workflow reuses or creates the
cfchat-filesbucket and creates theFILESbinding as usual. - Cloudflare returns error code
10042: the Worker is still deployed, but theFILESbinding is removed from the CI configuration. - The Token lacks permission, the account ID is incorrect, or another Cloudflare API error occurs: deployment stops so a configuration problem is not mistaken for an account without R2.
Without R2, sign-in, text chat, and administration still work. Users who upload attachments see a message explaining that the current deployment has no R2 binding. Attachment downloads and Telegram file synchronization are also unavailable. After enabling R2, rerun Deploy Worker; the workflow idempotently creates or reuses the bucket and restores attachment support automatically.
Server-side Encryption Keys
By default, each key is stored in an independent, versioned Worker Secret. A separate active key ID indicates which version encrypts newly written data. Existing EDGECHAT_ENCRYPTION_KEYRING JSON keyrings remain supported. The manual format is:
{"activeKeyId":"v1","keys":{"v1":"BASE64_ENCODED_32_BYTE_KEY"}}- If the target Worker has no encryption Secret, Actions generates a random 32-byte AES key.
- Normal pushes and ordinary manual deployments preserve existing Secrets without regenerating, overwriting, or rotating them.
- New messages and attachments use the active key after deployment.
- Telegram Bot Tokens and Webhook Secrets entered by administrators are encrypted with the same keyring before being stored in D1.
- Historical D1 messages and R2 attachments are not bulk-migrated; reads remain compatible with both plaintext and ciphertext.
To specify the initial deployment key manually, add a GitHub Repository Secret with the same name before deploying.
To rotate an existing key automatically and incrementally:
- Run
Deploy Workermanually. - Enable
rotate_encryption_key. - The workflow adds a versioned key Secret and changes the active key ID.
- All older versioned Secrets and the older JSON keyring remain unchanged.
apply_encryption_keyring is a fallback manual override. When using it, the Repository Secret must contain the complete JSON keyring. Retain every old key ID still referenced by historical messages, attachments, or encrypted Telegram Bot Tokens, then add the new key and update activeKeyId. Deleting an old key makes the corresponding historical ciphertext unreadable. The two rotation options cannot be enabled during the same run.
Cloudflare does not allow Worker Secrets to be read back. Automatic rotation works around this restriction by adding new versions without reading or overwriting older ones. If you need full custody and backups of every key, provide a JSON keyring manually before the first deployment.
This is server-side encryption at rest, not end-to-end encryption. The Worker decrypts content after session authorization succeeds, so the deployer and Worker runtime remain inside the trust boundary. The admin panel no longer provides message-content search or full-conversation inspection.
Automatic D1 Migrations
Before publishing the Worker, every deployment inspects the actual tables, columns, indexes, and triggers in the target D1 database. The edgechat_schema_migrations table records applied migrations and their file checksums.
- A new D1 database first runs the complete
worker/schema.sql, then records the current migration baseline. - An existing D1 database runs only the
worker/migrations/*.sqlfiles whose structures are not already present. - Databases previously deployed with the old unread-badge implementation first merge
channel_readscursors into the currentmessage_reads, then remove legacy foreign keys that would block rebuilding the messages table. - The Telegram Bridge migration independently creates configuration and mapping tables and adds external-sender, file-source, and deduplication fields to messages.
- Applied migrations are not run again, preventing duplicate columns and accidental data replacement.
- If a database contains only part of a migration, the workflow stops and reports the missing structure instead of allowing new code to connect to an outdated database and return repeated 500 errors.
- Applied migration files must never be rewritten. Database changes require a new, later migration file and an updated migration manifest.
After a fork pulls upstream changes, Actions updates both the database structure and the Worker as long as the Cloudflare Secrets point to the correct account. There is no need to sign in to D1 and execute SQL manually.
Update Checks in the Admin Panel
Production builds record the GitHub repository, branch, and commit used for the deployment. When an administrator opens "Site Settings," the browser calls the GitHub Compare API directly to compare the remote branch. The administrator can also click "Check for updates" to run the comparison again. This happens entirely in the frontend, creates no scheduled job, and requires no additional Cloudflare Secret.
For accurate results:
- The source repository must remain public so the browser can read comparison data anonymously.
- Actions deployments automatically use
GITHUB_REPOSITORY,GITHUB_REF_NAME, andGITHUB_SHA. - Manual deployments should build from a clean commit that has already been pushed.
- When there are uncommitted changes, unpushed commits, or temporary GitHub API rate limits, the admin panel reports that an accurate check is unavailable instead of falsely reporting an update.
Standalone Demo Workflow
The frontend-only demo uses .github/workflows/deploy-demo.yml and can only be triggered manually. It reads the separate DEMO_CLOUDFLARE_ACCOUNT_ID and DEMO_CLOUDFLARE_API_TOKEN, builds frontend/demo-dist, and deploys edgechat-demo.
This workflow does not inspect production resources, initialize or migrate D1, create an administrator, or prepare encryption Secrets. It also does not use the production Cloudflare Secrets. See Frontend-only Demo for the complete guide.
Android CI and Releases
.github/workflows/android-ci.yml continues to verify the temporarily deprecated native Compose client. It runs Gradle Wrapper validation, unit tests, Lint, and a Debug APK build when android/** or the API v1 contract changes.
.github/workflows/capacitor-android-ci.yml builds the primary Web UI Android client with Node.js 24, Java 21, and Android SDK 36. It builds and syncs the bundled frontend, runs application-module unit tests and Lint, builds the Debug and instrumentation APKs, and uploads the edgechat-capacitor-debug artifact. Its package name is com.aozorae.edgechat.web, so it can still be installed alongside the native client.
.github/workflows/android-release.yml runs from an android-v* tag or a manual trigger. It builds and syncs the Vue frontend, creates signed Capacitor APK/AAB artifacts and SHA256SUMS.txt, then publishes a GitHub Release. The native Compose client no longer enters this release workflow.
Configure these Repository Secrets before publishing:
ANDROID_KEYSTORE_BASE64ANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORD
Back up the signing keystore offline. The workflow restores it from Secrets only for the duration of the job. Android-only and Android documentation changes do not trigger the production Worker deployment; backend, frontend, test, migration, and deployment-script changes still run the complete Cloudflare pipeline.
Recommended Permissions
CLOUDFLARE_API_TOKEN requires at least:
Workers Scripts:EditWorkers Routes/Cron Triggers:EditD1:EditWorkers KV Storage:EditR2:Edit
Keep R2:Edit even if the account has not enabled R2. The workflow needs this permission to distinguish reliably between "R2 is not enabled" and "the Token cannot access R2." The latter is treated as a configuration error and stops deployment.
Recommendations
- Confirm that all Repository Secrets are present before the first deployment.
- If resources already exist, verify their IDs and namespace configuration first.
- Manage the administrator account through Secrets instead of editing the database manually.
