Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/0xdps/default-tamer/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Default Tamer supports three types of routing rules, each designed for different use cases. Rules are evaluated in order from top to bottom, and the first matching rule determines which browser opens the URL.
// Rule.swift:10-14
enum RuleType: String, Codable, CaseIterable {
    case sourceApp = "Source App"
    case domain = "Domain"
    case urlPattern = "URL Pattern"
}

Source App Rules

Route URLs based on which application opened them.

Use Cases

Work Apps

Open all Slack links in your work browser profile

Development

Open links from VS Code or Cursor in your development browser

Email Clients

Route email links to a specific browser or profile

Chat Apps

Keep Discord/Teams links separate from personal browsing

How It Works

Source App rules match based on the bundle identifier of the application that opened the URL.
// Router.swift:73-92
private static func evaluateSourceAppRule(
    _ rule: Rule, 
    sourceApp: String?
) -> RouteAction? {
    guard let ruleAppBundleId = rule.sourceAppBundleId else {
        return nil
    }
    
    guard let sourceApp = sourceApp else {
        return nil
    }
    
    if sourceApp == ruleAppBundleId {
        return .openInBrowser(
            bundleId: rule.targetBrowserId, 
            matchedRule: rule
        )
    }
    
    return nil
}

Configuration

sourceAppBundleId
string
required
The bundle identifier of the source application (e.g., com.tinyspeck.slackmacgap for Slack)
sourceAppName
string
Human-readable name for display purposes (e.g., “Slack”)
targetBrowserId
string
required
Bundle identifier of the browser to open URLs in (e.g., com.google.Chrome)

Examples

Type: Source App
From: Slack (com.tinyspeck.slackmacgap)
To: Chrome

Matches:
✅ https://example.com (from Slack)
✅ https://github.com/user/repo (from Slack)
❌ https://example.com (from other apps)
Bundle ID Resolution: Default Tamer includes logic to dynamically resolve bundle identifiers for apps that may have multiple versions or change their bundle IDs (see Rule.swift:106-121).

Domain Rules

Route URLs based on their hostname/domain.

Match Types

Domain rules support three matching modes:
// Rule.swift:16-20
enum DomainMatchType: String, Codable {
    case exact = "Exact"
    case suffix = "Suffix"
    case contains = "Contains"
}
Match a specific domain exactly. The www. prefix is automatically normalized for convenience.
// Router.swift:116-120
case .exact:
    // Normalize both by stripping 'www.' for cleaner UX
    // This way github.com matches both github.com and www.github.com
    let normalizedHost = host.hasPrefix("www.") 
        ? String(host.dropFirst(4)) 
        : host
    let normalizedPattern = pattern.hasPrefix("www.") 
        ? String(pattern.dropFirst(4)) 
        : pattern
    matches = normalizedHost == normalizedPattern
Examples:
  • Pattern: github.com
  • ✅ Matches: github.com, www.github.com
  • ❌ Doesn’t match: gist.github.com, api.github.com

Configuration

domainPattern
string
required
The domain pattern to match (e.g., github.com, .atlassian.net, staging)
domainMatchType
enum
required
How to match the pattern: exact, suffix, or contains
targetBrowserId
string
required
Bundle identifier of the browser to open URLs in

Examples

Type: Domain
Pattern: github.com
Match Type: Exact
To: Arc

Matches:
✅ https://github.com/user/repo
✅ https://www.github.com/user/repo
❌ https://gist.github.com
❌ https://api.github.com
Normalization: Domain matching is case-insensitive. Both the URL’s host and your pattern are converted to lowercase before comparison (Router.swift:96-111).

URL Pattern Rules

Route URLs based on patterns in the full URL string.

Match Types

URL Pattern rules support two matching modes:
Match if the pattern appears anywhere in the full URL (including protocol, path, and query parameters).
// Router.swift:143-147
if let contains = rule.urlContains?.lowercased() {
    if urlString.contains(contains) {
        return .openInBrowser(
            bundleId: rule.targetBrowserId, 
            matchedRule: rule
        )
    }
}
Examples:
  • Pattern: /admin
  • ✅ Matches: https://example.com/admin, https://app.com/admin/users
  • ❌ Doesn’t match: https://example.com/user

Configuration

urlContains
string
Substring to search for in the full URL (case-insensitive)
urlRegex
string
Regular expression pattern to match against the full URL
targetBrowserId
string
required
Bundle identifier of the browser to open URLs in
You can configure either urlContains or urlRegex, or both. If both are set, urlContains is evaluated first.

Examples

Type: URL Pattern
Contains: /admin
To: Chrome

Matches:
✅ https://example.com/admin
✅ https://example.com/admin/users
✅ https://example.com/app/admin?tab=settings
❌ https://example.com/user
❌ https://admin.example.com (in domain, not path)

Rule Properties

All rules share common properties:
// Rule.swift:22-51
struct Rule: Identifiable, Codable, Hashable {
    let id: UUID
    var type: RuleType
    var enabled: Bool
    var targetBrowserId: String
    var openInPrivateMode: Bool

    // Match criteria (only one set used based on type)
    var sourceAppBundleId: String?
    var sourceAppName: String?

    var domainPattern: String?
    var domainMatchType: DomainMatchType?

    var urlContains: String?
    var urlRegex: String?
}
id
UUID
Unique identifier for the rule
type
RuleType
required
The type of rule: sourceApp, domain, or urlPattern
enabled
boolean
default:"true"
Whether the rule is active. Disabled rules are skipped during evaluation.
targetBrowserId
string
required
Bundle identifier of the browser to open matched URLs in
openInPrivateMode
boolean
default:"false"
Whether to open URLs in private/incognito mode (browser support varies)

Combining Rules

Strategy: Place specific rules before general ones. More specific rules should appear higher in your list to prevent general rules from capturing URLs unintentionally.

Example: Layered Routing

# ✅ CORRECT ORDER: Specific to general

# 1. Handle work chat (most specific - by source app)
Source App: Slack → Chrome (Work Profile)

# 2. Handle specific work domains (specific domains)
Domain: .company.com (suffix) → Chrome (Work Profile)
Domain: .atlassian.net (suffix) → Chrome (Work Profile)

# 3. Handle development (path-based)
URL Pattern: /admin (contains) → Chrome (Admin Profile)

# 4. Handle personal GitHub (personal account)
Domain: github.com (exact) → Arc

# 5. Everything else goes to fallback browser

Example: Wrong Order

# ❌ INCORRECT ORDER: General rule blocks specific ones

# 1. This catches ALL .com domains!
Domain: .com (suffix) → Safari

# 2. These never get evaluated because rule #1 matches first
Domain: github.com (exact) → Arc
Source App: Slack → Chrome
URL Pattern: /admin (contains) → Chrome
Remember: First match wins. Rules are evaluated top-to-bottom, and evaluation stops at the first matching rule.

Routing Engine

Learn how rules are evaluated and routing decisions are made

Browser Detection

How Default Tamer discovers and manages browsers

Build docs developers (and LLMs) love