{"id":50680634,"url":"https://github.com/celestial-rose/updates","last_synced_at":"2026-06-08T18:32:47.128Z","repository":{"id":354131786,"uuid":"1145215218","full_name":"celestial-rose/updates","owner":"celestial-rose","description":"Serverless Expo Updates on Cloudflare","archived":false,"fork":false,"pushed_at":"2026-04-27T08:24:11.000Z","size":73,"stargazers_count":19,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T10:19:15.607Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/celestial-rose.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-29T15:09:26.000Z","updated_at":"2026-04-27T08:24:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/celestial-rose/updates","commit_stats":null,"previous_names":["celestial-rose/updates"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/celestial-rose/updates","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celestial-rose%2Fupdates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celestial-rose%2Fupdates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celestial-rose%2Fupdates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celestial-rose%2Fupdates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/celestial-rose","download_url":"https://codeload.github.com/celestial-rose/updates/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celestial-rose%2Fupdates/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34075956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-08T18:32:45.897Z","updated_at":"2026-06-08T18:32:47.118Z","avatar_url":"https://github.com/celestial-rose.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Celestial Updates: Serverless Expo Updates on Cloudflare\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Cloudflare-F38020?style=for-the-badge\u0026logo=Cloudflare\u0026logoColor=white\" alt=\"Cloudflare\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Hono-E36002?style=for-the-badge\u0026logo=hono\u0026logoColor=white\" alt=\"Hono\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Expo-000020?style=for-the-badge\u0026logo=expo\u0026logoColor=white\" alt=\"Expo\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge\u0026logo=typescript\u0026logoColor=white\" alt=\"TypeScript\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Bun-000000?style=for-the-badge\u0026logo=bun\u0026logoColor=white\" alt=\"Bun\" /\u003e\n\u003c/p\u003e\n\n**Deploy Expo Updates for pennies. Scale infinitely. Own your infrastructure.**\n\n**Celestial Updates** is a self-hosted, open-source custom Expo Updates server designed to run on the Cloudflare Developer Platform. Built with the ultra-fast **[Hono](https://hono.dev/)** framework, it leverages **Cloudflare Workers**, **R2 Storage**, and **KV Namespace** to provide a blazing fast, globally distributed, and incredibly cost-effective alternative.\n\nWe love [Expo Updates](https://docs.expo.dev/eas-update/introduction/). It allows you to:\n*   🚀 **Ship Instantly:** Fix bugs and push features without waiting for App Store review.\n*   🔄 **Sync Users:** Ensure everyone is on the latest version.\n*   🛠️ **Hotfix Live:** Patch issues in minutes, not days.\n\n**But as you scale, costs can add up.** \n**Celestial Updates solves this.** You pay only for what you use, often fitting within Cloudflare's generous free tier or costing significantly less at scale.\n\n## 🚀 Features\n\n*   **💸 Cost-Effective:** Zero egress fees with R2 and highly efficient Worker pricing. Stop paying a tax on your user growth.\n*   **🌍 Edge-Native:** Updates are served from Cloudflare's massive global network, ensuring low latency for users everywhere.\n*   **🔥 Built with Hono:** Uses the lightweight, ultrafast web framework for the Edges.\n*   **🔐 Secure:** Native support for [Expo Code Signing](https://docs.expo.dev/eas-update/code-signing/) to ensure update integrity.\n*   **⚡️ Standard Protocol:** Fully compliant with the open [Expo Updates Protocol](https://docs.expo.dev/technical-specs/expo-updates-1/). This open specification enables custom server implementations like Celestial Updates.\n*   **📦 Simple Deployment:** Deploy your entire update infrastructure with a single `wrangler deploy`.\n\n## 🛠 Prerequisites\n\n*   [Bun](https://bun.sh/) (recommended) or Node.js\n*   A Cloudflare account\n*   Wrangler CLI installed (`bun install -g wrangler`)\n*   An Expo project\n\n## 📦 Installation \u0026 Setup\n\n1.  **Clone the repository:**\n    ```bash\n    git clone https://github.com/celestial-rose/updates.git\n    cd updates\n    bun install\n    ```\n\n2.  **Configure Cloudflare Resources:**\n    You need to create an R2 bucket and a KV namespace.\n    ```bash\n    # Create KV Namespace\n    bunx wrangler kv namespace create EXPO_UPDATES_KV\n\n    # Create R2 Bucket (optional: specify jurisdiction)\n    bunx wrangler r2 bucket create expo-updates-assets --jurisdiction eu\n    ```\n\n3.  **Update `wrangler.jsonc`:**\n    Update the `kv_namespaces` section in `wrangler.jsonc` with the ID you received from the previous command. The R2 bucket name should match what you created (default is `expo-updates-assets`).\n\n    If you used a specific jurisdiction (like 'eu'), make sure to add it to the bucket configuration.\n\n    ```jsonc\n    // ...\n    \"r2_buckets\": [\n        {\n            \"bucket_name\": \"expo-updates-assets\",\n            \"binding\": \"EXPO_UPDATES_BUCKET\",\n            \"jurisdiction\": \"eu\" // Optional: if you created the bucket in a specific jurisdiction\n        }\n    ],\n    \"kv_namespaces\": [\n        {\n            \"binding\": \"EXPO_UPDATES_KV\",\n            \"id\": \"YOUR_NEW_KV_ID_HERE\"\n        }\n    ]\n    // ...\n    ```\n\n\n4.  **Generate Keys for Code Signing (Automated):**\n    The publish script will automatically detect if you have code signing enabled in your `app.json` but are missing keys. It will offer to generate them for you.\n    \n    *Manual Option:* If you prefer to generate them manually:\n    ```bash\n    mkdir -p code-signing\n    bunx expo-updates codesigning:generate --key-output-directory code-signing --certificate-output-directory code-signing --certificate-validity-duration-years 10 --certificate-common-name \"My App\"\n    ```\n\n5.  **Set Secrets (Automated):**\n    The publish script will also check if your private key is uploaded to Cloudflare. If not, it will offer to upload it for you.\n    \n    *Manual Option:*\n    ```bash\n    bunx wrangler secret put EXPO_UPDATES_PRIVATE_KEY_MY_APP --environment {ENVIRONMENT} \u003c code-signing/private-key.pem\n    ```\n\n6.  **Deploy:**\n    ```bash\n    bun run deploy\n    ```\n    Note the URL of your deployed worker (e.g., `https://my-expo-updates-server.my-subdomain.workers.dev`).\n\n## 📱 App Configuration\n\nIn your Expo project's `app.json` (or `app.config.js`), configure the `updates` section to point to your new Celestial Updates server.\n\n1.  **Set the URL and Code Signing (if enabled):**\n    ```json\n    {\n      \"expo\": {\n        \"slug\": \"my-app\",\n        \"runtimeVersion\": \"1\",\n        \"platforms\": [\"ios\", \"android\"],\n        \"updates\": {\n          \"url\": \"https://my-expo-updates-server.my-subdomain.workers.dev/my-app/api/manifest\",\n          \"enabled\": true,\n          \"checkAutomatically\": \"ON_LOAD\",\n          \"fallbackToCacheTimeout\": 0,\n          \"codeSigningCertificate\": \"./code-signing/certificate.pem\",\n          \"codeSigningMetadata\": {\n            \"keyid\": \"main\",\n            \"alg\": \"rsa-v1_5-sha256\"\n          }\n        },\n        // ...\n      }\n    }\n    ```\n\n    **Double Check:** Ensure the `url` includes your project slug (e.g., `/my-app/api/manifest`) and matches your worker's deployed URL. The `codeSigningCertificate` path must point to your generated certificate file.\n\n    *If you use code signing, ensure the `codeSigningCertificate` path points to a valid file. If not, the publish script can help you generate one.*\n\n2.  **Update .gitignore (Highly Recommended):**\n    Add `dist-updates` to your `.gitignore` file to prevent the build artifacts from polluting your repository.\n    ```\n    dist-updates\n    ```\n\n## Runtime versions and updates\n\nSince updates must be compatible with a build's native code, any time native code is updated, we're required to make a new build before publishing an update. Some developers only update native code when upgrading to a new Expo SDK, while others may upgrade native code between builds or at other intervals. Below is an explanation of different situations and configurations that may suit your project.\n\n### Setting `\"runtimeVersion\"`\n\nTo make managing the `\"runtimeVersion\"` property easier between builds and updates, we've created policies that derive the runtime version from another piece of information already present in your project. If these policies do not match the development flow of a project, there's also an option to set the `\"runtimeVersion\"` manually.\n\n#### Runtime version policies\n\nThe available policies are documented in the [`expo-updates` library documentation](https://docs.expo.dev/versions/latest/sdk/updates#automatic-configuration-using-runtime-version-policies).\n\n#### Custom runtime version\n\nYou must set a custom runtime version that meets the [runtime version formatting requirements](https://docs.expo.dev/versions/latest/config/app#runtimeversion):\n\n```json\n{\n  \"expo\": {\n    \"runtimeVersion\": \"1.0.0\"\n  }\n}\n```\n\n\n#### Platform-specific runtime version\n\nYou can also set runtime version per-platform, for example\n\n```json\n{\n  \"expo\": {\n    \"android\": {\n      \"runtimeVersion\": \"1.0.0\"\n    }\n  }\n}\n```\n\n\n\nWhen both a top level runtime and a platform specific runtime are set, the platform specific one takes precedence.\n\n### Avoiding Incompatible Updates\n\n**CRITICAL:** When you install or update native libraries (e.g., `expo-camera`, `react-native-reanimated`) or change native code, you **MUST** update the `runtimeVersion` in your `app.json`.\n\nIf you publish an update with the *same* `runtimeVersion` but *different* native code requirements, your users' apps will crash because the native code they have installed won't match what the JavaScript bundle expects.\n\n**Best Practices:**\n1.  **Bump `runtimeVersion`:** Change it manually (e.g., \"1.0.0\" -\u003e \"1.0.1\") or use a policy like \"appVersion\" or \"fingerprint\" (see [Runtime version policies](https://docs.expo.dev/versions/latest/sdk/updates#automatic-configuration-using-runtime-version-policies)).\n2.  **Test Before Release:** Always test your updates on a development build or a physical device with the specific native build you are targeting.\n\nFor a deeper dive into how runtime versions work, refer to the [Expo documentation on Runtime Versions](https://docs.expo.dev/eas-update/runtime-versions/).\n\n## 🚀 Publishing Updates\n\nWe provide a handy script to publish updates directly from your Expo project to your Celestial Updates infrastructure.\n\n1.  **Run the publish script:**\n    Run the publish script directly from the `celestial-updates` directory:\n    ```bash\n    # Publish to Production\n    bun run publish:prod\n    \n    # Publish to Staging\n    bun run publish:staging\n    ```\n    \n    **First Run \u0026 Configuration:**\n    *   On the first run, the script will ask for the **absolute path** to your Expo project root.\n    *   It will then ask if you want to save this path to a `.env` file in the `celestial-updates` directory.\n    *   **Multiple Projects:** You can add multiple paths to the `.env` file under `EXPO_PROJECT_PATHS` (comma-separated).\n        ```env\n        EXPO_PROJECT_PATHS=/path/to/project-a,/path/to/project-b\n        ```\n        If multiple paths exist, the script will ask you to select which project to publish.\n\n    *Or with a remote flag if you want to publish directly to Cloudflare:*\n    ```bash\n    bun run publish:prod --remote\n    ```\n\n    **What the script does:**\n    *   Reads your `app.json` to get the `runtimeVersion`.\n    *   Runs `expo export` to bundle your JavaScript and assets.\n    *   Uploads assets and the bundle to your **R2 Bucket**.\n    *   Updates the **KV Namespace** with the latest update metadata pointing to the new assets.\n\n## 🔐 Code Signing\n\nFor detailed information on Code Signing, strictly follow the [official Expo documentation](https://docs.expo.dev/eas-update/code-signing/).\n\nCelestial Updates expects the `expo-expect-signature` header and will sign the response using the `EXPO_UPDATES_PRIVATE_KEY` secret you set earlier.\n\n## 🧪 Running Tests\n\nWe use `vitest` with `@cloudflare/vitest-pool-workers` to test the worker logic in a simulation of the Cloudflare environment.\n\n**Run Tests:**\n```bash\nbun run test\n```\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see our contributing guidelines below to get started.\n\n### Contributing Guidelines\n\n1.  **Fork the Repository:** Start by forking this repository to your own GitHub account.\n2.  **Clone Locally:** Clone your fork to your local machine.\n3.  **Create a Branch:** Create a new branch for your feature or bug fix.\n    ```bash\n    git checkout -b feature/amazing-new-feature\n    ```\n4.  **Make Changes:** Implement your changes. Ensure you follow the code style (Prettier).\n5.  **Run Tests:** Run the test suite to ensure no regressions.\n    ```bash\n    bun run test\n    ```\n6.  **Commit:** Commit your changes with a descriptive message.\n7.  **Push:** Push your branch to your fork.\n8.  **Open a Pull Request:** Open a PR against the `main` branch of this repository.\n\n### Pull Request Rules\n\n*   **Descriptive Title:** Use a clear and concise title for your PR.\n*   **Description:** Provide a detailed description of what your PR does.\n*   **Tests:** Include tests for any new features or bug fixes.\n*   **Linting:** Ensure your code passes linting checks.\n*   **One Feature per PR:** Keep your PRs focused on a single task.\n\n## 🗺️ Roadmap \u0026 TODO\n\n### Sentry Source Map Uploads (Manual)\n\n\u003e **⚠️ Warning:** Sentry integration is currently a **Work In Progress (WIP)** and considered **Alpha**. It may not be ready for production use. Proceed with caution.\n\n**Disclaimer:** Since Celestial Updates uses `npx expo export` instead of `eas update`, source map uploading for Sentry needs to be handled manually or integrated into your CI/CD workflow.\n\n[Sentry Source Map Uploads for Expo](https://docs.sentry.io/platforms/react-native/sourcemaps/uploading/expo/#upload-source-maps)\n\nThe following documentation is taken from the link above:\n\n**Manual Upload for Updates:**\nTo upload source maps for updates generated via `npx expo export`, set up the Sentry Expo Plugin, the Sentry Metro plugin and execute the `sentry-expo-upload-sourcemaps` command.\n\n1.  **Create Update (which generates source maps):**\n    ```bash\n    bunx expo export --dump-sourcemap\n    # Then run the celestial-updates publish script to upload assets to R2/KV\n    ```\n\n2.  **Upload Source Maps:**\n    To upload source maps for all platforms use the following command:\n    ```bash\n    SENTRY_AUTH_TOKEN=sntrys_YOUR_TOKEN_HERE \\\n    bunx sentry-expo-upload-sourcemaps dist\n    ```\n\n    To upload source maps without expo plugin (bare workflow):\n    ```bash\n    SENTRY_AUTH_TOKEN=sntrys_YOUR_TOKEN_HERE \\\n    SENTRY_PROJECT=example-project \\\n    SENTRY_ORG=example-org \\\n    SENTRY_URL=https://sentry.io/ \\\n    bunx sentry-expo-upload-sourcemaps dist\n    ```\n\n**Notes:**\n*   `dist` is the default output directory of `bunx expo export`. Ensure this matches your export path.\n*   Sentry Org, Sentry Project, and Sentry Url will be loaded from the expo plugin config (`app.json` / `app.config.js`) if the environment variable isn't set.\n*   Uploaded source maps have no associated releases by default with this method. This is expected as updates can apply to multiple releases.\n\n## ⚠️ Disclaimer\n\nThis software is provided \"as is\", without warranty of any kind. The authors and contributors are not responsible for any misuse, errors, data loss, or system instability resulting from the use of this software. It is your responsibility to ensure the reliability and security of your deployment. Use at your own risk.\n\n## 📄 License\n\nMIT\n\n---\n\n\u003cp align=\"center\"\u003e\n  Made with 🧡 by Celestial Rose - Powered by Cloudflare\n\u003c/p\u003e/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcelestial-rose%2Fupdates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcelestial-rose%2Fupdates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcelestial-rose%2Fupdates/lists"}