Skip to content

Instantly share code, notes, and snippets.

@robinebers
Last active June 5, 2026 10:37
Show Gist options
  • Select an option

  • Save robinebers/53207f3f594978af2a6760b82dfce7d0 to your computer and use it in GitHub Desktop.

Select an option

Save robinebers/53207f3f594978af2a6760b82dfce7d0 to your computer and use it in GitHub Desktop.
Here are instructions to patch the local Claude.app on macOS to allow additional hosts other than localhost.

Claude Patched App (macOS)

Patch local Claude.app so Preview can follow HTTPS auth redirects (e.g. WorkOS), not only localhost.

Output: /Applications/Claude (Patched).app copied from /Applications/Claude.app.

Quit stock Claude before launching the patched copy — both use com.anthropic.claudefordesktop.


Quick apply

/Users/rebers/Dev/sandbox/patch-claude-app.sh

Requires: node, npm, ditto, PlistBuddy, codesign, shasum.


The patch (Claude 1.11187.1+)

File inside asar:

.vite/build/index.js

Method: isAllowedUrl — localhost check uses minified R9() (was V6i + port in older builds).

Original (68 bytes):

return R9(t.hostname)&&(t.protocol==="http:"||t.protocol==="https:")

Patched (68 bytes):

return t.protocol==="https:"||R9(t.hostname)&&(t.protocol==="http:")

Effect:

  • Any https:// URL is allowed (external IdP / WorkOS redirects).
  • http:// remains limited to loopback via R9 (localhost, 127.0.0.1, ::1, etc.).

Confirm exactly one occurrence of the original string in raw app.asar before patching (minification changes between releases).

Older builds (reference)

// Original
return V6i(t.hostname)&&t.port===`${this.port}`
// Patched
return t.protocol==="https:"||V6i(t.hostname)  

How to patch (correct workflow)

1. Copy the app

rm -rf '/Applications/Claude (Patched).app'
ditto '/Applications/Claude.app' '/Applications/Claude (Patched).app'

2. Edit and repack app.asar

Do not use plain asar extract + asar pack without Electron’s unpack rules. That packs .node binaries into the archive (~26 MB → ~68 MB) and the app crashes on launch.

Do use @electron/asar with unpack + integrity:

import crypto from 'node:crypto';
import { createPackageWithOptions, extractAll, getRawHeader } from '@electron/asar';

const ORIG = 'return R9(t.hostname)&&(t.protocol==="http:"||t.protocol==="https:")';
const PATCH = 'return t.protocol==="https:"||R9(t.hostname)&&(t.protocol==="http:")';

await extractAll('app.asar', 'extracted/');
// patch extracted/.vite/build/index.js (replace ORIG → PATCH once)

await createPackageWithOptions('extracted/', 'app.asar', {
  unpack: '{**/*.node,**/*.dll,**/*.so,**/*.dylib}',
  unpackDir: 'app.asar.unpacked',
  integrity: true,
  compression: 'maximum',
});

const { headerString } = getRawHeader('app.asar');
const hash = crypto.createHash('sha256').update(headerString).digest('hex');
// use hash in Info.plist (next step)

Keep native modules in Contents/Resources/app.asar.unpacked/ (copied via ditto from stock). Patched app.asar should stay ~25–26 MB, not ~68 MB.

3. In-place byte patch — not sufficient alone

The 68-byte string can be found and replaced in the raw app.asar without changing file size, but per-file integrity blocks in the asar header still describe the old index.js. Electron validates those at runtime. Always repack with integrity: true.

4. Update ElectronAsarIntegrity in Info.plist

Path:

/Applications/Claude (Patched).app/Contents/Info.plist

The hash is SHA256 of the asar JSON header (getRawHeader().headerString), not SHA256 of the whole app.asar file.

# WRONG — causes immediate crash
shasum -a 256 app.asar

# RIGHT — compute in Node (see getRawHeader above), then:
/usr/libexec/PlistBuddy -c "Set :ElectronAsarIntegrity:Resources/app.asar:hash <HEADER_HASH>" \
  '/Applications/Claude (Patched).app/Contents/Info.plist'

Stock app: plist hash matches getRawHeader on unmodified app.asar.

5. Re-sign ad-hoc (no Anthropic team IDs)

Remove team-bound entitlements; keep Electron runtime entitlements (JIT, devices, etc.):

  • remove com.apple.application-identifier
  • remove com.apple.developer.team-identifier
  • keep com.apple.security.cs.allow-jit and other device entitlements

Sign inside-out: nested .app / .framework helpers → Contents/MacOS/Claude → bundle root.

xattr -cr '/Applications/Claude (Patched).app'
xattr -dr com.apple.quarantine '/Applications/Claude (Patched).app'
codesign --verify --deep --strict '/Applications/Claude (Patched).app'

Verify

# Size sanity (patched ≈ stock, not ~68M)
stat -f%z '/Applications/Claude.app/Contents/Resources/app.asar'
stat -f%z '/Applications/Claude (Patched).app/Contents/Resources/app.asar'

# Launch — should see main + Helper (GPU/Renderer/Network), no instant exit
pgrep -fl 'Claude \(Patched\)'

Expected JSON checks (script prints this):

{
  "patchedAsarBytes": "~25884252",
  "headerHashMatchesPlist": true,
  "hasOriginalPreviewGate": false,
  "hasPatchedPreviewGate": true
}

When it crashes (integrity failure)

Symptoms:

  • Crash ~200 ms after launch
  • EXC_BREAKPOINT / SIGTRAP on main thread
  • Stack: node::builtins::BuiltinLoader::CompileAndCallElectronMain
  • procPath: Claude (Patched).app

Common causes:

Mistake Result
shasum whole app.asar for plist Wrong hash → crash
Plain asar pack without unpack ~68 MB asar → crash
In-place patch without integrity regen Block hash mismatch → crash
Wrong/minified gate string JS error or wrong behavior

Lessons learned

  1. Gate symbol changes between Claude releases (V6i + port → R9 + protocol). Always search isAllowedUrl / hostname in fresh index.js.
  2. Byte-length-matched replacements avoid shifting asar offsets, but you still must regenerate integrity metadata.
  3. ElectronAsarIntegrity = header hash, from @electron/asar getRawHeader, not file hash.
  4. Repack must preserve app.asar.unpacked via the unpack glob; compression alone does not fix the 68 MB blow-up.
  5. Ad-hoc sign after edits; official team entitlements cannot be reused on a modified bundle.

Files in this repo

File Purpose
patch-claude-app.sh Full automate: copy → repack → plist → sign → verify
patch-claude-app.mjs Node repack + patch logic
claude-patch.md This document

After each Claude.app update from Anthropic, re-run patch-claude-app.sh and re-check the gate strings if the app fails verification or crashes at startup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment