4 releases (breaking)
Uses new Rust 2024
| new 0.10.0 | Dec 8, 2025 |
|---|---|
| 0.8.0 | Dec 6, 2025 |
| 0.5.0 |
|
#1367 in Authentication
38 downloads per month
Used in 3 crates
215KB
3.5K
SLoC
kodegen_bundler_sign
Automated code signing and certificate provisioning for Kodegen daemon
Overview
kodegen_bundler_sign is a comprehensive tool for managing code signing workflows across macOS, Linux, and Windows platforms. Its primary focus is macOS, where it provides automated certificate provisioning through Apple's App Store Connect API, builds and signs privileged helper applications, and manages deployment to GitHub releases.
The package serves three main purposes:
- Certificate Provisioning: Automates the process of obtaining Developer ID Application certificates from Apple using App Store Connect API credentials
- Helper App Management: Builds, signs, packages, and deploys the macOS privileged helper application (
KodegenHelper.app) that enables the Kodegen daemon to execute administrative tasks - Notarization & Signing: Provides tools for Apple notarization and direct code signing with entitlements
Quick Start
Interactive Setup (Recommended)
cargo run --package kodegen_bundler_sign -- --interactive
This will guide you through:
- Checking for existing certificates
- Providing App Store Connect API credentials
- Generating and requesting a Developer ID certificate
- Installing it to your keychain
Build Helper App
cargo run --package kodegen_bundler_sign -- --build-helper
Build and Upload to GitHub
export GITHUB_TOKEN="your_token"
cargo run --package kodegen_bundler_sign -- --build-helper --upload
Notarize an App
export APPLE_API_KEY="your_key_id"
export APPLE_API_ISSUER="your_issuer_id"
export APPLE_API_KEY_PATH="/path/to/AuthKey_XXXXXXXXXX.p8"
cargo run --package kodegen_bundler_sign -- --notarize /path/to/YourApp.app
Sign a Binary
cargo run --package kodegen_bundler_sign -- --sign /path/to/binary \
--identity "Developer ID Application: Your Name (TEAM_ID)" \
--entitlements entitlements.plist
CLI Modes
1. Show Configuration
cargo run --package kodegen_bundler_sign -- --show
Displays:
- Developer ID certificates in keychain
- Configuration file location (
~/.config/kodegen/signing.toml)
2. Interactive Setup
cargo run --package kodegen_bundler_sign -- --interactive
Guided setup with prompts for:
- App Store Connect Issuer ID
- API Key ID
- Path to .p8 private key file
- Email address
3. Build Helper Mode
cargo run --package kodegen_bundler_sign -- --build-helper [OPTIONS]
Options:
--upload: Upload to GitHub releases--github-token <TOKEN>: GitHub API token (or useGITHUB_TOKENenv var)--output-dir <DIR>: Output directory (default:target/helper)
4. Notarization Mode
cargo run --package kodegen_bundler_sign -- --notarize <PATH> [OPTIONS]
Options:
--wait <BOOL>: Wait for notarization to complete (default: true)
Diagnose notarization setup:
cargo run --package kodegen_bundler_sign -- --diagnose-notarization
Environment Variables:
APPLE_API_KEY: App Store Connect API Key ID (recommended)APPLE_API_ISSUER: App Store Connect Issuer IDAPPLE_API_KEY_PATH: Path to .p8 file (optional, auto-searches standard locations)APPLE_ID: Apple ID email (legacy method)APPLE_PASSWORD: App-specific password (legacy method)APPLE_TEAM_ID: Team ID (legacy method)
5. Direct Signing Mode
cargo run --package kodegen_bundler_sign -- --sign <BINARY> [OPTIONS]
Options:
--identity <IDENTITY>: Signing identity (required)--entitlements <PATH>: Path to entitlements.plist (optional)--hardened-runtime <BOOL>: Enable hardened runtime (default: true)
6. Config File Mode
cargo run --package kodegen_bundler_sign -- --config signing.toml
Example signing.toml:
platform = "macos"
dry_run = false
verbose = true
issuer_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
key_id = "XXXXXXXXXX"
private_key_path = "~/.keys/AuthKey_XXXXXXXXXX.p8"
certificate_type = "developer_id"
common_name = "Kodegen Helper"
keychain = "login.keychain-db"
Architecture
Core Modules
lib.rs: Library interface with platform-conditional exports and shared utilitiesmain.rs: CLI application with six operational modes (show, interactive, build-helper, notarize, sign, config)config.rs: Configuration structures for all platformserror.rs: Custom error types usingthiserror
Platform-Specific Modules
macOS (src/macos/)
setup.rs: Certificate provisioning workflowkeychain.rs: Keychain operations with file lockingnotarization.rs: Apple notarization workflow (submit, poll, staple)validation.rs: Certificate and setup validationprompts.rs: Interactive user promptsmod.rs: Module exports and platform entry point
Build & Packaging (macOS)
build_helper.rs: macOS helper app creation and C code compilationsign_helper.rs: Code signing operations with codesignpackage_helper.rs: ZIP packaging and integrity hashingapple_api.rs: App Store Connect API client
Cross-Platform
windows.rs: Cross-platform Authenticode signing usingosslsigncode(available on all platforms, not just Windows)linux.rs: GPG-based signing guidance
Apple API Integration
App Store Connect API Client
The apple_api.rs module implements JWT-based authentication with Apple's certificate provisioning API.
Authentication Flow
- Load .p8 Private Key: ECDSA ES256 private key from App Store Connect
- Generate JWT Token:
- Algorithm: ES256
- Header:
kid(Key ID),alg: "ES256" - Claims:
iss(Issuer ID),aud: "appstoreconnect-v1",iat,exp(20 minutes)
- Sign JWT: Sign with private key using
jsonwebtokencrate - API Request: Include JWT as Bearer token in Authorization header
Certificate Request API
Endpoint: https://api.appstoreconnect.apple.com/v1/certificates
Request:
{
"data": {
"type": "certificates",
"attributes": {
"certificateType": "DEVELOPER_ID_APPLICATION",
"csrContent": "<PEM-encoded CSR>"
}
}
}
Response:
{
"data": {
"attributes": {
"certificateContent": "<base64-encoded DER certificate>"
}
}
}
Certificate Provisioning Workflow
Prerequisites
- Apple Developer Account with Admin or Developer role
- Create App Store Connect API Key:
- Navigate to App Store Connect
- Go to: Users and Access → Keys → App Store Connect API
- Click "+" to create new key
- Name: "Kodegen Signing"
- Access: Developer role
- Download
.p8file (one-time download only) - Note the Key ID (10 alphanumeric characters)
- Note the Issuer ID (UUID format)
Automated Provisioning Process
- API Authentication: JWT generation with ES256 signature (20-minute lifetime)
- CSR Generation: RSA key pair with
rcgen - Request Certificate: POST CSR to Apple's API
- Create P12 Bundle: Using OpenSSL
- Import to Keychain: With file locking to prevent race conditions
- Save Configuration: To
~/.config/kodegen/signing.toml - Cleanup: Remove temporary files
Helper App Architecture
Purpose
KodegenHelper.app is a macOS privileged helper that enables the Kodegen daemon to execute administrative tasks without running the entire daemon as root. It follows macOS best practices for privilege separation using the Service Management framework.
Security Model
Authorization Requirements:
- Helper requires
admingroup membership - Daemon identity must match
SMAuthorizedClientsin Info.plist - Helper identity must match
SMPrivilegedExecutablesin daemon's Info.plist - Code signature verification enforced by macOS
Runtime Security:
- Parent Process Validation: Uses
proc_pidpathto verify parent iskodegend - Script Size Limit: Maximum 1MB (1,048,576 bytes)
- Execution Timeout: 5 minutes enforced via
SIGALRM - Temporary File Security: Uses
mkstempfor secure random filenames - Secure Permissions: Files remain 0600 (owner-only) throughout execution
- Automatic Cleanup: Removes temporary files after execution
C Source Code Implementation
The helper is implemented in C and compiled with cc. Key features:
Main Function Flow:
1. Validate parent process name contains "kodegen" (macOS: proc_pidpath)
2. Accept script content as argv[1]
3. Validate script size <= 1MB
4. Set up SIGALRM timeout handler (300 seconds)
5. Create temporary file with mkstemp: /tmp/kodegend_helper_XXXXXX
6. Write script content (0600 permissions preserved for security)
7. Fork child process
8. Child: execl("/bin/sh", "sh", temp_path, NULL)
9. Parent: waitpid for completion
10. Clean up temporary file
11. Return child exit status
Error Handling:
- Exit code 1: Validation or setup failures
- Exit code 124: Timeout reached
- Exit code 128 + N: Killed by signal N
- Otherwise: Child process exit code
App Bundle Structure
KodegenHelper.app/
├── Contents/
│ ├── Info.plist # Bundle metadata
│ └── MacOS/
│ └── KodegenHelper # Compiled C executable
Info.plist Configuration
Key Settings:
- Bundle ID:
ai.kodegen.kodegend.helper - LSUIElement:
true(background agent, no dock icon) - Minimum macOS: 10.15
Authorization Settings:
<key>SMPrivilegedExecutables</key>
<dict>
<key>ai.kodegen.kodegend.helper</key>
<string>identifier "ai.kodegen.kodegend.helper" and anchor apple generic</string>
</dict>
<key>SMAuthorizedClients</key>
<array>
<string>identifier "ai.kodegen.kodegend" and anchor apple generic</string>
</array>
Code Signing Process
Signing Workflow
The sign_helper.rs module implements a multi-step signing process:
- Certificate Check:
security find-identity -v -p codesigning- Falls back to ad-hoc signing (
-) for development if no Developer ID found
- Falls back to ad-hoc signing (
- Entitlements Creation: Generates
helper.entitlementswith admin authorization requirements - Sign Executable:
codesign --force --sign <identity> --options runtime --entitlements helper.entitlements Contents/MacOS/KodegenHelper - Sign App Bundle:
codesign --force --deep --sign <identity> --options runtime KodegenHelper.app - Verify Signature:
codesign --verify --deep --strict KodegenHelper.app
Signing Identities
Production:
- Identity: "Developer ID Application: Your Name (TEAM_ID)"
- Obtained via automated provisioning or manual certificate installation
Development:
- Identity: "-" (ad-hoc signing)
- Set via:
export KODEGEN_SIGNING_IDENTITY="-" - Warnings instead of errors for signing failures
Hardened Runtime
All production builds use --options runtime flag, enabling:
- Library validation
- Hardened runtime protections
- Required for notarization
Notarization
Notarization Workflow
Apple notarization is a security process where Apple scans your app for malware and signs it with a notarization ticket.
Submit for notarization:
cargo run --package kodegen_bundler_sign -- --notarize /path/to/App.app
Non-blocking submission:
cargo run --package kodegen_bundler_sign -- --notarize /path/to/App.app --wait false
Diagnose setup issues:
cargo run --package kodegen_bundler_sign -- --diagnose-notarization
Authentication Methods
Modern (Recommended): App Store Connect API Key
export APPLE_API_KEY="XXXXXXXXXX"
export APPLE_API_ISSUER="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export APPLE_API_KEY_PATH="/path/to/AuthKey_XXXXXXXXXX.p8"
Legacy: Apple ID with app-specific password
export APPLE_ID="your@email.com"
export APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="XXXXXXXXXX"
Process Flow
- Upload: App is uploaded to Apple's notarization service using
xcrun notarytool submit - Poll: Tool polls for completion status
- Staple: On success, notarization ticket is stapled to the app using
xcrun stapler staple
Packaging and Distribution
ZIP Creation
The package_helper.rs module creates compressed packages:
Features:
- Compression: Deflated (standard ZLIB)
- Unix Permissions: 0755 preserved
- Recursive directory traversal
- Maintains app bundle structure
Output Files:
KodegenHelper.app.zip: Compressed app bundleKodegenHelper.app.zip.sha256: SHA-256 integrity hash (hex-encoded)app_zip_data.rs: Generated Rust code withinclude_bytes!macro
Integrity Verification
Hash Generation:
SHA256(KodegenHelper.app.zip) = <64-character hex string>
Verification Process:
- Checks for required files: Info.plist, executable
- Validates executable is not empty
- Verifies all files readable without corruption
- Ensures Info.plist is at least 100 bytes
Build System Integration
Generated Rust Code:
const APP_ZIP_DATA: &[u8] = include_bytes!("/path/to/KodegenHelper.app.zip");
Cargo Environment Variables:
HELPER_ZIP_PATH: Path to ZIP fileHELPER_ZIP_INCLUDE_FILE: Path to generated Rust fileMACOS_HELPER_ZIP_HASH: SHA-256 hash
Atomic Operations
All build operations are atomic:
- Create temporary working directory
- Validate output directory is writable
- Build and sign helper app
- Validate helper structure
- Create ZIP in temporary location
- Verify ZIP integrity
- Atomic rename to final location
- Cleanup temporary files (success or failure)
Rollback on Failure: All temporary files removed if any step fails.
GitHub Integration
Upload Process
Target Repository: cyrup-ai/kodegen
Workflow:
- Build and sign helper app
- Create ZIP package
- Calculate SHA-256 hash
- Detect system architecture (e.g.,
aarch64,x86_64) - Get latest release via GitHub API
- Upload as asset:
KodegenHelper.app-macos-{arch}.zip
Authentication:
- GitHub token via
--github-tokenflag - Or
GITHUB_TOKENenvironment variable - Requires
reposcope for releases
Upload Command:
cargo run --package kodegen_bundler_sign -- \
--build-helper \
--upload \
--github-token "ghp_xxxxxxxxxxxxxxxxxxxx"
Asset Naming Convention
Format: KodegenHelper.app-macos-{arch}.zip
Examples:
KodegenHelper.app-macos-aarch64.zip(Apple Silicon)KodegenHelper.app-macos-x86_64.zip(Intel)
Windows Cross-Platform Signing
The windows module provides cross-platform Authenticode signing using osslsigncode, available on all platforms (not just Windows). This enables macOS and Linux developers to sign Windows binaries.
Usage
use kodegen_bundler_sign::windows::{SignConfig, sign_binary_with_fallback};
let config = SignConfig {
cert_path: "path/to/cert.pfx".into(),
key_path: None, // Not needed for PKCS#12
password: Some("password".to_string()),
timestamp_url: Some("http://timestamp.digicert.com".to_string()),
app_name: Some("MyApp".to_string()),
app_url: Some("https://example.com".to_string()),
};
sign_binary_with_fallback(Path::new("myapp.exe"), &config).await?;
Features
- Automatic Fallback: Tries multiple timestamp servers if primary fails
- Last Resort: Signs without timestamp if all servers fail
- Cross-Platform: Uses
osslsigncodefor non-Windows platforms - Certificate Formats: Supports PEM, PFX/PKCS#12
Timestamp Servers
Built-in fallback list includes:
- DigiCert
- Sectigo (Comodo)
- GlobalSign
- Others
Dependencies
Core Dependencies
| Package | Version | Purpose |
|---|---|---|
clap |
4 | Command-line argument parsing with derive macros |
anyhow |
1 | Error handling with context |
serde |
1 | Serialization framework |
serde_json |
1 | JSON parsing |
toml |
0.9 | TOML configuration parsing |
thiserror |
2 | Custom error types |
tokio |
1 | Async runtime (full feature set) |
tempfile |
3 | Secure temporary file creation |
macOS-Specific Dependencies
| Package | Version | Purpose |
|---|---|---|
rcgen |
0.14 | CSR and key pair generation |
jsonwebtoken |
9 | JWT creation with ES256 |
base64 |
0.22 | Base64 encoding/decoding |
reqwest |
0.12 | HTTP client for Apple API |
dirs |
6 | Platform-specific paths |
shellexpand |
3 | Tilde expansion |
Build & Packaging Dependencies
| Package | Version | Purpose |
|---|---|---|
cc |
1 | C code compilation |
zip |
6 | ZIP archive creation |
sha2 |
0.10 | SHA-256 hashing |
hex |
0.4 | Hexadecimal encoding |
bytes |
1 | Byte buffers for uploads |
Security & Reliability Dependencies
| Package | Version | Purpose |
|---|---|---|
fs4 |
0.13 | File locking with async support |
rand |
0.9 | Random password generation |
zeroize |
1 | Secure memory clearing |
chrono |
0.4 | Certificate expiry handling |
Integration Dependencies
| Package | Version | Purpose |
|---|---|---|
kodegen_tools_github |
local | GitHub API client |
octocrab |
0.47 | GitHub API library |
cyrup_termcolor |
2 | Terminal colors (required) |
which |
8 | Binary path finding for osslsigncode |
System Dependencies (macOS)
- OpenSSL: PKCS#12 bundle creation (
opensslcommand) - Security Framework: Keychain management (
securitycommand) - Codesign: Code signature operations (
codesigncommand) - Xcode Command Line Tools: C compiler (
cccommand) - Notarytool: Apple notarization (
xcrun notarytool) - Stapler: Notarization ticket stapling (
xcrun stapler)
Platform Support
macOS (Full Support ✅)
Features:
- Automated certificate provisioning
- App Store Connect API integration
- Helper app building and compilation
- Code signing with Developer ID
- Apple notarization (submit, poll, staple)
- Direct binary signing with entitlements
- Hardened runtime support
- ZIP packaging with integrity hashing
- GitHub release uploads
Requirements:
- macOS 10.15 or later
- Xcode Command Line Tools
- Apple Developer Account (for certificate provisioning)
- App Store Connect API key
Windows (Cross-Platform Support ⚠️)
Current Implementation:
- Cross-platform Authenticode signing via
osslsigncode - Works on macOS, Linux, and Windows
- Automatic timestamp server fallback
- Supports PEM and PKCS#12 certificates
- Configuration parsing supported
- Guidance for native Windows tools
Recommendations for Native Windows:
# Import certificate
certutil -user -importpfx code_signing_cert.pfx
# View certificates
certmgr.msc
Linux (Minimal Support ⚠️)
Current Implementation:
- GPG setup guidance only
- No automated provisioning
- Configuration parsing supported
Recommendations:
# Generate GPG key
gpg --full-generate-key
# List keys
gpg --list-secret-keys --keyid-format LONG
Security Considerations
Certificate Security
-
Private Key Protection
- Store
.p8files securely with restricted permissions:chmod 600 - Never commit to version control
- Use environment variables in CI/CD
- Rotate API keys periodically
- Store
-
Keychain Security
- Certificates stored in macOS Keychain
- Access Control List restricts to
/usr/bin/codesign - Requires user authentication on first use
-
JWT Token Security
- Tokens expire after 20 minutes
- Generated on-demand, not stored
- Signed with ES256 algorithm
Helper App Security
-
Code Signing Verification
- Hardened Runtime enabled
- Signature checked by macOS before execution
- Tampering detected and prevented
-
Authorization Model
- Requires admin group membership
- Parent process validation
- Identity matching via Service Management
-
Execution Limits
- 1MB script size limit prevents abuse
- 5-minute timeout prevents hangs
- Automatic cleanup prevents file leaks
-
Input Validation
- Parent process name verification
- Script size validation
- Proper error handling for all system calls
GitHub Upload Security
-
Token Permissions
- Minimum:
reposcope - Recommended: Fine-grained token scoped to repository
- Never expose tokens in logs or error messages
- Minimum:
-
Asset Verification
- SHA-256 hash published with release
- Users should verify hash before use
- Download only from official releases
Troubleshooting
Certificate Provisioning
Issue: Certificate request failed: Unauthorized
Solutions:
- Verify Issuer ID and Key ID are correct
- Ensure
.p8file is not corrupted - Check API key has "Developer" role in App Store Connect
- Confirm API key is not revoked
Issue: Failed to import to keychain
Solutions:
# Unlock keychain
security unlock-keychain login.keychain-db
# List keychains
security list-keychains
# Verify OpenSSL installation
which openssl
openssl version
Issue: No Developer ID certificate found
Solutions:
- Run interactive setup:
cargo run --package kodegen_bundler_sign -- --interactive - Or use ad-hoc signing:
export KODEGEN_SIGNING_IDENTITY="-"
Helper Building
Issue: Failed to compile helper: cc not found
Solutions:
# Install Xcode Command Line Tools
xcode-select --install
# Verify installation
cc --version
xcode-select -p
Issue: Failed to sign executable: code object is not signed at all
Solution: This is a warning in development mode, not an error. The build continues with ad-hoc signing.
Issue: Helper validation failed: Helper executable not executable
Solutions:
# Check permissions
ls -l target/helper/KodegenHelper.app/Contents/MacOS/KodegenHelper
# Fix permissions
chmod +x target/helper/KodegenHelper.app/Contents/MacOS/KodegenHelper
Notarization
Issue: No notarization credentials found in environment
Solutions:
# Set API key credentials (recommended)
export APPLE_API_KEY="XXXXXXXXXX"
export APPLE_API_ISSUER="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export APPLE_API_KEY_PATH="/path/to/AuthKey_XXXXXXXXXX.p8"
# Or use Apple ID (legacy)
export APPLE_ID="your@email.com"
export APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="XXXXXXXXXX"
Issue: Notarization failed
Solutions:
- Run diagnostics:
cargo run --package kodegen_bundler_sign -- --diagnose-notarization - Check app is signed with Developer ID
- Verify hardened runtime is enabled
- Ensure all frameworks and executables are signed
- Check notarization log for specific errors
GitHub Upload
Issue: GitHub token required for upload
Solutions:
# Set environment variable
export GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"
# Or use CLI flag
cargo run --package kodegen_bundler_sign -- --build-helper --upload --github-token "ghp_xxx"
Issue: Failed to get latest release
Solutions:
- Verify repository exists:
cyrup-ai/kodegen - Check token has
reposcope - Ensure at least one release exists
- Verify network connectivity
Development
Local Development Setup
# 1. Clone repository (adjust path for your setup)
cd kodegen-bundler-sign
# 2. Provision certificate (one-time)
cargo run --package kodegen_bundler_sign -- --interactive
# 3. Build helper app
cargo run --package kodegen_bundler_sign -- --build-helper
# 4. Verify output
ls -lh target/helper/
Testing
# Run with verbose output (via config file with verbose = true)
cargo run --package kodegen_bundler_sign -- --config signing.toml
# Dry-run mode (via config file with dry_run = true)
cargo run --package kodegen_bundler_sign -- --config signing.toml
# Show current configuration
cargo run --package kodegen_bundler_sign -- --show
# Diagnose notarization setup
cargo run --package kodegen_bundler_sign -- --diagnose-notarization
CI/CD Integration
GitHub Actions Example:
name: Build Helper
on:
push:
branches: [main]
release:
types: [created]
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt, clippy
- name: Provision Certificate
env:
ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}
run: |
echo "$PRIVATE_KEY" > AuthKey.p8
chmod 600 AuthKey.p8
cargo run --package kodegen_bundler_sign -- \
--issuer-id "$ISSUER_ID" \
--key-id "$KEY_ID" \
--private-key AuthKey.p8
- name: Build Helper
run: |
cargo run --package kodegen_bundler_sign -- --build-helper
- name: Upload to Release
if: github.event_name == 'release'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cargo run --package kodegen_bundler_sign -- \
--build-helper \
--upload
License
See LICENSE.md in the repository root.
Dependencies
~27–46MB
~800K SLoC