> For the complete documentation index, see [llms.txt](https://mercure-technologies.gitbook.io/xprem/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mercure-technologies.gitbook.io/xprem/3-0-x/controle-plane-mode/local-development.md).

# Local development

{% hint style="info" %}
This page only concerns projects that use `expo-dev-client`. Without it, debug builds fetch their bundle straight from Metro and never request a manifest, so code signing is never exercised in development and you can skip this page.
{% endhint %}

In control plane mode, the key pair used to sign your updates belongs to the server. It is generated there, stored encrypted, and the only thing you ever download from the dashboard is `certificate.pem`, the public certificate your app embeds to verify that updates really come from your server. There is deliberately no way to download the private key, and as this page explains, you never need it on a development machine.

## When the problem appears

In development, an `expo-dev-client` build does not load its JavaScript blindly: the launcher first asks the dev server for a manifest, through expo-updates, using the configuration embedded in the build. Because `updates.codeSigningCertificate` is set in your Expo config, that embedded configuration includes your certificate, so the request demands a signed manifest. The Expo CLI must then sign it with your private key, which it only reads from the `--private-key-path` flag, and without it the command fails:

```shellscript
CommandError: Must specify --private-key-path argument to sign development manifest for requested code signing key
```

In control plane mode you cannot provide that key, because it never leaves the server. And you should not need to: development builds never verify production updates, so a certificate embedded in a development build serves no purpose. The fix is to stop embedding the code signing configuration in development builds.

## Disable code signing in development

Recent versions of `eoas init` write the condition into your `app.config.js` for you. Make sure yours guards the two code signing fields on `DISABLE_CODE_SIGNING`:

```js
export default ({ config }) => ({
  ...config,
  updates: {
    url: 'https://your-server.com/manifest',
    enabled: true,
    requestHeaders: {
      'expo-channel-name': process.env.RELEASE_CHANNEL,
      'expo-app-id': 'your-app-id',
    },
    codeSigningCertificate: process.env.DISABLE_CODE_SIGNING ? undefined : './certs/certificate.pem',
    codeSigningMetadata: process.env.DISABLE_CODE_SIGNING
      ? undefined
      : { keyid: 'main', alg: 'rsa-v1_5-sha256' },
  },
});
```

Signing stays on unless explicitly disabled: forgetting the variable breaks a development command noisily, while a production build can never silently lose signature verification.

{% hint style="warning" %}
Never set `DISABLE_CODE_SIGNING` when building a release. The variable acts at prebuild time: it decides whether the certificate gets embedded in the binary. A release build made with it, wherever you build it (locally with `--configuration Release`, in your CI, or on a build service), would apply updates from your server without verifying their signature, and nothing would warn you. Keep the variable in your dev scripts or a gitignored `.env.local` only, never in a committed `.env` or in your release build environment.
{% endhint %}

If you build with EAS Build, you can make that mistake impossible there. EAS sets `EAS_BUILD=true` on its workers, so with this fuse above the export a misconfigured profile fails the build loudly instead of shipping without signature verification:

```js
if (process.env.DISABLE_CODE_SIGNING && process.env.EAS_BUILD) {
  throw new Error('DISABLE_CODE_SIGNING must never be set on an EAS build');
}
```

## Run the development server

The variable matters on both sides. At prebuild time it decides whether the build embeds the certificate, and therefore whether the app will ask for signed manifests. When starting the dev server it decides whether the CLI would try to sign them. A build made with the variable must be served by a dev server started with it, so keep them consistent by putting it in the scripts everyone uses:

```json
{
  "scripts": {
    "start_dev": "DISABLE_CODE_SIGNING=1 expo start --dev-client",
    "ios_dev": "DISABLE_CODE_SIGNING=1 expo run:ios",
    "android_dev": "DISABLE_CODE_SIGNING=1 expo run:android"
  }
}
```

{% hint style="info" %}
If you already generated your native projects with code signing enabled, run `DISABLE_CODE_SIGNING=1 npx expo prebuild --clean` once so development builds stop embedding the certificate.
{% endhint %}

Production builds are unaffected: built without the variable, they embed the certificate from [Getting Started](/xprem/3-0-x/controle-plane-mode/getting-started.md) and verify the signature of every update they download.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mercure-technologies.gitbook.io/xprem/3-0-x/controle-plane-mode/local-development.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
