Desktop app unblocking guide (Phase D + Phase 6 manual testing)
Corresponds to the items marked
[ ](blocked) and[~](written but not build-verified) in TODO-genspark-workspace-shell.md. What blocks these items is external resources — toolchain, credentials, a graphical environment, a second machine. Not missing code. This guide breaks every blocker into “why it’s blocked → prerequisites → steps → matching TODO acceptance criteria.”Suggested order: Gate A (local, free, half a day) → Gate E (update signing key, free) → Gate B (macOS signing, needs a paid account) → Gate C (Windows signing) → Gate D (Linux). Once A is done, you can use the app yourself and run every lifecycle check.
Gate A — install the Tauri toolchain and get it running locally (no signing)
Section titled “Gate A — install the Tauri toolchain and get it running locally (no signing)”Blocks: D0 🧪, D1 🧪, D2.1/D2.3/D2.4/D2.5/D2.6 🧪, D5 item 1, P6.3 manual test + screenshots. Why it’s blocked: this writing environment has no Tauri CLI, no system WebView dev dependencies, and no display. Your Mac has all three. Cost: free. Time: about 0.5–1 hour (including the first build).
A.1 Prerequisites (macOS)
Section titled “A.1 Prerequisites (macOS)”# Xcode Command Line Tools (if not installed)xcode-select --install
# Rust (if not installed)curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Tauri CLI v2cargo install tauri-cli --version "^2" --lockedcargo tauri --version # should print tauri-cli 2.xWindows additionally needs the WebView2 Runtime (built into Win11) + MSVC Build Tools; Linux needs
libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf.
A.2 Generate the app icon (one-time)
Section titled “A.2 Generate the app icon (one-time)”cd src-tauri# Prepare a square PNG ≥1024×1024 (🐾 on an amber background), e.g. save it to web/public/paw-1024.pngcargo tauri icon ../web/public/paw-1024.png# Produces icons/32x32.png, 128x128.png, 128x128@2x.png, icon.icns, icon.icoA.3 Stage the sidecar + dev mode
Section titled “A.3 Stage the sidecar + dev mode”# From the repo rootcargo build --release -p duduclaw-cli --bin duduclaw # produces target/release/duduclawscripts/desktop/stage-sidecar.sh # copies it to src-tauri/binaries/duduclaw-<triple>
cd src-tauricargo tauri dev # opens the dev window; beforeDevCommand starts Vite automaticallyA.4 Unsigned production build (verify locally)
Section titled “A.4 Unsigned production build (verify locally)”cd src-tauricargo tauri build# Output: src-tauri/target/release/bundle/{macos,dmg}/...# Opening the .app directly for the first time gets blocked by Gatekeeper (expected — it isn't signed yet); allow it locally with:xattr -dr com.apple.quarantine "target/release/bundle/macos/DuDuClaw.app"open "target/release/bundle/macos/DuDuClaw.app"A.5 Item-by-item acceptance (matches the TODO)
Section titled “A.5 Item-by-item acceptance (matches the TODO)”| TODO item | How to verify |
|---|---|
| D0 🧪 | cargo tauri dev launches, the window shows the login page, sending one chat message gets a response |
| D1 🧪 | (a) First make sure no launchd/CLI gateway is running → open the app; it should auto-start the sidecar. (b) First run duduclaw run to occupy port 18789 → open the app; it should attach without restarting it (Activity Monitor shows only one duduclaw process) |
| D2.1 🧪 | Open the app twice in a row → only one window gets focus, only one duduclaw process exists |
| D2.3 🧪 | After a normal quit, `ps aux |
| D2.4 🧪 | The tray icon shows status; the Start/Stop menu items drive the sidecar; closing the window minimizes to the tray instead of quitting |
| D2.5 🧪 | Manually kill <sidecar pid> → the app should auto-restart with exponential backoff; after 5+ consecutive kills it should enter an error state and notify, not retry forever |
| D2.6 🧪 | Launch the app from Finder/Dock (not the terminal) → confirm the child processes (Claude CLI, etc.) are still discoverable; you can trigger a chat action that needs the CLI to verify |
| D5 item 1 | cargo tauri build produces a runnable app that auto-starts the sidecar, opens the workspace, and can send chat messages |
| P6.3 manual test | On first launch in the personal edition, land on the workspace → send one chat message → switch to “Advanced” to see the full dashboard → the mode choice persists after a refresh |
| P6.3 screenshots | Capture one light and one dark screenshot, and put them side by side with Genspark 4.0 for critique |
Gate E — generate the Tauri auto-update signing key (free, do this first)
Section titled “Gate E — generate the Tauri auto-update signing key (free, do this first)”Blocks: D4.4 (replacing the update pubkey placeholder).
Why it’s blocked: tauri.conf.json > plugins.updater.pubkey is currently the placeholder REPLACE_WITH_..., and the updater needs a real key pair before it can verify signatures. Until the key is ready, the updater is fully disabled (plugins.updater.active = false and bundle.createUpdaterArtifacts = false); otherwise a local cargo tauri build fails at the final updater-artifact signing step with A public key has been found, but no private key.
cargo tauri signer generate -w ~/.tauri/duduclaw-updater.key# The terminal prints the public key and writes the private key to ~/.tauri/duduclaw-updater.key- Paste the public key into
plugins.updater.pubkeyin src-tauri/tauri.conf.json. - In the same file, turn the updater back on:
plugins.updater.active = true,bundle.createUpdaterArtifacts = true. - Set the private key content and its password as GitHub repo secrets:
TAURI_SIGNING_PRIVATE_KEY(the private key file’s content)TAURI_SIGNING_PRIVATE_KEY_PASSWORD
- The private key never goes into the repo. Losing it means published clients can no longer receive updates, so back it up to a password manager.
Acceptance (half of D4.4): after a CI release, the artifacts include latest.json with signature fields. The full “old version → update” round trip needs two already-signed releases (do that after Gate B).
Gate B — Apple Developer ID signing + notarization (macOS release)
Section titled “Gate B — Apple Developer ID signing + notarization (macOS release)”Blocks: D3.1 🧪, D3.2 🧪, D4.1, D4.4 (Mac end-to-end), D5 signing/clean machine.
Current status (verified against Keychain, 2026-07): signing is unblocked. This machine has a valid
Developer ID Application: Dudu Technology Ltd. (7469HYQ6HH)certificate (expires 2031-03, private key in Keychain,codesignverified working), and it’s already set in tauri.conf.json underbundle.macOS.signingIdentity, socargo tauri buildsigns automatically (no env vars needed). All that’s left is notarization: create an app-specific password (B.1 step 4) and passAPPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID=7469HYQ6HH, then verify D4.1 / D5 on a second, clean machine.
B.1 Get the certificate and authentication details
Section titled “B.1 Get the certificate and authentication details”- ✅ Already have an Apple Developer Program account + Developer ID certificate (Team ID
7469HYQ6HH). - ✅ The Developer ID Application certificate is already in Keychain and valid (see “Current status” above).
- (For CI) Export it as a
.p12(including the private key) and note the password. - ⬜ Create an app-specific password: appleid.apple.com → Sign-In and Security → App-Specific Passwords. (The only remaining step for notarization.)
- ✅ Team ID =
7469HYQ6HH.
B.2 Sign and notarize locally (verify once by hand)
Section titled “B.2 Sign and notarize locally (verify once by hand)”# signingIdentity is already set in tauri.conf.json, so the build signs automatically. Pass these three env vars for notarization:export APPLE_ID="you@example.com"export APPLE_PASSWORD="<app-specific-password>" # B.1 step 4export APPLE_TEAM_ID="7469HYQ6HH"cd src-tauri && cargo tauri build # signs + notarizes + staples (when the env vars are set)# Or build first, then sign, notarize, and staple separately with the bundled script:../scripts/desktop/sign-notarize-macos.sh "target/release/bundle/dmg/DuDuClaw_1.31.0_aarch64.dmg"The script uses the hardened runtime entitlements from src-tauri/entitlements.plist.
B.3 Set as CI secrets (for automated releases)
Section titled “B.3 Set as CI secrets (for automated releases)”In the GitHub repo, go to Settings → Secrets and variables → Actions and add:
| Secret | Value |
|---|---|
APPLE_CERTIFICATE |
base64 -i DeveloperID.p12 (the full output) |
APPLE_CERTIFICATE_PASSWORD |
the .p12 password |
APPLE_SIGNING_IDENTITY |
Developer ID Application: <name> (<TEAMID>) |
APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID |
same as above |
B.4 Item-by-item acceptance
Section titled “B.4 Item-by-item acceptance”| TODO item | How to verify | Status |
|---|---|---|
| D4.1 🧪 | Copy the signed and notarized .dmg to another Mac that has never had your certificate installed, then double-click it → it should not show “from an unidentified developer” (來自未識別開發者) |
✅ Verified (2026-07-01, desktop-v1.31.0): stapler validate = worked, spctl -a = accepted / Notarized Developer ID |
| D3.1 🧪 | After signing with the hardened runtime, open the app and confirm the sidecar can still spawn the CLI / reach the network (trigger a chat action that needs the network) | ⬜ Still needs a real run inside the signed app |
| D3.2 🧪 | The first time Computer Use runs, the system should show the Accessibility / Screen Recording permission prompt; after granting it, screenshots/simulated input should work | ⬜ Not yet verified |
| D5 signing/clean machine | Same as D4.1, and spctl -a -vvv DuDuClaw.app should return accepted |
✅ Verified: spctl -a -vvv = accepted, source=Notarized Developer ID |
Gate C — Windows Authenticode signing
Section titled “Gate C — Windows Authenticode signing”Blocks: D4.2. Why it’s blocked: it needs an Authenticode code-signing certificate.
⚠️ Major change starting 2023/6: the CA/B Forum now requires even OV (standard) certificates to be stored on FIPS-compliant hardware (a USB token or a cloud HSM) — you can no longer download a plain
.pfxand drop it into CI. Automated signing therefore needs a cloud signing service; the plain-.pfxpath only still works for old inventory certificates or temporary certificates exported from a cloud HSM.
Where to buy one (cheapest to priciest)
Section titled “Where to buy one (cheapest to priciest)”| Option | Type | Price (approx.) | CI auto-sign | Best for |
|---|---|---|---|---|
| Azure Trusted Signing | OV (Microsoft’s own) | ~US$9.99/month | ✅ native signtool dlib |
First choice: cheapest, best SmartScreen reputation; requires identity verification |
| Certum open-source code signing | OV (open-source only) | ~US$30–70/year | ✅ SimplySign cloud | DuDuClaw is Apache-2.0 → qualifies, budget pick |
| SSL.com eSigner | OV / EV | OV from ~US$249/year | ✅ eSigner cloud API | Established, well-documented |
| DigiCert KeyLocker | OV / EV | On the higher end | ✅ KeyLocker | Enterprise-grade |
| Sectigo/Comodo (resellers: The SSL Store, SignMyCode, Codegic) | OV / EV | OV ~US$200–400/year | Depends on the plan | Resellers often discount |
OV vs. EV: OV is cheaper, but its SmartScreen reputation needs accumulated download volume before warnings taper off; EV is pricier but clears SmartScreen right away. You can buy either online by card from Taiwan; the process includes identity/organization verification.
Route 1 (recommended) — Azure Trusted Signing (cloud, ~US$10/month)
Section titled “Route 1 (recommended) — Azure Trusted Signing (cloud, ~US$10/month)”- In the Azure portal, create a Trusted Signing account + Certificate Profile and complete identity verification.
- Create a service principal and set these CI secrets:
AZURE_TENANT_ID,AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_TS_ENDPOINT,AZURE_TS_ACCOUNT,AZURE_TS_PROFILE. - Sign in CI using the official action (replacing the plain-
.pfxstep):- name: Azure Trusted Signingif: matrix.os == 'windows-latest'uses: azure/trusted-signing-action@v0with:azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}endpoint: ${{ secrets.AZURE_TS_ENDPOINT }}trusted-signing-account-name: ${{ secrets.AZURE_TS_ACCOUNT }}certificate-profile-name: ${{ secrets.AZURE_TS_PROFILE }}files-folder: src-tauri/targetfiles-folder-filter: msi,exefile-digest: SHA256timestamp-rfc3161: http://timestamp.acs.microsoft.comtimestamp-digest: SHA256
⚠️ Regional restriction: Azure Trusted Signing is currently open only to organizations in the US/Canada/EU/UK, and to individual developers in the US/Canada. Taiwan/Macau and similar regions don’t qualify: you can fill out the form and create the resources, but identity validation gets stuck, so it’s wasted effort. If you’re outside those regions, use Route 2 (Certum) instead.
Route 2 — Certum open-source certificate (cloud SimplySign, no regional restriction, works for Taiwan/Macau)
Section titled “Route 2 — Certum open-source certificate (cloud SimplySign, no regional restriction, works for Taiwan/Macau)”- Go to shop.certum.eu and search for “Open Source Code Signing,” then buy “Open Source Code
Signing in the Cloud” (the cloud edition, about €49). The three editions differ like this:
- code (€25): certificate only; you supply your own Certum crypto card and reader, so it doesn’t fit here.
- set (€69): includes a physical card and reader; needs international shipping and CI can’t automate it, so it doesn’t fit either.
- in the Cloud (€49): the certificate lives in the cloud, no hardware needed — pick this one.
- Complete individual identity verification (accepts international applicants, requires uploading ID), and attach the DuDuClaw GitHub link to prove it’s open source.
- Install SimplySign (maps the cloud certificate to a locally usable signing device), or use its CLI.
- Signing tools:
- Windows:
signtoolconnected to SimplySign (PKCS#11 / CSP). - Mac/Linux/CI: use
osslsigncodewith the SimplySign cloud key — you can sign.msifiles without ever opening Windows.
- Windows:
💳 Payment note (verified 2026-06): Certum’s payment processor (Autopay, EU) only accepts Visa/Mastercard, not JCB; cross-border Apple Pay often fails with “service unavailable” (無法取得服務). If you only have a JCB card: ① try PayPal (it usually accepts JCB); ② get a Wise/Revolut virtual Visa (available in Taiwan/Macau, and useful later for the Apple Developer $99 fee and various SaaS subscriptions — strongly recommended); ③ ask someone with a Visa/Mastercard to pay on your behalf.
Route 3 (fallback) — plain .pfx (only for old inventory certificates / temporary certificates exported from an HSM)
Section titled “Route 3 (fallback) — plain .pfx (only for old inventory certificates / temporary certificates exported from an HSM)”Use the existing sign-windows.ps1 script: set the secrets
WINDOWS_CERT_PFX_BASE64 and WINDOWS_CERT_PASSWORD, then run it locally by hand:
pwsh scripts/desktop/sign-windows.ps1 -Artifact path\to\DuDuClaw_1.30.1_x64.msiRecommendation: in the US/Canada/EU/UK, use Route 1 (Azure, ~$10/month, friendliest to SmartScreen); in Taiwan/Macau and other regions, use Route 2 (Certum Cloud, €49) — the only option with no regional restriction that still supports CI.
⏭️ This gate can wait (priority note)
Section titled “⏭️ This gate can wait (priority note)”Windows signing is the lowest-priority, most deferrable item in all of Phase D. Don’t let it block the project:
- An unsigned Windows installer still installs fine — SmartScreen just shows an “Unknown publisher” (未知發行者) warning once, and the user clicks “Run anyway” (仍要執行) to proceed.
- If you’re developing on macOS and the audience skews Mac/Taiwan, do Gate A (get it running locally) + Gate B (macOS signing) first; Windows can ship unsigned initially, and get signed later once you have a Wise/Revolut card or actual Windows users need it.
- In CI: when the repo variable
WINDOWS_SIGN_METHODisn’t set, the signing step auto-skips (see desktop-release.yml), so it doesn’t block releases on other platforms.
Acceptance (D4.2 🧪): download the signed .msi on a clean Windows machine — SmartScreen should not block it (OV needs accumulated reputation; EV/Azure clears faster).
Gate D — Linux packaging verification
Section titled “Gate D — Linux packaging verification”Blocks: D4.3 🧪.
Why it’s blocked: it needs a Linux environment/VM to test the .AppImage/.deb. No signing required.
# On Ubuntu 22.04 (or an already-configured CI runner)sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelfcd src-tauri && cargo tauri build# Output: target/release/bundle/{appimage,deb}/...Acceptance: run the .AppImage once each on Ubuntu and Fedora — the app should launch and connect to the gateway.
Gate F — end-to-end auto-update (needs B + E done)
Section titled “Gate F — end-to-end auto-update (needs B + E done)”Blocks: D4.4 🧪, D5 auto-update.
- Confirm Gate E’s pubkey is filled in and the private key is in the secrets.
- Publish the first version:
git tag desktop-v1.30.1 && git push origin desktop-v1.30.1(CI produces the release +latest.json). - Install that version on a test machine.
- Bump the version in
src-tauri/tauri.conf.jsonto1.30.2and publish a second tag. - Open the old app → it should detect the new version → verify the signature → download it → prompt for a restart → apply it.
- Negative test: sign a fake update with the wrong key → the client should refuse to install it (signature verification fails).
One-time checklist (clears everything)
Section titled “One-time checklist (clears everything)”- Gate A:
cargo tauri buildproduces the app locally, all 7 lifecycle checks pass (D0/D1/D2.*/D5-1/P6.3) - Gate E: updater key generated, pubkey filled in, private key in secrets (half of D4.4)
- Gate B: Apple certificate → signed and notarized, unblocked on a clean Mac (D3.1/D3.2/D4.1/D5)
- Gate C: Windows certificate → signed, SmartScreen doesn’t block it (D4.2)
- Gate D: Linux
.AppImage/.debruns (D4.3) - Gate F: auto-update between two versions succeeds, and signature verification rejects a mismatch (D4.4/D5)
Once everything is done, flip the matching TODO items from
[ ]/[~]to[x].