Claude Desktop — Bundle Architecture
Documentation: Claude Desktop — Bundle Architecture
Claude Desktop — Bundle Architecture
Claude Desktop Electron app structure, extracted from ASAR (/Applications/Claude.app/Contents/Resources/app.asar).
Build System
- Version: 1.14271.0
- Build tool: Electron Forge + Vite 6.4.2
- Renderer: React 18.3.1 + Tailwind CSS 3.4.19
- Minifier: Terser (local variables mangled, properties preserved)
- Package:
@ant/desktop
ASAR Structure
app.asar/
├── package.json # @ant/desktop, deps
├── harness-bootstrap.js # Bezetacil runtime (added via repack)
├── .vite/
│ ├── build/
│ │ ├── index.js # 14 MB — Electron Main Process (ALL logic)
│ │ ├── index.pre.js # 811 KB — Preload script
│ │ ├── mainWindow.js # 165 KB
│ │ ├── mainView.js # 194 KB
│ │ └── ...
│ └── renderer/
│ ├── main_window/
│ │ ├── assets/
│ │ │ ├── main-Bh0l-09t.js # 291 KB — React shell (react, react-dom, react-intl)
│ │ │ └── MainWindowPage-1yenKknx.js # 13 KB — UI components
│ ├── about_window/ # About window bundle
│ ├── find_in_page/ # Find-in-page bundle
│ └── quick_window/ # Quick entry window
├── node_modules/
│ ├── @ant/claude-native/ # Native addons (.node, .dylib)
│ ├── ws/ # WebSocket library
│ └── node-pty/ # Terminal PTY
└── resources/
└── office365-mcp/ # Office 365 MCP server
1p vs 3p — The Real Difference
Not two separate bundles. Same code with compile-time switches:
| Mechanism | 3p (prod) | 1p (internal) |
|---|---|---|
buildType | "prod" | "dev" or "staging" |
isNestBuild | false | true |
dzA() (staging detect) | no-op stub () => {} | real function |
uF() (URL base) | hardcoded "https://claude.ai" | CLAUDE_AI_URL env var |
hardcodedMainGrowthBookFeatures() | return null | return V3i |
_vt() (voice module) | return Promise.resolve({}) | real module |
y4n (Echo MCP) | Object.freeze({}) empty | real module |
URL Override (Dev)
function uF() {
if (buildType === "dev")
return process.env.CLAUDE_AI_URL || config.claudeAiUrl || "https://claude.ai"
if (isDeveloperApprovedDevUrlOverrideEnabled && process.env.CLAUDE_AI_URL)
return process.env.CLAUDE_AI_URL
return "https://claude.ai"
}
isDeveloperApprovedDevUrlOverrideEnabled requires Ed25519 signature via CLAUDE_CDP_AUTH.
Remote Bundle (1p)
Loaded from assets-proxy.anthropic.com/claude-ai/v2/assets/v1/:
| File | Size | Content |
|---|---|---|
index-BW29VlNm.js | 7.2 MB | Complete React webapp (voice, UI, GrowthBook) |
c6a992d55-CvsZ6KID.css | 758 KB | Design system @ant-cds + Tailwind |
vendor-ant-cds-D6IJ1PR1.css | 204 B | CDS loader |
Extracted to ~/.bezetacil/workdir/claude-desktop/ion-dist/ via CDP.
Voice Components in Remote Bundle
buildVoiceWebSocket, getVoice, setVoice, selectedVoice,
voiceContentBlocks, voiceSessionId
SFX: /audio/voice/sfx/enter_voice_mode.mp3, exit_voice_mode.mp3, disconnected.mp3
AudioWorklet: audioSinkWorklet with 5.76 MB ring buffer
Persistent Workdir
~/.bezetacil/workdir/claude-desktop/
├── package.json # From original ASAR
├── harness-bootstrap.js # Bezetacil runtime
├── jsconfig.json # Config for LSP (tsserver)
├── .vite/ # ASAR bundles (main + renderer)
├── ion-dist/ # Remote bundles extracted from 1p
│ ├── index-BW29VlNm.js # 7.2 MB — webapp React
│ └── *.css # Design system
├── remote-scripts.json # Remote script URLs
├── 1p-boot-features.json # 1p desktopBootFeatures
├── 1p-features.json # 1p scope scan
└── feature-flags.csv # 226 analyzed features
Design System
- Framework: Tailwind CSS 3.4.19 with custom HSL tokens
- Components:
@ant-cds(Anthropic Design System) +@radix-ui - Icons:
@phosphor-icons/react - Fonts: AnthropicSans, AnthropicSerif (variables)
- Button variants:
primary,secondary,danger,ghost,flat,unstyled
ASAR Repack
Tools used: @electron/asar (Node.js API), Python for Info.plist
Integrity hash: SHA256 of headerString (JSON), stored in ElectronAsarIntegrity in Info.plist (9 copies).
References
- claude-desktop-feature-flags — Feature flag system
- claude-desktop-analysis-workflow — Analysis workflow
- bezetacil-toolset — ASAR pipeline and harness