Milady Electrobun Platform
Cross-platform specifics for Electrobun apps — platform support matrix, Linux/Windows/macOS behavioral differences, CEF requirements by platform, events API, security patterns, CI release matrix, artifact naming, and common pitfalls. Use when targeting multiple platforms, handling platform-specific bugs, setting up CI, or understanding webview differences.
git clone https://github.com/milady-ai/milady
T=$(mktemp -d) && git clone --depth=1 https://github.com/milady-ai/milady "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/plugins/electrobun-dev/skills/electrobun-platform" ~/.claude/skills/milady-ai-milady-electrobun-platform && rm -rf "$T"
.claude/plugins/electrobun-dev/skills/electrobun-platform/SKILL.mdElectrobun Cross-Platform Reference
Platform Support Matrix
| Platform | OS value | Arch | Status | Default Webview |
|---|---|---|---|---|
| macOS ARM64 | | | Stable | WKWebView |
| macOS Intel | | | Stable | WKWebView |
| Windows x64 | | | Stable | WebView2 (Edge) |
| Windows ARM64 | | | Via emulation | Runs x64 binary |
| Linux x64 | | | Stable | GTKWebKit |
| Linux ARM64 | | | Stable | GTKWebKit |
Build each platform on its native OS — no cross-compilation.
Linux — Always Bundle CEF
GTKWebKit has severe limitations:
- No webview layering (no overlapping webviews)
- No masking
- No
compositing<electrobun-webview> - Renderer mixing not supported — all webviews must use same renderer
For anything beyond a single-webview app on Linux:
// electrobun.config.ts build: { linux: { bundleCEF: true, defaultRenderer: 'cef' }, }
Windows — Console Output
Production Windows builds use the GUI subsystem — no console for end users.
set ELECTROBUN_CONSOLE=1 .\MyApp.exe
Has no effect on macOS or Linux. Dev builds auto-attach a console.
Webview Hidden/Passthrough Behavior
// macOS: hidden and passthrough are INDEPENDENT webviewSetHidden(webviewId, true); // hidden, still intercepts clicks webviewSetPassthrough(webviewId, true); // separate call for click-through // Windows & Linux: hidden = hidden + passthrough automatically webviewSetHidden(webviewId, true); // hidden AND clicks pass through // webviewSetPassthrough() is a no-op on Windows/Linux
<electrobun-webview>
HTML Tag (OOPIF)
<electrobun-webview>Process-isolated "super iframes" — positioned by DOM, rendered natively. Requires CEF on Linux.
<electrobun-webview src="https://example.com" width="100%" height="400px" renderer="cef"> </electrobun-webview>
Access from bun:
BrowserView.getAll() includes OOPIFs.
CEF Version Override
build: { cefVersion: '144.0.11+ge135be2+chromium-144.0.7559.97', }
Same major = safe. Adjacent major = usually fine. Distant majors = higher risk.
Chromium Flags (CEF only)
build: { mac: { bundleCEF: true, chromiumFlags: { 'show-paint-rects': true, 'user-agent': 'MyApp/1.0', 'disable-web-security': true, }, }, }
Per-Window Renderer Override
// Override defaultRenderer per window const win = new BrowserWindow({ url: '...', renderer: 'native' }); const cefWin = new BrowserWindow({ url: '...', renderer: 'cef' });
Mixing allowed on macOS and Windows. Not allowed on Linux.
Events API
import Electrobun from 'electrobun/bun'; // App lifecycle Electrobun.events.on('before-quit', (e) => { if (hasUnsavedChanges()) e.response = { allow: false }; // cancel quit }); Electrobun.events.on('open-url', (e) => { const url = new URL(e.data.url); // macOS URL scheme deep links }); // Window events (global — all windows) Electrobun.events.on('close', (e) => { /* e.data.id */ }); Electrobun.events.on('resize', (e) => { /* e.data.id, width, height */ }); Electrobun.events.on('move', (e) => { /* e.data.id, x, y */ }); Electrobun.events.on('blur', (e) => { /* e.data.id */ }); Electrobun.events.on('focus', (e) => { /* e.data.id */ }); // Menu / tray Electrobun.events.on('application-menu-clicked', (e) => { /* e.data.action */ }); Electrobun.events.on('context-menu-clicked', (e) => { /* e.data.action, data */ }); Electrobun.events.on('tray-menu-clicked', (e) => { /* e.data.action */ });
Quit trigger sources: dock icon, Cmd+Q, Ctrl+C, SIGTERM, window close,
Utils.quit(), process.exit() — all route through before-quit.
Linux caveat: system-initiated quit paths (window-manager close, taskbar) may bypass
before-quit. Utils.quit() and process.exit() are reliable.
URL Schemes / Deep Linking
macOS only. App must be in
/Applications or ~/Applications.
// electrobun.config.ts app: { urlSchemes: ['myapp', 'myapp-dev'] } // src/bun/index.ts Electrobun.events.on('open-url', (e) => { const url = new URL(e.data.url); if (url.pathname.startsWith('/auth')) { /* handle */ } });
Windows/Linux URL schemes not yet supported.
Security Checklist
on any BrowserView loading untrusted URLs — disables RPC, events still worksandbox: true
to allowlist permitted navigation targets; last match winssetNavigationRules- Never expose
APIs to sandboxed views — route through RPC with validationelectrobun/bun - Use separate
values per account for session isolationpartition - Prefer
overviews://
for local assetsfile://
Code Signing (macOS)
One-Time Setup
- Install full Xcode from App Store (not CLI tools alone — avoids expired cert issues)
- Xcode → Settings → Accounts → add Apple ID → Manage Certificates →
→ Developer ID Application+ - Apple Developer Portal: Identifiers → register bundle ID
- account.apple.com: App Specific Passwords → create one
Required Env Vars
export ELECTROBUN_DEVELOPER_ID="My Corp Inc. (BGU899NB8T)" export ELECTROBUN_TEAMID="BGU899NB8T" export ELECTROBUN_APPLEID="you@example.com" export ELECTROBUN_APPLEIDPASS="xxxx-xxxx-xxxx-xxxx"
Unsigned apps show "damaged and can't be opened" — users fix with:
xattr -cr /Applications/YourApp.app
Entitlements
build: { mac: { entitlements: { 'com.apple.security.device.camera': 'Needed for video', 'com.apple.security.device.microphone': 'Needed for audio', } } }
Application Icons
build: { mac: { icons: 'icon.iconset' }, // icon.iconset/ must contain icon_{16,32,64,128,256,512}x{same}[@2x].png win: { icon: 'icon.iconset/icon_256x256.png' }, // .ico or .png, auto-converted linux: { icon: 'icon.iconset/icon_256x256.png' }, // 256x256+ .png }
Reuse PNGs from the macOS iconset for Windows/Linux — no separate files needed.
Artifact Naming Convention
Artifacts use
{channel}-{os}-{arch}- prefix. Stable omits the channel. App name spaces stripped.
canary-macos-arm64-MyApp-canary.dmg canary-macos-arm64-MyApp-canary.app.tar.zst canary-macos-arm64-{prevHash}.patch canary-win-x64-MyApp-Setup-canary.zip canary-win-x64-MyApp-canary.tar.zst canary-linux-x64-MyAppSetup-canary.tar.gz stable-macos-arm64-MyApp.dmg ← no "stable" in name on stable channel
Keep old patches on your static host — users >1 version behind auto-fall back to full
.tar.zst.
Release Hosting
Static file host — no server required: S3, Cloudflare R2, GCS, GitHub Releases.
release: { baseUrl: 'https://storage.googleapis.com/mybucket/myapp/', // GitHub Releases: // baseUrl: 'https://github.com/ORG/REPO/releases/latest/download', }
GitHub Releases limitation:
/releases/latest/download skips prereleases. Stable works; canary auto-updates will NOT — use R2/S3 for canary channel.
CI Build Matrix (GitHub Actions)
Each platform must build on its own native runner:
jobs: build-macos-arm64: runs-on: macos-14 # Apple Silicon build-macos-x64: runs-on: macos-13 # Intel build-windows: runs-on: windows-latest build-linux: runs-on: ubuntu-latest # Each job: # uses: actions/checkout@v4 # uses: oven-sh/setup-bun@v2 # run: bun install # run: electrobun build --env=stable # uses: softprops/action-gh-release@v1 # with: files: artifacts/*
Tag naming convention: tags containing
-canary → canary build; all others → stable.
ASAR Packaging
build: { useAsar: true, asarUnpack: ['*.node', '*.dll', '*.dylib', '*.so', 'data/large/**/*'], }
Packs app resources into
app.asar. Unpacked files → app.asar.unpacked/. Benefits: faster I/O, code obfuscation via randomized temp paths.
Common Pitfalls
| Issue | Fix |
|---|---|
| Linux webview layering broken | + |
| Canary auto-update not working | GitHub skips prereleases — use R2/S3 |
| "damaged and can't be opened" macOS | Enable , or for testing |
| Code signing cert errors | Install full Xcode from App Store, not just CLI tools |
| RPC type mismatch at runtime | Confirm shared type file is imported by both sides |
| Sandbox webview + RPC silent failure | disables RPC — use events or remove sandbox |
| Windows no console output | Set env var |
| URL schemes not registering | App must be in , macOS only |
| Cross-compilation attempt | Each platform must build on its own native OS |
| Linux renderer mixing | All webviews must use the same renderer on Linux |
| Old patches missing | Keep all patch files on static host for sequential upgrades |
not firing on Linux | System-initiated quits may bypass it; is reliable |
| Notarize + codesign disabled for speed | Turn off during rapid debug cycles, re-enable for release |