How to Set Up and Use the ipswd Daemon for Remote REST API Operations

The ipswd daemon transforms the ipsw command-line tool into a lightweight RESTful HTTP service, enabling remote machines and CI pipelines to execute firmware analysis operations over HTTP or Unix domain sockets.

The ipswd daemon is part of the blacktop/ipsw open-source project, providing a server mode that exposes core ipsw functionality through a structured REST API. By running the daemon, you can offload IPSW scanning, symbolication, and AEA lookup tasks to a dedicated service, allowing distributed systems to interact with Apple firmware data without local command-line installation.

Architecture of the ipswd Daemon

Understanding the daemon's internal structure helps troubleshoot configuration issues and extend functionality.

Command Entry Point

The binary entry point resides in cmd/ipswd/main.go, which simply invokes cmd.Execute() to register the CLI command tree.

// cmd/ipswd/main.go
cmd.Execute()

This lightweight main function delegates all operational logic to subcommands, following standard Go CLI patterns.

Start Command Implementation

The start subcommand creates the daemon instance and initiates the server. In cmd/ipswd/cmd/start.go, the RunE function instantiates the daemon and calls its Start() method:

// cmd/ipswd/cmd/start.go
daemon := daemon.NewDaemon()
daemon.Start()

This sequence triggers the full initialization chain: configuration loading, database setup, and HTTP server creation.

Daemon Core Orchestration

The internal/daemon/daemon.go file contains the primary orchestration logic, wiring together three critical components:

  1. Configuration – Loaded from ~/.config/ipsw/config.yml (or a custom path via --config)
  2. Database layer – Initialized through setupDB(), supporting SQLite, PostgreSQL, or in-memory storage
  3. HTTP server – Constructed by api/server/server.go

The Start() method coordinates these dependencies, ensuring the database connection is established before the server begins accepting requests.

Server Configuration

The HTTP server configuration is defined in api/server/server.go through a struct that specifies:

  • Host and port (default 127.0.0.1:3993)
  • Unix socket path (optional alternative to TCP)
  • Debug mode flag
  • Log file destination
  • PEM database path for AEA operations
  • Kernel symbol signatures directory

API Routing Structure

Routes are registered under the /v1 prefix, with specific handlers located in api/server/routes/. Key endpoints include:

  • /v1/syms/scan – Kernel symbol scanning
  • /v1/aea/* – AEA lookup operations
  • /v1/version – Server version information

Configuration and Setup

Proper configuration ensures the daemon listens on the correct interface and maintains persistent data where required.

Configuration File Location

The daemon searches for config.yml in ~/.config/ipsw/ by default. You can override this with the --config flag. The repository provides a reference template in config.example.yml:

daemon:
  host: 127.0.0.1
  port: 3993
  debug: false
  log_file: /var/log/ipswd.log

Copy this template to the config directory and modify values to match your deployment environment.

Database Options

The setupDB() function in daemon.go supports three storage backends:

  • SQLite – File-based storage suitable for single-node deployments
  • PostgreSQL – Network database for high-availability setups
  • In-memory – Volatile storage for testing or ephemeral workloads

Configure the driver in config.yml under the database section. If omitted, the daemon may initialize an SQLite file automatically in the config directory.

Starting and Managing the Daemon

Once configured, launch the service using the CLI.

Starting the Service

Execute the start command to initialize the daemon:

ipswd start

This command reads the configuration, establishes database connections, and binds the HTTP server to the configured address.

Checking Daemon Status

Verify the service is operational by querying the version endpoint:

curl http://localhost:3993/v1/version

Alternatively, use the built-in status command:

ipswd status

Using the REST API

With the daemon running, clients can execute ipsw operations via HTTP requests.

Scanning IPSW Files

To scan an IPSW file for kernel symbols remotely, send a POST request to the scan endpoint:

http POST http://localhost:3993/v1/syms/scan path=./iPhone15_2.ipsw

The daemon processes the file using the same logic as the local ipsw syms scan command, returning JSON data containing discovered kernels, build numbers, and firmware keys.

Unix Socket Communication

For local inter-process communication without TCP overhead, configure a Unix socket in config.yml:

daemon:
  socket: /tmp/ipsw.sock
  debug: true

After restarting the daemon, communicate through the socket using tools like socat or custom clients. The server detects the non-empty socket field and switches from TCP to Unix domain socket mode automatically.

Programmatic API Example

Fetch version information programmatically using curl and jq:

curl -s http://localhost:3993/v1/version | jq .

Expected response:

{
  "api_version": "v1.0",
  "os_type": "linux",
  "builder_version": "0.5.2"
}

Summary

  • The ipswd daemon exposes ipsw CLI functionality through HTTP endpoints defined in api/server/routes/
  • Configuration resides in ~/.config/ipsw/config.yml and supports both TCP and Unix socket listeners
  • The daemon supports SQLite, PostgreSQL, and in-memory databases via setupDB() in internal/daemon/daemon.go
  • Default service address is 127.0.0.1:3993, with all routes prefixed by /v1
  • Remote operations mirror local commands, enabling CI pipelines to execute firmware analysis without local tool installation

Frequently Asked Questions

What is the default listening address for ipswd?

By default, the daemon binds to 127.0.0.1:3993 as defined in the server configuration struct within api/server/server.go. You can override this by specifying host and port values in your config.yml or by setting the socket parameter to use Unix domain sockets instead.

How do I run ipswd with a Unix socket instead of TCP?

Set the socket field in your configuration file to a valid filesystem path, such as /tmp/ipsw.sock. When daemon.Start() detects a non-empty socket value, it creates a Unix domain socket listener rather than a TCP socket, eliminating network overhead for local clients.

Which database backends does ipswd support?

The daemon supports three storage options through the setupDB() function: SQLite for file-based persistence, PostgreSQL for enterprise deployments requiring concurrent access, and in-memory storage for testing environments. Configure your preferred driver in the database section of config.yml.

How do I verify the daemon is running correctly?

Query the health check endpoint using curl http://localhost:3993/v1/version or execute ipswd status from the command line. A successful response returns JSON containing the API version and builder version, confirming that the daemon initialized correctly in internal/daemon/daemon.go and is actively serving requests.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →