7.5 KiB
Code Signing Guide for VibeTunnel
This comprehensive guide covers all aspects of code signing for VibeTunnel, from local development setup to release distribution.
Table of Contents
Development Setup
Initial Team Configuration
VibeTunnel uses xcconfig files to manage developer team settings, allowing multiple developers to work without code signing conflicts.
-
Copy the template file to create your local configuration:
cp ../apple/Local.xcconfig.template ../apple/Local.xcconfig -
Edit
../apple/Local.xcconfigand add your development team ID:DEVELOPMENT_TEAM = YOUR_TEAM_ID_HEREFinding your team ID in Xcode:
- Open Xcode → Settings (or Preferences)
- Go to Accounts tab
- Select your Apple ID
- Look for your Team ID in the team details
-
Open the project in Xcode - it will now use your personal development team automatically.
How xcconfig Works
VibeTunnel/Shared.xcconfig- Contains shared configuration and includes local settings../apple/Local.xcconfig- Your personal settings (ignored by git)../apple/Local.xcconfig.template- Template for new developers
Avoiding Keychain Dialogs During Development
VibeTunnel stores dashboard passwords in the keychain, which can trigger repeated authorization dialogs during development.
Debug Mode Behavior
In DEBUG builds, the app automatically skips keychain reads to avoid dialogs:
- Password Setting: You can set passwords during the current session
- Session Persistence: Passwords work normally until app restart
- No Persistence: Passwords are "forgotten" on restart (not read from keychain)
- No Dialogs: Prevents keychain authorization dialogs during development
When setting a password in debug mode, you'll see:
Debug mode: Password saved to keychain but will not persist across app restarts.
The password will only be available during this session to avoid keychain
authorization dialogs during development.
Testing Password Persistence
To test actual password persistence:
- Build in Release mode:
- Product → Scheme → Edit Scheme → Run → Build Configuration → Release
- Use Archive build:
- Product → Archive (always uses Release configuration)
Release Signing & Notarization
Prerequisites
- Apple Developer Program membership ($99/year)
- Developer ID Application certificate in your Keychain
- App Store Connect API key for notarization
Setting Up Developer ID Certificate
- Go to Apple Developer Portal
- Create a new certificate → Developer ID → Developer ID Application
- Download and install the certificate in your Keychain
Environment Variables
Create a .env file in the project root (gitignored):
# Optional: Specify signing identity (otherwise uses first Developer ID found)
SIGN_IDENTITY="Developer ID Application: Your Name (TEAM123456)"
# App Store Connect API Key for notarization
APP_STORE_CONNECT_API_KEY_P8="-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg...
-----END PRIVATE KEY-----"
APP_STORE_CONNECT_KEY_ID="ABC123DEF4"
APP_STORE_CONNECT_ISSUER_ID="12345678-1234-1234-1234-123456789012"
Creating App Store Connect API Key
- Go to App Store Connect
- Click "Generate API Key"
- Set role to "Developer"
- Download the
.p8file - Note the Key ID and Issuer ID
Usage
Sign Only (for development)
./scripts/sign-and-notarize.sh --sign-only
Sign and Notarize (for distribution)
./scripts/sign-and-notarize.sh --sign-and-notarize
Individual Scripts
# Just code signing
./scripts/codesign-app.sh build/Build/Products/Release/VibeTunnel.app
# Just notarization (requires signed app)
./scripts/notarize-app.sh build/Build/Products/Release/VibeTunnel.app
Script Options
# Show help
./scripts/sign-and-notarize.sh --help
# Sign and notarize with custom app path
./scripts/sign-and-notarize.sh --app-path path/to/VibeTunnel.app --sign-and-notarize
# Skip stapling (for CI environments)
./scripts/sign-and-notarize.sh --sign-and-notarize --skip-staple
# Don't create ZIP archive
./scripts/sign-and-notarize.sh --sign-and-notarize --no-zip
# Verbose output for debugging
./scripts/sign-and-notarize.sh --sign-and-notarize --verbose
CI/CD Setup (GitHub Actions)
Add these secrets to your GitHub repository:
APP_STORE_CONNECT_API_KEY_P8- The complete .p8 key contentAPP_STORE_CONNECT_KEY_ID- The Key IDAPP_STORE_CONNECT_ISSUER_ID- The Issuer ID
The CI workflow automatically uses these for notarization when building on the main branch.
Troubleshooting
Development Issues
xcconfig Not Working
- Ensure
../apple/Local.xcconfigexists - Check that the file isn't committed to git
- Verify the DEVELOPMENT_TEAM value is correct
Keychain Dialogs Still Appearing
- Verify you're running in Debug configuration
- Check
DashboardKeychain.swiftimplementation - Ensure you're not in Release mode
Code Signing Issues
"No signing identity found"
- Install Developer ID Application certificate
- Check with:
security find-identity -v -p codesigning
"User interaction is not allowed"
- Unlock keychain:
security unlock-keychain - Or use:
security unlock-keychain -p <password> login.keychain
Notarization Issues
"Invalid API key"
- Verify API key content, ID, and Issuer ID
- Ensure .p8 key includes BEGIN/END lines
"App bundle not eligible for notarization"
- Ensure proper code signing with hardened runtime
- Check entitlements configuration
"Notarization failed"
- Script shows detailed error messages
- Common issues: unsigned binaries, invalid entitlements, prohibited code
Verification Commands
# Verify code signature
codesign --verify --verbose=2 VibeTunnel.app
# Test with Gatekeeper (should pass for notarized apps)
spctl -a -t exec -vv VibeTunnel.app
# Check if notarization ticket is stapled
stapler validate VibeTunnel.app
Reference
Build Configurations
- Debug builds: Use personal development certificate
- Release builds: Use Developer ID for distribution
- CI builds: Use ad-hoc signing
File Structure After Signing
build/
├── Build/Products/Release/VibeTunnel.app # Signed and notarized app
├── VibeTunnel-notarized.zip # Distributable archive
└── VibeTunnel-1.0.0.dmg # DMG (if created)
Security Notes
- Never commit signing certificates or API keys
- Use environment variables or secure CI/CD secrets
- The
.envfile is gitignored for security - API keys should have minimal permissions (Developer role)
Implementation Details
Debug keychain behavior is in DashboardKeychain.swift:
getPassword()returnsnilin DEBUG buildssetPassword()saves but logs non-persistencehasPassword()works normally