GitHub - Matzielab/HostsLab: hosts file configuration electron app

5 min read Original article ↗

A macOS desktop app for managing your /etc/hosts file and ~/.ssh/config. Built with Electron, React, and TypeScript.

HostsLab provides a visual interface to manage host entries and SSH connections without manually editing config files. Two tabs give you access to both tools from a single app.

HostsLab Demo

Features

Hosts Tab

  • View all entries from /etc/hosts in a table
  • Add new host mappings
  • Edit existing entries (IP and hostname)
  • Toggle entries on/off (comments/uncomments the line)
  • Delete entries
  • System entries are protected from accidental modification
  • Preserves comments, blank lines, and formatting in the hosts file
  • Backs up the original hosts file before the first modification
  • Elevated permissions via native macOS password prompt

SSH Config Tab

  • View all host entries from ~/.ssh/config in a table
  • Add, edit, and delete SSH connection entries
  • Fields: Host (alias), HostName (IP/domain), User, Port, IdentityFile
  • Dropdown shows detected SSH keys from ~/.ssh/
  • Create new SSH keys (Ed25519, RSA 4096, ECDSA) directly from the app
  • No sudo required — user owns ~/.ssh/config
  • Creates ~/.ssh/ directory and config file if they don't exist
  • Sets proper file permissions (700 for directory, 600 for config and private keys)
  • Backs up original config on first edit
  • Preserves comments, unknown directives, and wildcard Host blocks

How It Works

Architecture

The app follows Electron's process model with a clear separation between main and renderer:

Renderer (React UI)
  -- IPC bridge -->
Main Process (Node.js)
  -- reads/writes -->
/etc/hosts          (via sudo)
~/.ssh/config       (direct)
~/.ssh/*            (key scan + ssh-keygen)
  • Renderer process only sees typed objects (HostEntry[], SSHEntry[]) — it never touches raw files
  • Main process parses config files line-by-line, caching the full structure to preserve comments, blank lines, and formatting between saves
  • IPC channels:
    • hosts:read — returns HostEntry[]
    • hosts:save(entries) — writes to /etc/hosts via elevated cp, returns {success, error?}
    • ssh:read — returns SSHEntry[]
    • ssh:save(entries) — writes to ~/.ssh/config directly, returns {success, error?}
    • ssh:listKeys — scans ~/.ssh/ for private keys, returns SSHKey[]
    • ssh:createKey(options) — runs ssh-keygen, returns {success, keyPath?}

Hosts File Parsing

  • Blank lines and # comments are preserved as-is
  • Lines like # 127.0.0.1 example.com are treated as disabled entries
  • Multi-hostname lines (e.g. 127.0.0.1 foo bar) are split into separate entries
  • System entries (localhost, broadcasthost, ::1 localhost) are marked read-only

SSH Config Parsing

  • Parses Host blocks with indented key-value directives
  • Recognized directives: HostName, User, Port, IdentityFile
  • Wildcard hosts (Host *) are preserved in the file but not shown as editable entries
  • Unknown directives and comments are preserved on save

Saving

Hosts file:

  1. Serializes entries by walking the cached line structure
  2. Writes to a temp file
  3. Uses osascript with do shell script ... with administrator privileges to copy the temp file to /etc/hosts with proper permissions
  4. Re-reads the file to keep the cache in sync
  5. A backup of the original file is saved to the app's user data directory on first save

SSH config:

  1. Serializes entries back into SSH config format (Host blocks with indented directives)
  2. Writes directly to ~/.ssh/config with mode 600
  3. Re-reads the file to keep the cache in sync
  4. A backup is saved to the app's user data directory on first save

SSH Key Detection

  • Scans ~/.ssh/ for private key files on app load
  • Detects known key types: id_rsa, id_ed25519, id_ecdsa, *.pem
  • Also detects any file with an OPENSSH PRIVATE KEY or PRIVATE KEY header
  • Skips .pub files, known_hosts, config, and authorized_keys

SSH Key Creation

  • Generates keys via ssh-keygen with no passphrase
  • Supports Ed25519 (recommended), RSA 4096, and ECDSA
  • Sets permissions: 600 for private key, 644 for public key
  • Refreshes the key list automatically after creation

Project Structure

src/
  types.ts                  # Shared types (HostEntry, SSHEntry, SSHKey, ElectronAPI, etc.)
  index.ts                  # Main process entry — creates window, registers IPC
  preload.ts                # Exposes electronAPI via contextBridge
  renderer.ts               # Renderer entry — loads CSS and app
  app.tsx                   # React root mount
  index.html                # HTML shell

  main/
    hosts-file.ts           # Parse, serialize, backup, save hosts file
    ssh-config.ts           # Parse, serialize, backup, save SSH config + key management
    ipc-handlers.ts         # ipcMain.handle for all IPC channels

  components/
    App.tsx                 # Top-level layout, tab navigation, state orchestration
    HostsTable.tsx          # Hosts entries table
    HostRow.tsx             # Single host row with toggle/edit/delete
    EntryForm.tsx           # Hosts add/edit modal form with validation
    SSHTable.tsx            # SSH entries table
    SSHRow.tsx              # Single SSH row with edit/delete
    SSHEntryForm.tsx        # SSH add/edit modal form with key dropdown
    SSHKeyModal.tsx         # Create new SSH key modal
    Toast.tsx               # Toast notifications
    EmptyState.tsx          # Empty state message

  hooks/
    useHosts.ts             # Hosts CRUD state management + IPC calls
    useSSH.ts               # SSH CRUD state management + key operations + IPC calls
    useToast.ts             # Toast state management

  styles/
    app.css                 # All app styles

Development

Prerequisites

  • Node.js (v18+)
  • npm
  • macOS (the elevated save uses osascript)

Setup

Run

This launches the app in development mode with hot reload for the renderer process. Changes to main process files require a restart.

Build

Produces a packaged .app in the out/ directory.

Creates distributable installers (.dmg and .zip on macOS). Builds for both Intel (x64) and Apple Silicon (arm64).

Lint

Releases

Releases are built automatically via GitHub Actions. To create a new release:

git tag v1.0.0
git push origin v1.0.0

This triggers a workflow that:

  1. Builds the app on macOS for both Intel (x64) and Apple Silicon (arm64)
  2. Signs and notarizes the app with Apple via App Store Connect API key
  3. Produces .dmg and .zip artifacts for each architecture
  4. Creates a GitHub Release with auto-generated release notes and all downloadable artifacts

Download the latest release from the Releases page.

License

MIT