Skip to content

Instantly share code, notes, and snippets.

@xrl
Last active March 25, 2026 20:49
Show Gist options
  • Select an option

  • Save xrl/c07ab302cfefe4a94b91c16c99c55f93 to your computer and use it in GitHub Desktop.

Select an option

Save xrl/c07ab302cfefe4a94b91c16c99c55f93 to your computer and use it in GitHub Desktop.
Using Finicky to route URLs to different Firefox profiles (with the new profile system)

Using Finicky to Route URLs to Different Firefox Profiles (2026)

Background: What is Finicky?

Finicky is a macOS app that acts as your default browser. When you click a link anywhere on your system (Slack, email, terminal, etc.), Finicky intercepts it, evaluates rules from ~/.finicky.js, and opens the URL in the right browser — or the right profile of a browser.

This is useful when you work across multiple organizations and want to keep sessions, cookies, and bookmarks separated.

Finicky's official documentation covers the profile property for Chromium browsers only (e.g. browser: { name: "Google Chrome", profile: "Work" }). Firefox profile routing is undocumented, and the args property that makes it possible isn't mentioned in the wiki at all. This guide fills that gap.

The Goal

Route URLs to specific Firefox profiles based on the domain or path:

open "https://github.com/orgA"  →  Firefox Profile "Work"
open "https://github.com/orgB"  →  Firefox Profile "Personal"

The Problem: Firefox Changed Its Profile System

Recent versions of Firefox introduced a new profile management system backed by SQLite. Profiles you create through Firefox's UI are stored in:

~/Library/Application Support/Firefox/Profile Groups/<storeId>.sqlite

You can inspect these with:

sqlite3 ~/Library/Application\ Support/Firefox/Profile\ Groups/*.sqlite "SELECT * FROM Profiles;"

Which shows something like:

1|Profiles/usht2jg6.default-release|Original profile|paw-print|...
2|Profiles/abc12345.Profile 1|Work|sparkle-single|...
3|Profiles/def67890.Profile 2|Personal|heart-rate|...

However, Firefox's command-line flags (-P, --profile) still use the old profile system defined in:

~/Library/Application Support/Firefox/profiles.ini

If you created your profiles through Firefox's new UI, they may not appear in profiles.ini at all — meaning the -P ProfileName flag silently does nothing.

However, the --profile /full/path/to/directory flag bypasses both systems entirely — it just tells Firefox "use this directory." The working Finicky solution in this guide uses --profile with a full path, so it doesn't depend on profiles.ini or the SQLite database at all.

Step 1 (Optional): Backport Profiles to profiles.ini

Note: This step is not required for the Finicky solution below, which uses --profile /full/path. But it's useful if you want -P ProfileName to work for other CLI tools, scripts, or the old profile chooser dialog (firefox -P).

Check what's currently in your profiles.ini:

cat ~/Library/Application\ Support/Firefox/profiles.ini

You'll likely see only the default profiles. You need to add entries for your new profiles. Find the StoreID from the SQLite filename (e.g., afc1a1c0.sqliteStoreID=afc1a1c0) and add sections like:

[Profile2]
Name=Work
IsRelative=1
Path=Profiles/abc12345.Profile 1
StoreID=afc1a1c0

[Profile3]
Name=Personal
IsRelative=1
Path=Profiles/def67890.Profile 2
StoreID=afc1a1c0

Note: Firefox may reorder sections when it next writes to this file. That's fine.

Step 2: Verify the CLI Works

Before touching Finicky, confirm Firefox respects the --profile flag when called directly. You need the full path to the profile directory — find it by listing what's in your Profiles folder:

ls ~/Library/Application\ Support/Firefox/Profiles/

Then test:

/Applications/Firefox.app/Contents/MacOS/firefox \
  --profile ~/Library/Application\ Support/Firefox/Profiles/abc12345.Profile\ 1 \
  "https://example.com"

This should open the URL in the correct profile. Since --profile takes a direct path to the profile directory, it works regardless of what's in profiles.ini or the new SQLite database.

Important: The direct binary call works, but open -a Firefox --args --profile ... does not when Firefox is already running. macOS Launch Services ignores --args for an already-running app. This distinction is critical.

Step 3: The Working Finicky Configuration

After extensive debugging (see below), here's the configuration that actually works with Finicky v4.2.2 and modern Firefox:

// ~/.finicky.js
export default {
  defaultBrowser: "Firefox",
  handlers: [
    {
      match: ["github.com/orgA*", "*.orgA.com/*", "orgA.com/*"],
      browser: (url) => ({
        name: "/Applications/Firefox.app",
        appType: "path",
        args: [
          "-n",
          "--args",
          "--profile",
          "/Users/YOUR_USERNAME/Library/Application Support/Firefox/Profiles/abc12345.Profile 1",
          url.href,
        ],
      }),
    },
    {
      match: ["github.com/orgB*", "*.orgB.com/*", "orgB.com/*"],
      browser: (url) => ({
        name: "/Applications/Firefox.app",
        appType: "path",
        args: [
          "-n",
          "--args",
          "--profile",
          "/Users/YOUR_USERNAME/Library/Application Support/Firefox/Profiles/def67890.Profile 2",
          url.href,
        ],
      }),
    },
  ],
};

Why This Specific Incantation?

The resulting shell command is:

open -a /Applications/Firefox.app -n --args --profile '/path/to/profile' https://example.com

Each piece matters:

Fragment Why
open -a /Applications/Firefox.app Finicky always uses open. The -a flag and app path come from name + appType: "path"
-n Critical. Forces open to launch a new instance. Without this, open hands off to the already-running Firefox which ignores --args entirely
--args Separates open flags from Firefox flags. Everything after this is passed to Firefox as argv
--profile '/path/to/profile' Tells Firefox which profile directory to use. Must be the full absolute path
url.href The URL to open. Must be passed explicitly in args when using a browser function

Why -n Appears in args But Acts as an open Flag

This is the key trick. Finicky constructs the command like this:

open -a <name> [args...]

When args contains "--args", Finicky doesn't add its own --args (to avoid doubling). So our args array ["-n", "--args", "--profile", ...] gets appended directly after -a /Applications/Firefox.app. Since -n appears before --args, macOS open interprets it as its own flag (new instance), not as a Firefox argument.

What Doesn't Work (and Why)

browser: { name: "Firefox", profile: "Work" }

Finicky v4.2.2 has a built-in profile property that's supposed to handle this automatically. It reads profiles.ini, finds the matching profile name, and adds -P ProfileName to the command.

But it doesn't work because browsers.json (embedded in the v4.2.2 binary) only contains Chromium-based browsers. Firefox entries were added to main after the v4.2.2 release but haven't shipped yet. The profile resolution silently fails and the profile flag is never added.

You can verify by checking the debug log — you'll see profile: "Work" in "Final browser options" but the "Run command" line will just be open -a Firefox https://example.com with no profile flag.

open -a Firefox --args -P Work https://example.com

This is what you'd expect to work. And it does — if Firefox isn't already running. When Firefox is already running, macOS open -a finds the existing instance and just sends it the URL via Apple Events, completely ignoring --args.

open -a Firefox -n --args -P Work https://example.com

Adding -n forces a new instance, which should pass --args through. But -P Work triggers Firefox's profile chooser dialog instead of directly opening the named profile. This appears to be a Firefox bug or behavior change with the new profile system.

--profile /full/path (without -n)

open -a /Applications/Firefox.app --args --profile '/path/to/profile' https://example.com

Same problem as above — --args is ignored when Firefox is already running.

Debugging Guide

Key Files

File Purpose
~/.finicky.js Your Finicky configuration
~/Library/Application Support/Firefox/profiles.ini Old profile system (used by CLI flags)
~/Library/Application Support/Firefox/Profile Groups/*.sqlite New profile system (used by Firefox UI)
~/Library/Application Support/Firefox/Profiles/ Actual profile data directories
~/Library/Logs/Finicky/ Finicky debug logs (when logRequests: true)
~/Library/Caches/Finicky/ Cached/bundled config — check config_cache_*.json to verify Finicky picked up your changes

Enable Debug Logging

Add logRequests: true to your config:

export default {
  defaultBrowser: "Firefox",
  options: {
    logRequests: true,
  },
  handlers: [ ... ],
};

Then check the log files in ~/Library/Logs/Finicky/. The Finicky UI only shows INFO level, but the log files include DEBUG messages — most importantly the "Run command" line showing the exact shell command being executed.

Finicky v4 API Changes

If you're migrating from v3, note these breaking changes:

  • urlString is removed. The browser function receives the URL object directly as the first argument: browser: (url) => ({ ... }). Use url.href for the string.
  • { url } destructuring doesn't work. The first argument is the URL, not an object containing it. Use (url) => not ({ url }) =>.
  • match is required. Typos like beepmatch cause a silent validation error and Finicky falls back to the default browser.

Testing Without Finicky

Always test the open command directly in terminal first to isolate whether the problem is Finicky or Firefox:

# This should work (direct binary call):
/Applications/Firefox.app/Contents/MacOS/firefox \
  --profile "/path/to/profile" \
  "https://example.com"

# This is what Finicky needs to generate:
open -a /Applications/Firefox.app -n \
  --args --profile "/path/to/profile" \
  "https://example.com"

Inspecting the New Profile System

# Find the SQLite database
ls ~/Library/Application\ Support/Firefox/Profile\ Groups/

# List all profiles in the new system
sqlite3 ~/Library/Application\ Support/Firefox/Profile\ Groups/*.sqlite \
  "SELECT id, path, name FROM Profiles;"

Relevant Issues

  • johnste/finicky#90 — "Support for Firefox profiles" (closed). Long history of workarounds. The --profile + wrapper script approach originated here.
  • johnste/finicky#431 — "Args not working on latest version" (closed). Documents the -n flag requirement and how open -a ignores --args for already-running apps.
  • johnste/finicky#211 — "Finicky + Firefox Containers" (open). Different from profiles — containers require a Firefox extension.

Environment

  • macOS (Sequoia / Darwin 25.x)
  • Firefox 137+ (with new profile system)
  • Finicky v4.2.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment