macgo

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 5, 2026 License: MIT Imports: 10 Imported by: 1

README

macgo

A Go library for building macOS applications with proper permissions, entitlements, and bundle structure.

GoDoc License

Overview

macgo simplifies the process of creating macOS applications in Go by automatically handling:

  • App bundle creation and management
  • Permission requests (camera, microphone, files, etc.)
  • Code signing (ad-hoc and Developer ID)
  • Entitlements and sandboxing
  • TCC (Transparency, Consent, and Control) integration

Installation

go get github.com/tmc/macgo

Quick Start

package main

import (
    "log"
    "github.com/tmc/macgo"
)

func main() {
    // Request camera permission with automatic app bundle creation
    err := macgo.Request(macgo.Camera)
    if err != nil {
        log.Fatal(err)
    }

    // Your application code here
}

Core Features

Simple Permission Requests

Request macOS permissions with a single function call:

// Request single permission
macgo.Request(macgo.Camera)

// Request multiple permissions
macgo.Request(macgo.Camera, macgo.Microphone, macgo.Files)
Automatic Bundle Creation

macgo automatically creates a proper .app bundle structure with:

  • Info.plist with required metadata
  • Entitlements for requested permissions
  • Proper executable location
  • App icons (if provided)
Code Signing Support

Built-in support for code signing:

// Ad-hoc signing (development)
cfg := macgo.NewConfig().
    WithAppName("MyApp").
    WithAdHocSign()

// Developer ID signing (distribution)
cfg := macgo.NewConfig().
    WithAppName("MyApp").
    WithAutoSign() // Auto-detects Developer ID
Environment Configuration

Configure via environment variables:

MACGO_APP_NAME=MyApp          # Application name
MACGO_BUNDLE_ID=com.example   # Bundle identifier
MACGO_AD_HOC_SIGN=1          # Enable ad-hoc signing
MACGO_AUTO_SIGN=1             # Auto-detect signing identity
MACGO_DEBUG=1                 # Debug output
MACGO_FORCE_DIRECT=1          # Force direct execution (bypass LaunchServices)
MACGO_FORCE_LAUNCH_SERVICES=1 # Force use of LaunchServices

Available Permissions

Permission Description
macgo.Camera Camera access
macgo.Microphone Microphone access
macgo.Location Location services
macgo.ScreenRecording Screen recording/capture
macgo.Accessibility Accessibility features (e.g., input simulation)
macgo.Files User-selected file access
macgo.Network Network access
macgo.Sandbox App sandbox

Advanced Usage

Custom Configuration
cfg := macgo.NewConfig().
    WithAppName("MyApp").
    WithBundleID("com.example.myapp").
    WithPermissions(macgo.Camera, macgo.Microphone).
    WithAppGroups("group.com.example.shared").
    WithDebug()

err := macgo.Start(cfg)
Auto Packages

Import auto packages for automatic configuration:

import (
    _ "github.com/tmc/macgo/auto/media"   // Camera + Microphone
    _ "github.com/tmc/macgo/auto/adhoc"   // Ad-hoc signing
    "github.com/tmc/macgo"
)

func main() {
    // Permissions and signing are pre-configured
    macgo.Request()
}

Package Structure

  • macgo - Core library and main API
  • bundle/ - App bundle creation and management
  • codesign/ - Code signing utilities
  • permissions/ - Permission definitions and validation
  • teamid/ - Team ID detection for signing
  • auto/ - Auto-configuration packages
  • examples/ - Example applications
  • internal/ - Internal implementation packages

Examples

See the examples/ directory for complete examples:

Requirements

  • Go 1.21 or later (1.24+ recommended)
  • macOS 11.0 (Big Sur) or later
  • Xcode Command Line Tools (for code signing)

Version Compatibility:

  • macOS 15 (Sequoia): ✅ Fully supported and tested
  • macOS 14 (Sonoma): ✅ Fully supported and tested
  • macOS 13 (Ventura): ✅ Fully supported and tested
  • macOS 12 (Monterey): ⚠️ Limited support (manual testing)
  • macOS 11 (Big Sur): ⚠️ Limited support (manual testing)

For detailed version-specific behaviors, quirks, and testing strategies, see:

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments

This library leverages macOS native frameworks and tools to provide seamless integration with the operating system's security model.

Documentation

Overview

Package macgo provides seamless macOS application bundle creation with permissions and entitlements.

macgo enables Go programs to integrate with macOS security features by automatically creating app bundles with proper structure, Info.plist, entitlements, and code signing. This allows Go applications to request system permissions (camera, microphone, files, etc.) just like native macOS applications.

Quick Start

The simplest way to use macgo is with the Request function:

err := macgo.Request(macgo.Camera)
if err != nil {
    log.Fatal(err)
}
// Camera permission granted, proceed with camera access

Request multiple permissions at once:

err := macgo.Request(macgo.Camera, macgo.Microphone, macgo.Files)
if err != nil {
    log.Fatal(err)
}

Configuration

For more control, use the Config struct with builder methods:

cfg := macgo.NewConfig().
    WithAppName("MyApp").
    WithBundleID("com.example.myapp").
    WithPermissions(macgo.Camera, macgo.Microphone).
    WithAdHocSign().
    WithDebug()

err := macgo.Start(cfg)
if err != nil {
    log.Fatal(err)
}

Available Permissions

  • Camera: access to camera
  • Microphone: access to microphone
  • Location: access to location services
  • ScreenRecording: access to screen recording
  • Accessibility: access to Accessibility features (e.g. input simulation)
  • Files: access to user-selected files
  • Network: network client/server access
  • Sandbox: enable app sandbox with restricted file access

Launch Modes

macgo supports three launch strategies:

Bundle mode (default): creates a .app bundle, relaunches via LaunchServices, and forwards I/O. Required for TCC-dialog permissions (Camera, Microphone, Location).

Single-process mode: codesigns the binary in-place, re-execs for the kernel to pick up new entitlements, then calls setActivationPolicy. No bundle, no child process, no I/O forwarding. Only works for entitlement-only permissions (Accessibility, Virtualization, Network). Enable via Config.WithSingleProcess or MACGO_SINGLE_PROCESS=1.

The internal/launchservices package provides a standalone pure Go replacement for /usr/bin/open (available as cmd/lsopen), but is not used by the default bundle launch path due to run loop constraints in library contexts.

Code Signing

macgo supports multiple signing approaches:

Ad-hoc signing for development:

cfg := macgo.NewConfig().WithAdHocSign()

Automatic Developer ID detection:

cfg := macgo.NewConfig().WithAutoSign()

Specific signing identity:

cfg := macgo.NewConfig().WithCodeSigning("Developer ID Application: Your Name")

Environment Variables

All configuration can be driven by environment variables, which are read by Config.FromEnv. Variables are grouped by category below.

Application identity:

MACGO_APP_NAME            Application display name
MACGO_APP_NAME_PREFIX     Prefix prepended to app names (e.g. "Dev-")
MACGO_BUNDLE_ID           Bundle identifier (e.g. "com.example.myapp")
MACGO_BUNDLE_ID_PREFIX    Prefix prepended to bundle IDs (e.g. "test.")

Permissions (set to "1" to enable):

MACGO_CAMERA              Request camera access
MACGO_MICROPHONE          Request microphone access
MACGO_LOCATION            Request location services access
MACGO_SCREEN_RECORDING    Request screen recording access
MACGO_FILES               Request user-selected file access
MACGO_NETWORK             Request network client access
MACGO_SANDBOX             Enable app sandbox

Code signing:

MACGO_AD_HOC_SIGN         Ad-hoc sign the bundle (set to "1")
MACGO_AUTO_SIGN           Auto-detect Developer ID certificate (set to "1")
MACGO_CODE_SIGN_IDENTITY  Specific signing identity string

Launch behavior (set to "1" unless noted):

MACGO_SINGLE_PROCESS      Single-process mode: codesign + re-exec, no bundle
MACGO_FORCE_DIRECT        Force direct binary execution (skip LaunchServices)
MACGO_FORCE_LAUNCH_SERVICES  Force LaunchServices even when not needed
MACGO_NO_RELAUNCH         Disable automatic relaunch entirely
MACGO_OPEN_NEW_INSTANCE   Set to "0" to disable -n flag for open command

Bundle and debugging:

MACGO_DEBUG               Enable debug logging to stderr (set to "1")
MACGO_KEEP_BUNDLE         Preserve temporary bundle after execution (set to "1")
MACGO_ICON                Path to .icns file for the app icon
MACGO_PROVISIONING_PROFILE  Path to provisioning profile to embed
MACGO_RESET_PERMISSIONS   Reset TCC permissions before requesting (set to "1")

Development:

MACGO_DEV_MODE            Dev mode: signed wrapper exec's the source binary (set to "1")
MACGO_TTY_PASSTHROUGH     Pass TTY device to child process (experimental, set to "1")

Internal (set by macgo, not typically set by users):

MACGO_STDIN_PIPE          Path to stdin named pipe (child reads from parent)
MACGO_STDOUT_PIPE         Path to stdout named pipe (child writes to parent)
MACGO_STDERR_PIPE         Path to stderr named pipe (child writes to parent)
MACGO_CONTROL_PIPE        Path to control FIFO (child writes PID for signal forwarding)
MACGO_CWD                 Original working directory to restore in child
MACGO_BUNDLE_PATH         Path to the .app bundle
MACGO_ORIGINAL_EXECUTABLE Path to the original binary before bundle copy
MACGO_SINGLE_PROCESS_ACTIVE  Sentinel: set to "1" after single-process re-exec

Bundle Structure

macgo creates a standard macOS app bundle:

MyApp.app/
├── Contents/
│   ├── Info.plist
│   ├── MacOS/
│   │   └── MyApp
│   ├── Resources/
│   └── _CodeSignature/

Bundle ID Generation

macgo generates bundle IDs from your Go module path:

github.com/user/project     → com.github.user.project.appname
gitlab.com/org/tool         → com.gitlab.org.tool.appname
example.com/app             → com.example.app.appname
Private/custom modules      → io.username.appname

Auto Packages

Import auto packages for pre-configured setups:

import (
    _ "github.com/tmc/macgo/auto/media"   // Camera + Microphone
    _ "github.com/tmc/macgo/auto/files"   // File access
    _ "github.com/tmc/macgo/auto/adhoc"   // Ad-hoc signing
    "github.com/tmc/macgo"
)

func main() {
    macgo.Request()
}

Cleanup

Cleanup is no longer required. FIFO-based I/O forwarding uses pipe EOF as the exit signal, so no explicit cleanup call is needed. The function is retained as a no-op for backward compatibility.

TCC Integration

macgo integrates with macOS TCC (Transparency, Consent, and Control) to:

  • Generate proper entitlements for requested permissions
  • Handle permission prompts automatically
  • Reset permissions for testing (when requested)
  • Support both sandboxed and non-sandboxed applications

Package macgo provides simple macOS app bundle creation and TCC permission management.

macgo enables Go applications to request macOS system permissions (camera, microphone, files, etc.) by automatically creating app bundles with proper entitlements and handling the relaunch process when necessary.

Usage

To correctly use macgo, you must call macgo.Start() or macgo.Request() at the very beginning of your main() function, before any other logic or background goroutines are started.

1. Basic Usage (One-Liner)

func main() {
    // Request permissions immediately.
    // If the app is not in a bundle, this will create one and relaunch the app.
    if err := macgo.Request(macgo.Camera, macgo.Microphone); err != nil {
        log.Fatal(err)
    }

    // Your application logic starts here...
}

2. Advanced Usage (Configuration)

func main() {
    cfg := &macgo.Config{
        AppName:     "MyApp",
        Permissions: []macgo.Permission{macgo.Camera, macgo.Files},
        Debug:       true, // Enable debug logging to stderr
    }

    // Initialize macgo. This logic handles the process lifecycle.
    // If a relaunch is needed, Start will exit the current process.
    if err := macgo.Start(cfg); err != nil {
        log.Fatal(err)
    }

    // Your application logic starts here...
}

How it Works

When macgo.Start() is called:

  1. It checks if the program is running inside a valid macOS App Bundle.
  2. If NOT, it creates a temporary App Bundle in /tmp with the requested entitlements.
  3. It relaunches the current executable *inside* that bundle using `open`.
  4. It transparently forwards stdin/stdout/stderr from the bundled process to your terminal.
  5. The original process exits.
  6. If ALREADY inside a bundle (the relaunched process), Start() simply returns nil, allowing your main() function to proceed with the requested permissions.

Important Notes

  • macgo.Start() MUST be called early in main().
  • Do not rely on init() functions for logic that depends on permissions, as they run before main.
  • macgo preserves exit codes and terminal I/O.
Example (BundleIDGeneration)

Example showing automatic bundle ID generation

package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// macgo automatically generates meaningful bundle IDs based on your Go module
	// Examples of automatic bundle ID generation:
	//
	// Module: github.com/user/myproject -> Bundle ID: com.github.user.myproject.appname
	// Module: gitlab.com/company/tool   -> Bundle ID: com.gitlab.company.tool.appname
	// Module: example.com/service       -> Bundle ID: com.example.service.appname
	// No module info available          -> Bundle ID: dev.username.appname (or local.app.appname)
	//
	// This replaces the old generic "com.macgo.*" format with meaningful,
	// unique identifiers that reflect your actual project.

	cfg := &macgo.Config{
		AppName: "MyTool",
		// BundleID is automatically inferred from Go module if not specified
		Permissions: []macgo.Permission{macgo.Files},
	}

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// Bundle ID automatically generated based on module path
}
Example (EnvironmentVariables)

Example showing environment variables

package main

import (
	"os"

	"github.com/tmc/macgo"
)

func main() {
	// Environment variables that control macgo behavior:
	// MACGO_NO_RELAUNCH=1           - Skip bundle creation and relaunch
	// MACGO_DEBUG=1                 - Enable debug logging
	// MACGO_RESET_PERMISSIONS=1     - Reset TCC permissions using tccutil
	// MACGO_APP_NAME=MyApp          - Set application name
	// MACGO_APP_NAME_PREFIX=Dev-    - Add prefix to all app names
	// MACGO_BUNDLE_ID=com.example   - Set bundle identifier
	// MACGO_BUNDLE_ID_PREFIX=dev    - Add prefix to all bundle IDs
	// MACGO_KEEP_BUNDLE=1           - Preserve bundle after execution
	// MACGO_CODE_SIGN_IDENTITY=xyz  - Set code signing identity
	// MACGO_AUTO_SIGN=1             - Enable automatic code signing
	// MACGO_AD_HOC_SIGN=1           - Use ad-hoc code signing
	// MACGO_CAMERA=1                - Request camera permissions
	// MACGO_MICROPHONE=1            - Request microphone permissions
	// MACGO_LOCATION=1              - Request location permissions
	// MACGO_FILES=1                 - Request file access permissions
	// MACGO_NETWORK=1               - Request network permissions
	// MACGO_SANDBOX=1               - Enable app sandbox

	// Example: Reset permissions before requesting new ones
	_ = os.Setenv("MACGO_RESET_PERMISSIONS", "1")
	_ = os.Setenv("MACGO_DEBUG", "1")
	defer func() {
		_ = os.Unsetenv("MACGO_RESET_PERMISSIONS")
		_ = os.Unsetenv("MACGO_DEBUG")
	}()

	err := macgo.Request(macgo.Camera)
	if err != nil {
		// Handle error
		return
	}
	// Permissions reset and then requested
}

Index

Examples

Constants

View Source
const (
	Camera          = permissions.Camera
	Microphone      = permissions.Microphone
	Location        = permissions.Location
	ScreenRecording = permissions.ScreenRecording
	Accessibility   = permissions.Accessibility
	Files           = permissions.Files
	Network         = permissions.Network
	Sandbox         = permissions.Sandbox
)

Core permissions covering 95% of use cases. Re-exported from the permissions package for convenience.

View Source
const (
	// UIModeBackground sets LSBackgroundOnly=true. No UI at all.
	// Use for CLI tools, MCP servers, daemons. Prevents -1712 timeout.
	// This is the default.
	UIModeBackground = bundle.UIModeBackground

	// UIModeAccessory sets LSUIElement=true. Can show windows/menu bar but no Dock icon.
	// Use for menu bar apps, floating utilities.
	UIModeAccessory = bundle.UIModeAccessory

	// UIModeRegular is a normal app with Dock icon and full UI.
	// Use for standard GUI applications.
	UIModeRegular = bundle.UIModeRegular
)

Variables

This section is empty.

Functions

func Auto

func Auto() error

Auto loads configuration from environment variables and starts macgo. Useful for external configuration without code changes. Equivalent to Start(NewConfig().FromEnv()).

func Cleanup deprecated

func Cleanup()

Cleanup should be called when the macgo-wrapped application exits. Cleanup is a no-op retained for backward compatibility. Previously it wrote a sentinel file to signal the parent that the child had exited. With FIFO-based I/O forwarding, pipe EOF serves as the exit signal and no sentinel is needed.

Deprecated: No longer required. Safe to remove from callers.

func LaunchAppBundle

func LaunchAppBundle(bundlePath string) error

LaunchAppBundle launches an app bundle using the open command. This ensures proper registration with TCC for permission dialogs.

func Request

func Request(perms ...Permission) error

Request is a convenience function for requesting specific permissions. Creates a minimal config and starts macgo immediately. Respects MACGO_DEBUG environment variable for debug output.

Example (Basic)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Simple permission request
	err := macgo.Request(macgo.Camera, macgo.Microphone)
	if err != nil {
		// Handle error
		return
	}
	// App now has camera and microphone permissions
}
Example (Files)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Request file access permissions
	err := macgo.Request(macgo.Files)
	if err != nil {
		// Handle error
		return
	}
	// App now has file access permissions
}

func SetUIMode

func SetUIMode(mode UIMode) error

SetUIMode is only supported on macOS.

func Start

func Start(cfg *Config) error

Start initializes macgo with the given configuration. Creates an app bundle if needed and handles permission requests. On non-macOS platforms, this is a no-op that returns a no-op function and nil error.

Example (Basic)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Using Start with configuration
	cfg := &macgo.Config{
		AppName:     "MyApp",
		Permissions: []macgo.Permission{macgo.Camera},
		Debug:       false,
	}

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// App now running with configuration
}
Example (Comprehensive)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Comprehensive configuration example
	cfg := &macgo.Config{
		AppName:  "ComprehensiveApp",
		BundleID: "com.example.comprehensive",
		Version:  "1.0.0",
		Permissions: []macgo.Permission{
			macgo.Camera,
			macgo.Microphone,
			macgo.Files,
			macgo.Network,
		},
		Custom: []string{
			"com.apple.security.device.capture",
			"com.apple.security.automation.apple-events",
		},
		Debug:         true,
		AutoSign:      true,
		CleanupBundle: false,
	}

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// App running with full configuration
}

func StartContext

func StartContext(ctx context.Context, cfg *Config) error

Types

type Config

type Config struct {
	// AppName is the application name. Defaults to executable name.
	AppName string

	// BundleID is the bundle identifier. Defaults to module-based ID (e.g., com.github.user.repo.appname).
	BundleID string

	// Version is the application version. Defaults to "1.0.0".
	Version string

	// Permissions are the requested macOS permissions.
	Permissions []Permission

	// Custom allows specifying custom entitlements not covered by Permission constants.
	Custom []string

	// CustomStrings allows specifying custom entitlements with string values.
	// Use WithCustomString to add entries (e.g. com.apple.application-identifier).
	CustomStrings map[string]string

	// CustomArrays allows specifying custom entitlements with string array values.
	// Use WithCustomArray to add entries.
	CustomArrays map[string][]string

	// AppGroups specifies app group identifiers for sharing data between apps.
	// Requires sandbox permission and com.apple.security.application-groups entitlement.
	AppGroups []string

	// Debug enables debug logging.
	Debug bool

	// CleanupBundle enables cleanup of the app bundle after execution.
	// Defaults to false (bundle is kept for reuse).
	CleanupBundle bool

	// CodeSignIdentity is the signing identity to use for code signing.
	// If empty and AutoSign is false, the app bundle will not be signed.
	// Use "Developer ID Application" for automatic identity selection.
	CodeSignIdentity string

	// AutoSign enables automatic detection of Developer ID certificates.
	// If true and CodeSignIdentity is empty, macgo will try to find and use
	// a Developer ID Application certificate automatically.
	AutoSign bool

	// AdHocSign enables ad-hoc code signing using the "-" identity.
	// Ad-hoc signing provides basic code signing without requiring certificates.
	// This is useful for development and testing.
	AdHocSign bool

	// CodeSigningIdentifier is the identifier to use for code signing.
	// If empty, defaults to the bundle identifier.
	CodeSigningIdentifier string

	// ForceDirectExecution forces direct execution instead of LaunchServices.
	// This preserves terminal I/O (stdin/stdout/stderr) but may not trigger
	// proper TCC dialogs. Use this for CLI commands that need terminal output.
	// Note: LaunchServices is used by default to ensure TCC compatibility.
	ForceDirectExecution bool

	// Info allows specifying custom Info.plist keys.
	// This is useful for UsageDescriptions (e.g. NSAccessibilityUsageDescription).
	Info map[string]interface{}

	// UIMode controls how the app appears in the UI.
	// Default (UIModeBackground): LSBackgroundOnly=true for CLI tools.
	UIMode UIMode

	// DevMode creates a stable wrapper bundle that exec's the original binary.
	// This preserves TCC permissions across rebuilds since only the wrapper is signed.
	// Enable via MACGO_DEV_MODE=1 for development workflows where you rebuild frequently.
	DevMode bool

	// ProvisioningProfile is the path to a provisioning profile (.provisionprofile or .mobileprovision).
	// When specified, the profile is embedded in the bundle as Contents/embedded.provisionprofile.
	// Required for entitlements like keychain-access-groups.
	ProvisioningProfile string

	// IconPath is the path to an .icns file to use as the app icon.
	// When specified, the icon is copied to Contents/Resources/ and
	// CFBundleIconFile is set in the Info.plist.
	IconPath string

	// SingleProcess enables single-process mode: codesign in-place, re-exec,
	// and call setActivationPolicy instead of creating an app bundle.
	// This eliminates the two-process architecture entirely.
	// Enable via MACGO_SINGLE_PROCESS=1 or WithSingleProcess().
	SingleProcess bool

	// PostCreateHook is called after the bundle structure is created but
	// before code signing. Use this to inject additional files (helper
	// binaries, LaunchDaemon plists) into Contents/. The bundlePath
	// argument is the .app directory path.
	PostCreateHook func(bundlePath string, cfg *Config) error
}

Config holds macgo configuration options. The zero value is valid and uses sensible defaults. Use NewConfig() and builder methods for fluent configuration.

Example (AppGroups)

Example showing app groups for sharing data between sandboxed apps

package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// App groups allow sandboxed apps to share data
	cfg := &macgo.Config{
		AppName:  "AppGroupsExample",
		BundleID: "com.example.appgroups.demo",
		Permissions: []macgo.Permission{
			macgo.Sandbox, // Required for app groups
		},
		AppGroups: []string{
			"TEAMID.shared-data", // TEAMID placeholder gets automatically substituted
		},
		Debug:    true,
		AutoSign: true,
	}

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// App now has access to shared app group container
}
Example (AppGroupsBuilder)

Example using builder pattern for app groups

package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Using builder pattern with app groups
	cfg := macgo.NewConfig().
		WithAppName("AppGroupBuilder").
		WithPermissions(macgo.Sandbox).
		WithAppGroups("TEAMID.shared-data"). // TEAMID placeholder gets automatically substituted
		WithDebug()

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// App configured with app groups using builder pattern
}
Example (Builder)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Using builder pattern with Config
	cfg := macgo.NewConfig().
		WithAppName("BuilderApp").
		WithPermissions(macgo.Camera, macgo.Microphone).
		WithCustom("com.apple.security.device.capture").
		WithDebug()

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// App configured using builder pattern
}
Example (FromEnv)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// Environment-based configuration
	// Set MACGO_CAMERA=1, MACGO_DEBUG=1, etc.
	cfg := macgo.NewConfig().FromEnv()

	err := macgo.Start(cfg)
	if err != nil {
		// Handle error
		return
	}
	// App configured from environment variables
}

func NewConfig

func NewConfig() *Config

NewConfig creates a new Config with sensible defaults. The zero value is valid, so this is equivalent to &Config{}.

func (*Config) FromEnv

func (c *Config) FromEnv() *Config

FromEnv loads configuration from environment variables. This provides explicit configuration without magic init() functions.

Supported environment variables:

MACGO_APP_NAME          - Application name
MACGO_APP_NAME_PREFIX   - Prefix to add to all app names
MACGO_BUNDLE_ID         - Bundle identifier
MACGO_BUNDLE_ID_PREFIX  - Prefix to add to all bundle IDs
MACGO_DEBUG=1           - Enable debug logging
MACGO_KEEP_BUNDLE=1     - Preserve bundle after execution
MACGO_CODE_SIGN_IDENTITY - Code signing identity
MACGO_AUTO_SIGN=1       - Enable automatic code signing
MACGO_AD_HOC_SIGN=1     - Enable ad-hoc code signing
MACGO_CAMERA=1          - Request camera permission
MACGO_MICROPHONE=1      - Request microphone permission
MACGO_LOCATION=1        - Request location permission
MACGO_SCREEN_RECORDING=1 - Request screen recording permission
MACGO_FILES=1           - Request file access permission
MACGO_NETWORK=1         - Request network permission
MACGO_SANDBOX=1         - Enable app sandbox
MACGO_FORCE_LAUNCH_SERVICES=1 - Force use of LaunchServices
MACGO_FORCE_DIRECT=1    - Force direct execution
MACGO_TTY_PASSTHROUGH=1 - Pass TTY device to child (experimental; default: pipe-based I/O)
MACGO_OPEN_NEW_INSTANCE=0 - Disable -n flag (new instance) for open command (enabled by default)
MACGO_DEV_MODE=1        - Dev mode: wrapper exec's original binary, preserves TCC across rebuilds
MACGO_PROVISIONING_PROFILE - Path to provisioning profile to embed in bundle
MACGO_ICON              - Path to app icon (.icns) to embed in bundle
MACGO_SINGLE_PROCESS=1  - Single-process mode: codesign + re-exec, no app bundle

func (*Config) Validate

func (c *Config) Validate() error

Validate checks the configuration for common issues and dependency requirements. Returns an error if the configuration is invalid.

func (*Config) WithAdHocSign

func (c *Config) WithAdHocSign() *Config

WithAdHocSign enables ad-hoc code signing using the "-" identity. Ad-hoc signing provides basic code integrity without requiring certificates. Useful for development and testing.

func (*Config) WithAppGroups

func (c *Config) WithAppGroups(groups ...string) *Config

WithAppGroups adds app group identifiers for sharing data between sandboxed apps. Requires Sandbox permission. Use reverse-DNS format (e.g. "group.com.example.shared").

func (*Config) WithAppName

func (c *Config) WithAppName(name string) *Config

WithAppName sets the application name for the bundle. If empty, defaults to the executable name.

func (*Config) WithAutoSign

func (c *Config) WithAutoSign() *Config

WithAutoSign enables automatic detection and use of Developer ID certificates. macgo will search for and use an available Developer ID Application certificate.

func (*Config) WithCodeSigning

func (c *Config) WithCodeSigning(identity string) *Config

WithCodeSigning enables code signing with the specified identity. Use "Developer ID Application" for automatic identity selection.

func (*Config) WithCustom

func (c *Config) WithCustom(entitlements ...string) *Config

WithCustom adds custom entitlements not covered by Permission constants. Use full entitlement identifiers (e.g. "com.apple.security.device.capture").

func (*Config) WithCustomArray

func (c *Config) WithCustomArray(key string, values ...string) *Config

WithCustomArray adds a custom entitlement with a string array value. Use for entitlements that require an array instead of a boolean (e.g. "com.apple.developer.applesignin" with values ["Default"]).

func (*Config) WithCustomString

func (c *Config) WithCustomString(key, value string) *Config

WithCustomString adds a custom entitlement with a string value. Use for entitlements that require a string instead of a boolean (e.g. "com.apple.application-identifier" or "com.apple.developer.team-identifier").

func (*Config) WithDebug

func (c *Config) WithDebug() *Config

WithDebug enables debug logging.

func (*Config) WithDevMode

func (c *Config) WithDevMode() *Config

WithDevMode enables development mode where a stable wrapper exec's the original binary. This preserves TCC permissions across rebuilds since the wrapper's signature stays stable. Use this when actively developing and rebuilding frequently.

func (*Config) WithIcon

func (c *Config) WithIcon(path string) *Config

WithIcon sets the path to an .icns file to use as the app icon. The icon is copied into the bundle's Resources directory.

func (*Config) WithInfo

func (c *Config) WithInfo(key string, value interface{}) *Config

WithInfo adds a custom key/value pair to the Info.plist.

func (*Config) WithPermissions

func (c *Config) WithPermissions(perms ...Permission) *Config

WithPermissions adds macOS system permissions to request. Permissions are additive - multiple calls append to the list.

func (*Config) WithPostCreateHook

func (c *Config) WithPostCreateHook(fn func(bundlePath string, cfg *Config) error) *Config

WithPostCreateHook sets a function to run after the bundle is created but before code signing. This allows injecting additional files (helper binaries, LaunchDaemon plists) into the bundle's Contents/ directory.

func (*Config) WithProvisioningProfile

func (c *Config) WithProvisioningProfile(path string) *Config

WithProvisioningProfile sets the path to a provisioning profile to embed in the bundle. The profile will be copied to Contents/embedded.provisionprofile. Required for entitlements like keychain-access-groups.

When present, com.apple.application-identifier and com.apple.developer.team-identifier are automatically extracted from the profile and injected as string entitlements. Values set explicitly via WithCustomString take precedence over auto-derived values.

func (*Config) WithSingleProcess

func (c *Config) WithSingleProcess() *Config

WithSingleProcess enables single-process mode: codesign in-place, re-exec, and call setActivationPolicy. No app bundle is created. Only works for entitlement-only permissions (Accessibility, Virtualization, Network); TCC-dialog permissions (Camera, Microphone, Location) require app bundles.

func (*Config) WithUIMode

func (c *Config) WithUIMode(mode UIMode) *Config

WithUIMode sets how the app appears in the macOS UI. Options: UIModeBackground (default), UIModeAccessory, UIModeRegular

func (*Config) WithUsageDescription

func (c *Config) WithUsageDescription(key, description string) *Config

WithUsageDescription sets a usage description string for a specific permission. This is required for permissions like Accessibility, Camera, etc. to trigger prompts. Example: WithUsageDescription("NSAccessibilityUsageDescription", "Needed to inspect UI")

type Error

type Error struct {
	Op   string // Operation that failed (e.g., "create bundle", "code sign")
	Err  error  // Underlying error
	Help string // Actionable guidance for the user
}

Error represents a macgo error with additional context and actionable guidance.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Permission

type Permission = permissions.Permission

Permission represents a macOS system permission that can be requested. These correspond to TCC (Transparency, Consent, Control) permission types. This is an alias for permissions.Permission for convenience.

Example (Constants)
package main

import (
	"github.com/tmc/macgo"
)

func main() {
	// All available permission constants
	permissions := []macgo.Permission{
		macgo.Camera,     // Camera access
		macgo.Microphone, // Microphone access
		macgo.Location,   // Location services
		macgo.Files,      // File access
		macgo.Network,    // Network access
		macgo.Sandbox,    // App sandbox
	}

	err := macgo.Request(permissions...)
	if err != nil {
		// Handle error
		return
	}
	// App has all permissions
}

type UIMode

type UIMode = bundle.UIMode

UIMode controls how the app appears in the macOS UI.

Directories

Path Synopsis
cmd
lsopen command
Command lsopen is a pure Go replacement for /usr/bin/open on macOS.
Command lsopen is a pure Go replacement for /usr/bin/open on macOS.
macgo command
Command macgo provides signing diagnostics, bundle inspection, and code signing for macOS app bundles.
Command macgo provides signing diagnostics, bundle inspection, and code signing for macOS app bundles.
macgo-cleanup command
Package main provides a utility to find and kill orphaned xpcproxy processes.
Package main provides a utility to find and kill orphaned xpcproxy processes.
examples
env-dump command
env-dump: Simple test app that writes environment variables to a file Used to verify whether `open --env` passes env vars to .app bundles
env-dump: Simple test app that writes environment variables to a file Used to verify whether `open --env` passes env vars to .app bundles
mouse-click command
mouse-click: Perform a mouse click at absolute screen coordinates using CGEvent APIs By default, smoothly moves to the target, clicks, and returns to original position
mouse-click: Perform a mouse click at absolute screen coordinates using CGEvent APIs By default, smoothly moves to the target, clicks, and returns to original position
mouse-move command
mouse-move: Move mouse cursor with natural human-like movement using CGEvent APIs
mouse-move: Move mouse cursor with natural human-like movement using CGEvent APIs
sigquit-test command
sigquit-test is a simple program to test SIGQUIT stack dumps with macgo.
sigquit-test is a simple program to test SIGQUIT stack dumps with macgo.
internal
bundle
Package bundle provides macOS app bundle creation and management functionality.
Package bundle provides macOS app bundle creation and management functionality.
launch
Package launch provides application launching strategies for macOS app bundles.
Package launch provides application launching strategies for macOS app bundles.
launchservices
Package launchservices provides pure Go bindings for the macOS LaunchServices private SPI _LSOpenURLsWithCompletionHandler, the same function that /usr/bin/open uses internally.
Package launchservices provides pure Go bindings for the macOS LaunchServices private SPI _LSOpenURLsWithCompletionHandler, the same function that /usr/bin/open uses internally.
plist
Package plist provides utilities for generating macOS property list files.
Package plist provides utilities for generating macOS property list files.
system
Package system provides internal system-level utilities for macgo.
Package system provides internal system-level utilities for macgo.
tcc
Package tcc provides TCC (Transparency, Consent, Control) permission utilities.
Package tcc provides TCC (Transparency, Consent, Control) permission utilities.
Package sysprefpane provides functions to open macOS System Settings panes.
Package sysprefpane provides functions to open macOS System Settings panes.
Package teamid provides Apple Developer Team ID detection and validation.
Package teamid provides Apple Developer Team ID detection and validation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL