Unlock Computer Use plugin in Codex.app for EU / region-locked accounts (in-place asar patch) OpenAI's Codex desktop app includes a Computer Use plugin binary that is pre-installed on all systems but hidden from users in regions like the EU through a server-side Statsig feature gate and a local configuration flag. This guide provides instructions to bypass both restrictions by editing the user's TOML config file and performing a binary patch on the Electron bundle to force the Statsig check to return true. The process takes approximately five minutes, requires intermediate technical skill, and includes automatic backups to minimize risk. Unlock Computer Use in Codex.app EU / region-locked accounts OpenAI's Codex desktop app ships with a Computer Use plugin computer-use@openai-bundled but hides it from users in regions where the feature isn't rolled out yet — including most of the EU. The plugin binary is already on your machine /Applications/Codex.app/Contents/Resources/plugins/openai-bundled/computer-use/ ; only the UI is gated. This guide flips two flags so the plugin appears in your plugin picker. Tested on: macOS 15 arm64 , Codex.app version 26.506.31421 May 2026 build . Difficulty: Intermediate. You'll edit a TOML config and binary-patch Codex's Electron bundle. Risk: Low. We back up everything we touch and never change file lengths. Time: ~5 minutes. --- Why two flags? The Codex client side gates computer-use behind two checks both in webview/assets/use-in-app-browser-use-availability- UMFu9j2.js inside app.asar : 1. Statsig feature gate 1506311413 — server-evaluated. Returns false for EU/non-rolled-out accounts. This is the region wall. 2. Local experimental feature flag computer use — read from ~/.codex/config.toml features section. Defaults off. We enable both: edit the config easy , and patch one 15-byte string inside app.asar so the Statsig call always returns truthy less easy, but mechanical . --- Step 1 — Add the local feature flag Open ~/.codex/config.toml and add computer use = true to your features block. If the block doesn't exist, create it. toml features computer use = true That's it for Step 1. Save the file. --- Step 2 — Add the plugin entry to your config Codex needs to know you actually want this plugin enabled. Append to ~/.codex/config.toml : toml plugins."computer-use@openai-bundled" enabled = true Note: Codex's startup-sync may strip this entry if the Statsig gate still rejects the plugin. That's why Step 3 is required before Step 2 will stick. --- Step 3 — Patch the Statsig gate inside app.asar This is the only invasive step. We do an in-place binary edit no repacking, no extracting . The patch: - Replaces s \ 1506311413\ → 0 15 bytes → 15 bytes, evaluates to true - Recomputes the SHA-256 of the patched file and overwrites the matching hash strings in the asar JSON header same length, since SHA-256 hex is always 64 chars - Recomputes the asar header SHA-256 and writes it to Info.plist ElectronAsarIntegrity - Ad-hoc re-signs the .app bundle so macOS hardened runtime accepts it 3a. Install the helper bash mkdir -p /tmp/codex-asar-tools && cd /tmp/codex-asar-tools npm init -y /dev/null npm install @electron/asar 3b. Save the patcher script Save this as /tmp/codex-asar-tools/patch.mjs : js import as asar from '@electron/asar'; import { readFileSync, writeFileSync, copyFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { execSync } from 'node:child process'; const APP = '/Applications/Codex.app'; const ASAR = ${APP}/Contents/Resources/app.asar ; const PLIST = ${APP}/Contents/Info.plist ; const REL = 'webview/assets/use-in-app-browser-use-availability- UMFu9j2.js'; const BEFORE = s \ 1506311413\ ; // 15 bytes const AFTER = 0 ; // 15 bytes, evaluates 0 = true const STAMP = Date.now ; // 0. Back up copyFileSync ASAR, ${ASAR}.bak.${STAMP} ; copyFileSync PLIST, ${PLIST}.bak.${STAMP} ; console.log backups: .bak.${STAMP} ; // 1. Parse asar layout from raw bytes NOT from getRawHeader's headerSize, which is off by 8 const fullBuf = readFileSync ASAR ; const innerPickleTotal = fullBuf.readUInt32LE 4 ; const stringLen = fullBuf.readUInt32LE 12 ; const headerJsonStart = 16; const headerJsonEnd = headerJsonStart + stringLen; const dataStart = 8 + innerPickleTotal; // <-- where file content blob begins const header = JSON.parse fullBuf.subarray headerJsonStart, headerJsonEnd .toString 'utf8' ; function get node, parts { for const p of parts { if node.files || node.files p throw new Error 'missing entry: ' + parts.join '/' ; node = node.files p ; } return node; } const entry = get header, REL.split '/' ; const fileSize = Number entry.size ; const fileAbsOffset = dataStart + Number entry.offset ; // 2. Read the file, swap the pattern, verify size const orig = fullBuf.subarray fileAbsOffset, fileAbsOffset + fileSize ; const text = orig.toString 'utf8' ; if text.includes BEFORE throw new Error 'pattern not found — Codex version may have changed' ; if BEFORE.length == AFTER.length throw new Error 'replacement length mismatch' ; const newBuf = Buffer.from text.replace BEFORE, AFTER , 'utf8' ; // 3. Sanity: pre-patch hash must match header const preHash = createHash 'sha256' .update orig .digest 'hex' ; if preHash == entry.integrity.hash throw new Error 'pre-patch hash mismatch — file may already be patched or asar is corrupt' ; // 4. Compute new file integrity single block since file < 4 MiB const newOverall = createHash 'sha256' .update newBuf .digest 'hex' ; const newBlocks = ; for let off = 0; off < newBuf.length; off += entry.integrity.blockSize { newBlocks.push createHash 'sha256' .update newBuf.subarray off, off + entry.integrity.blockSize .digest 'hex' ; } // 5. In-place edit: replace hash strings inside the JSON header function replaceInRange big, lo, hi , oldStr, newStr { const oldB = Buffer.from oldStr, 'utf8' ; const newB = Buffer.from newStr, 'utf8' ; if oldB.length == newB.length throw new Error 'hash length changed' ; let pos = lo, count = 0; while pos < hi { const found = big.indexOf oldB, pos ; if found < 0 || found = hi break; newB.copy big, found ; count++; pos = found + 1; } return count; } replaceInRange fullBuf, headerJsonStart, headerJsonEnd , entry.integrity.hash, newOverall ; for let i = 0; i < entry.integrity.blocks.length; i++ { replaceInRange fullBuf, headerJsonStart, headerJsonEnd , entry.integrity.blocks i , newBlocks i ; } // 6. Patch file content at the correct offset newBuf.copy fullBuf, fileAbsOffset ; writeFileSync ASAR, fullBuf ; // 7. Recompute asar header hash for Info.plist const newHeaderJson = readFileSync ASAR .subarray headerJsonStart, headerJsonEnd .toString 'utf8' ; const newHeaderHash = createHash 'sha256' .update newHeaderJson .digest 'hex' ; console.log 'new asar header hash:', newHeaderHash ; // 8. Update Info.plist execSync /usr/libexec/PlistBuddy -c "Set :ElectronAsarIntegrity:Resources/app.asar:hash ${newHeaderHash}" "${PLIST}" ; // 9. Ad-hoc re-sign Info.plist change invalidates Apple's signature execSync codesign --force --deep --sign - "${APP}" , { stdio: 'inherit' } ; console.log 'Done. Restart Codex.app.' ; 3c. Run it bash cd /tmp/codex-asar-tools && node patch.mjs Expected output last lines : new asar header hash: e5bf56229175a64fea86dd21519c19b7b54a0014ac752016975c193bd9737385 /Applications/Codex.app: replacing existing signature Done. Restart Codex.app. 3d. Restart Codex bash pkill -f "/Applications/Codex.app" open -a Codex Open Settings → Plugins. Computer Use now appears next to Browser Use. --- How to verify it worked bash 1. Codex.app process is alive no FATAL on launch ps aux | grep "/Applications/Codex.app/Contents/MacOS/Codex" | grep -v grep 2. Check the patched bytes are present in app.asar grep -ao ' 0 ' /Applications/Codex.app/Contents/Resources/app.asar | head -1 3. Confirm Info.plist integrity hash matches the new header /usr/libexec/PlistBuddy -c "Print :ElectronAsarIntegrity" /Applications/Codex.app/Contents/Info.plist If Codex starts and the plugin tile is visible, you're done. --- Rollback The patcher leaves backups at /Applications/Codex.app/Contents/Resources/app.asar.bak.