Dutis

repository·master·Indexed 42 days ago

https://github.com/tsonglew/dutis

A comprehensive macOS application File Extension Manager and default app setter written in Rust. Dutis allows users to manage associations for filename extensions, Uniform Type Identifiers (UTIs), MIME types, and URL schemes via a keyboard-first TUI, a CLI for automation, or declarative TOML configuration files. It includes features for snapshot rollbacks, configuration drift monitoring via LaunchAgents, and an MCP server for AI agent integration.

Tokens
33.8K
Snippets
109
Records
155
Agent score
53%

What's inside Dutis

  1. Overview of Dutis

    master

    Dutis is a Rust-based application designed for macOS to inspect and manage default application handlers. It allows users to manage associations for:

    • Filename extensions (e.g., .txt, .pdf)
    • Uniform Type Identifiers (UTIs)
    • MIME types
    • URL schemes
    • Launch Services roles (e.g., viewer, editor, shell)

    Key capabilities include a keyboard-first TUI for manual changes, a CLI for automation (with JSON output and dry runs), and support for declarative configuration via TOML policies.

  2. How Dutis identifies application handler capabilities

    master

    Dutis identifies handler registrations by inspecting Info.plist records within application bundles.

    Document Types:

    • CFBundleDocumentTypes containing CFBundleTypeExtensions, LSItemContentTypes, or CFBundleTypeMIMETypes are treated as registrations.
    • CFBundleTypeRole determines the capability. If omitted, it defaults to all. Supported roles are viewer, editor, shell, or all.
    • An explicit CFBundleTypeRole = None or an unknown role is ignored.

    URL Types:

    • CFBundleURLTypes containing CFBundleURLSchemes are treated as registrations with the role all.

    Role Logic:

    • An editor declaration satisfies a viewer query.
    • An all query returns every matching declaration.
    • editor and shell capabilities are independent; one does not imply the other.
  3. The Dutis Workflow: Inspect, Plan, and Apply Changes

    master

    Dutis manages macOS Launch Services associations (extensions, UTIs, MIME types, and URL schemes) using a strict safety boundary. To prevent accidental or unauthorized system changes, follow this lifecycle:

    1. Inspect (Read-Only): Check current readiness and effective policies. Use handler get to inspect existing associations and handler query to discover apps that register specific kinds and roles. Use handler defaults to see the native Launch Services role matrix.
    2. Plan: If a change is needed, express multi-target changes as version 2 TOML. Build a fresh plan and run a policy check. Treat policy violations or unresolved selectors as stop conditions.
    3. Dry-Run & Review: Show the user the changed kinds, identifiers, roles, target bundle IDs, and the plan digest. Always run a dry-run before requesting approval.
    4. Approval: Ask for explicit approval immediately before any write operation. Never infer approval from previous inspection steps.
    5. Apply: Apply the reviewed digest. Include a meaningful requester identity and report the resulting safety snapshot and audit IDs.
    6. Verify: Confirm the change with dutis get and use dutis audit to view the local record.
  4. Configure Dutis approval modes

    master

    The approval_mode setting in your policy determines how mutations are authorized:

    • explicit: Requires interactive user confirmation or the --yes CLI flag.
    • token: Requires a secret token. The policy must contain the SHA-256 digest of the secret in the approval_token_sha256 field. The plaintext secret is provided via the DUTIS_APPROVAL_TOKEN environment variable.
    • deny: Disables all mutations while still allowing policy inspection.

    To generate a digest for a token:

    printf %s 'your-random-secret' | shasum -a 256
    printf %s 'your-random-secret' | shasum -a 256
  5. Understand the HTTP event request contract

    master

    When the adapter forwards an event, it follows these rules:

    • Payload: Accepts one JSON event on stdin (up to 1 MiB).
    • Method: Sends an HTTPS POST with Content-Type: application/json.
    • Headers: Sets X-Dutis-Event-Id, X-Dutis-Event-Type, and Idempotency-Key. It also optionally sets the Authorization: Bearer ... header.
    • Security: Restricts curl to HTTPS, does not follow redirects, and disables the user's ambient .curlrc.
    • Error Handling: Discards response bodies and exits with a non-zero status for HTTP or transport failures.
    • Idempotency: Because retries can occur if a response is lost, consumers must use the event ID or Idempotency-Key to deduplicate requests.

    Credentials and endpoints are written to a temporary, owner-readable curl configuration file and a temporary event body file, both of which are removed immediately after curl exits.

  6. Understand the architecture of Dutis

    master
    Dutis is built with a modular design that separates application scanning and plist parsing from the interactive user flow. It operates as a macOS native tool by interacting with application bundles and macOS Launch Services via the duti utility. The application uses anyhow for comprehensive error handling.
  7. Use the Command event sink

    master

    The --event-command sink executes a local, trusted executable for every event.

    Execution Model:

    • Input: Dutis writes exactly one JSON event followed by a newline to the command's stdin.
    • Environment: Dutis sets DUTIS_EVENT_ID and DUTIS_EVENT_TYPE in the child environment for routing. It removes all approval-token variables before execution, but inherits other environment variables.
    • Output: Dutis discards stdout (to protect CLI/MCP protocol streams) and captures stderr to report warnings if the command fails.
    • Synchronicity: The command runs synchronously. Handlers should be short-lived; delegate slow tasks to a background service or queue.

    Example Handler:

    #!/bin/sh
    exec /usr/bin/logger -t dutis-event

    For HTTP-based delivery, use the packaged dutis-event-http command which provides retries, timeouts, and idempotency.

  8. Understand the Dutis MCP protocol and audit contract

    master

    The Dutis MCP server follows these protocol and auditing standards:

    Protocol Details

    • Supported Versions: 2024-11-05, 2025-03-26, and 2025-06-18.
    • Output Format: Tool results contain a text representation and a structuredContent object with api_version: "1".
    • Error Handling: Tool failures set isError: true and return a stable error kind and message.

    Auditing and Logging

    • Audit Events: Every tool call emits a JSON audit event to stderr. These events include schema_version, timestamp, request_id, tool, access (e.g., write), and outcome. Crucially, audit events never include tool arguments or approval tokens.
    • Event Sinks: You can configure where lifecycle events (like dutis_drift checks) are sent using the --event-log or --event-command flags, or via the DUTIS_EVENT_LOG / DUTIS_EVENT_COMMAND environment variables.
    • Event Recovery: If an event-command fails, the event is stored in a durable outbox and can be recovered using the CLI commands dutis events pending and dutis events replay.
    {
      "schema_version": 1,
      "timestamp": "2026-08-22T00:00:00Z",
      "request_id": 7,
      "tool": "dutis_apply",
      "access": "write",
      "outcome": "success"
    }
  9. How role-aware reads and verification work

    master

    Dutis uses the native macOS Launch Services API by default when a specific role is requested via dutis handler get ... --role <role>.

    • Specific Roles: When you request --role viewer, --role editor, or --role shell, Dutis uses the native API to ensure high-fidelity verification. This is used for declarative plans, drift checks, snapshots, and post-write verification.
    • Role all: Requesting the all role uses the legacy duti read path. This maintains backward compatibility and provides additional metadata like application names and paths for filename extensions.

    Note: The native integration is read-only. All mutations (writes) still use the duti tool.

  10. Use the MCP server for AI agent integration

    master

    Dutis can be exposed via a local Model Context Protocol (MCP) server, allowing AI agents to interact with macOS associations safely.

    Security Model

    • Separation of Concerns: Read tools (list, query, get, diff) are kept separate from mutation tools (apply, rollback).
    • Controlled Mutations: Write tools do not accept free-form shell commands. Instead, they require a plan digest and an explicit approval token to execute changes.
    • Granular Permissions: Write capabilities can be disabled independently of read capabilities.
  11. Use Dutis MCP tools

    master

    Dutis provides read-only Model Context Protocol (MCP) servers. These servers expose the following tools:

    • dutis_profiles: List available profiles.
    • dutis_profile: Inspect a specific profile.
    • dutis_recommend: Generate a recommendation for a profile (e.g., { "profile": "developer" }).

    Important for Agents: MCP tools use the server's local policy and cannot accept caller-supplied policy. Agents must still perform a dutis_diff and dutis_policy_check, obtain explicit approval, and provide a freshly reviewed digest before calling any write tools.

  12. Use the JSONL event sink

    master

    The --event-log sink appends one JSON event per line to a specified file.

    Behavioral Details:

    • Permissions: Dutis creates missing parent directories with owner-only permissions and maintains the event file at mode 0600 on Unix. Existing parent-directory permissions are not modified.
    • Rotation: Log rotation must be managed externally. Because a long-running watcher opens the file for each event, it will follow the new path on the next check if the file is replaced or renamed between Dutis invocations.