matchlock

repository·main·Indexed 20 days ago

https://github.com/jingkaihe/matchlock

A CLI tool and SDK suite for running AI agents in isolated, ephemeral microVMs. Matchlock provides security through network allowlisting and a MITM proxy for secret injection, ensuring sensitive credentials never enter the guest environment. It supports the Agent Client Protocol (ACP) and includes examples for running agents like Claude Code and Codex in sandboxed environments.

Tokens
50.1K
Snippets
170
Records
227
Agent score
70%

What's inside matchlock

  1. Distinguish between Host Rules and SDK-Local Hooks

    main

    Matchlock uses two different enforcement paths for VFS interception. Choosing the right one depends on whether you want to block the underlying filesystem operation or the SDK's API call.

    Host Wire Rules (action=block)

    These rules are sent to the sandbox and evaluated inside the host VFS interception. Use these to enforce security at the filesystem level (e.g., preventing a process from creating a file even if it bypasses the SDK).

    SDK-Local Callbacks

    These run within your SDK process. Matching is based on the SDK API operation:

    • WriteFile or WriteFileMode maps to op=write
    • ReadFile maps to op=read
    • ListFiles maps to op=readdir

    Important: An action_hook with ops=[create] will not match a WriteFile call. To block the creation of a file, use a host wire rule with action=block and ops=[create]. To block SDK write calls directly, use an action_hook with ops=[write].

  2. Understand the Matchlock architecture

    main

    Matchlock operates using a Host-to-VM communication model. The Host contains the Matchlock CLI, a Policy Engine, a Transparent Proxy (with TLS MITM capabilities), and a VFS Server. The VM (running on Firecracker or Virtualization.framework) contains a Guest Agent, a FUSE-based /workspace, and any OCI Image (e.g., Alpine, Ubuntu).

    Communication between the Host and the VM occurs over vsock:

    • Proxy to Agent: vsock :5000
    • VFS to FUSE: vsock :5001
  3. How Matchlock manages VM lifecycle and resource cleanup

    main

    Matchlock uses a persistent lifecycle state machine to track VM progress and ensure resources are cleaned up even if a sandbox shutdown fails (e.g., due to process crashes or network issues).

    Lifecycle Phases

    VMs transition through several primary phases:

    • Success Path: creating $\rightarrow$ created $\rightarrow$ starting $\rightarrow$ running $\rightarrow$ stopping $\rightarrow$ cleaning $\rightarrow$ cleaned.
    • Failure Phases: create_failed, start_failed, stop_failed, and cleanup_failed.

    Resource Ownership

    Matchlock tracks specific resources to ensure deterministic cleanup:

    • VM State Directory: ~/.matchlock/vms/<vm-id>/
    • Rootfs Copy: rootfs.ext4 located within the VM state directory.
    • Subnet Allocation: Managed via the subnet_allocations table in the state database.
    • Linux Network Artifacts: TAP interfaces (fc-<suffix>) and nftables tables (matchlock_<tap>, matchlock_nat_<tap>).

    On macOS and non-Linux platforms, only the subnet and rootfs are reconciled; network artifact reconciliation is a no-op.

  4. Understand Recursion and Safety in VFS Hooks

    main

    When using VFS interception callbacks, be aware of how recursion is handled:

    • hook callbacks: These are after-only and run with recursion suppression enabled. This prevents infinite loops if the hook itself triggers another intercepted operation.
    • dangerous_hook callbacks: These are after-only and bypass recursion suppression. Use these with caution, as they allow performing actions (like client.Exec) that could trigger further intercepted events.
    • Automatic Event Emission: When any SDK after-event callbacks (hook or dangerous_hook) are present, event emission is automatically enabled for interception.
  5. How the Agent Client Protocol (ACP) Example works

    main

    This example demonstrates running an AI coding agent (kodelet) inside a matchlock micro-VM sandbox using the Agent Client Protocol (ACP).

    Workflow:

    1. A host application (like a Streamlit app or a TUI) spawns matchlock run --image <image_name> -i -- as a subprocess.
    2. The host application acts as an ACP Client, communicating with the guest process via stdin/stdout pipes.
    3. Inside the micro-VM, the agent (kodelet acp) processes prompts and tool calls.
    4. Outbound HTTPS traffic from the VM is intercepted by a MITM Proxy inside the matchlock environment. This proxy injects secrets (like ANTHROPIC_API_KEY) and enforces host allowlists, ensuring sensitive credentials never enter the VM itself.
  6. Configure Network Policies and Secrets

    main

    Matchlock allows fine-grained control over network access and secure secret injection.

    Network Access

    • Allowlist: Use .allow_host(host1, ...) to restrict access to specific domains.
    • Private IPs: Control access to private IP ranges (10.x, 172.16.x, 192.168.x) using:
      • .block_private_ips() or .with_block_private_ips(True) to block.
      • .allow_private_ips() or .with_block_private_ips(False) to allow.
      • .unset_block_private_ips() to reset to default (which blocks private IPs if a network config is sent).
    • Offline Mode: Use .with_no_network() for a sandbox with no guest NIC or egress.

    Secret Injection

    Use .add_secret(key, value, host) to inject sensitive data. The Matchlock MITM proxy ensures the real value is only provided when the request is directed to the specified host.

    import os
    from matchlock import Client, Sandbox
    
    sandbox = (
        Sandbox("python:3.12-alpine")
        .allow_host("api.anthropic.com", "pypi.org", "files.pythonhosted.org")
        .with_network_mtu(1200)
        .with_block_private_ips(True)
        .add_secret("ANTHROPIC_API_KEY", os.environ["ANTHROPIC_API_KEY"], "api.anthropic.com")
    )
    
    with Client() as client:
        client.launch(sandbox)
        result = client.exec("python3 call_api.py")
    
        # Forward local 18080 -> guest 8080
        bindings = client.port_forward("18080:8080")
        print(bindings[0].address, bindings[0].local_port, bindings[0].remote_port)
  7. Understand environment forwarding in the Claude Code example

    main

    When running the Claude Code example, certain local Git configurations are automatically forwarded into the Matchlock sandbox to maintain context.

    Forwarded Git settings:

    • user.name
    • user.email
    • core.editor

    These are only forwarded if they are available in your local environment.

  8. Understand VFS Interception and the Rule Model

    main

    VFS interception allows you to inspect and control filesystem operations on mounted guest paths from the host side. Rules are defined by three main components:

    1. phase: Determines when the rule triggers.
      • before: Occurs before the operation is executed. Supports action=block, action_hook callbacks, and mutate_hook callbacks.
      • after: Occurs after the operation is executed. Supports hook and dangerous_hook callbacks.
    2. ops: An operation filter (e.g., create, write, read, readdir).
    3. path: A filepath-style glob (e.g., /workspace/*).
  9. Understand the VFS Interception and Hook Model

    main

    Matchlock implements Virtual File System (VFS) interception as host-side middleware. This allows you to intercept filesystem operations (like Stat, Open, ReadAt, WriteAt, etc.) for logging, fault injection, or custom behavior without modifying the guest protocol.

    Interception is split into two distinct hook types to balance security and performance:

    1. Policy Hooks (Synchronous/Inline): Used for immediate decisions and mutations. These must be fast as they are on the critical path.
      • Allow/Block: Decide if an operation is permitted.
      • Mutation: Modify request payloads (e.g., overriding the content of a write operation).
    2. Side-effect Hooks (Asynchronous/Out-of-band): Used for non-blocking tasks.
      • Logging and Notifications: Sending telemetry or external alerts.
      • Hook-triggered Exec: Running external commands. To prevent deadlocks, these are only allowed from asynchronous after hooks, never from synchronous before hooks.
  10. Python SDK Execution Modes

    main

    The Python SDK supports three distinct execution styles for interacting with processes via Matchlock:

    1. exec_stream: Used for live streaming of stdout and stderr.
    2. exec_pipe: Provides bidirectional communication via stdin, stdout, and stderr without using a PTY (Pseudo-Terminal).
    3. exec_interactive: Provides an interactive shell experience using PTY semantics.

    Note on Interactive Mode: The exec_interactive mode requires a real POSIX TTY. In non-interactive environments, the SDK will skip interactive mode but continue to function for exec_stream and exec_pipe.

  11. How Matchlock Boot Sandboxes Work with OverlayFS

    main

    Matchlock uses an overlay root assembly model to boot sandboxes, providing a consistent logical model across Linux and macOS. Instead of copying a monolithic rootfs, Matchlock assembles the filesystem at boot time using a combination of read-only layers and a writable upper layer.

    The Boot Components

    1. Bootstrap Root Disk (vda): A minimal disk containing the /init bootstrap logic and early userspace tools required for mounting and pivot_root.
    2. Read-Only Layers (lowerdirs): The N number of OCI image layers attached as read-only block devices.
    3. Writable Upper Disk (upperdir/workdir): A per-VM, sparse, growable ext4 disk that stores all runtime changes.

    Boot Lifecycle

    1. Preparation: A writable upper disk is created. Runtime-specific files (e.g., matchlock guest runtime binaries, network CA certs) are injected into this upper disk.
    2. Attachment: The read-only layer disks and the writable upper disk are attached to the VM.
    3. Mapping: Layer and upper disk mappings are passed to the guest via kernel command-line parameters.
    4. Assembly: The guest's /init process mounts the lowerdirs and the upper/work directories, assembles the kernel overlayfs root, performs pivot_root, and continues the standard guest-init flow.
  12. How Secret Injection works in Matchlock

    main

    Matchlock uses a transparent MITM (Man-in-the-Middle) proxy to inject secrets securely. When you use the --secret KEY@HOST flag:

    1. The real API key never enters the VM.
    2. Inside the VM, the environment variable contains a placeholder (e.g., SANDBOX_SECRET_a1b2c3d4...).
    3. When the application (like Kodelet) makes an API call to the designated HOST (e.g., api.anthropic.com), the host-side proxy replaces the placeholder with the real key in-flight.

    Security Benefits:

    • The real key is never visible inside the sandbox.
    • Even if the agent is compromised, the key cannot be exfiltrated because it is only injected for requests to the specific designated host.