---
title: Cross-Resource Commands are Handshakes
description: Why Mirage treats cross-resource commands like cp, mv, and diff as handshakes, two resources meeting through a single gesture.
icon: handshake
---

## Two Resources Meeting Through One Gesture

<Icon icon="hand-sparkles" /> **[Gestures](/home/design/gestures)** usually act on one resource at a time. A **handshake** is the special case where a single gesture reaches across *two* resources and makes them cooperate.

- `cp /gdrive/data.csv /s3/data.csv`, Google Drive and S3 coordinate to move a file
- `mv /ram/scratch.txt /disk/archive.txt`, in-memory and local disk hand off
- `diff /s3/a.txt /github/b.txt`, two resources both produce bytes for comparison
- `cat /s3/a.txt /github/b.txt`, aggregator; both feed the same output stream

In Arm-metaphor terms: two Arms are reaching at once, and their Hands meet, one coordinated motion, two endpoints, two different <Icon icon="eye" /> **[Eyes](/home/design/eyes)** being touched.

## Why Handshakes Are Special

A normal gesture like `cat /s3/data.csv` is a single-handed motion: one resource, one <Icon icon="hand-point-up" /> **[Finger](/home/design/fingers)** (`read`), one result.

A handshake requires two resources to cooperate. That means:

- **Two fingers, possibly of different kinds.** `cp /s3/a /gdrive/b` is `read` on S3 plus `write` on GDrive.
- **A meeting point.** Bytes stream from one resource through Mirage into the other. A direct resource-to-resource path (e.g., S3→S3 server-side copy) is a future optimization; the default is stream-through.
- **A shared failure model.** If one side breaks mid-transfer, the gesture must clean up on both sides without leaving partial state.

## Handshake Types

| Type            | When it fires                        | Example                             | Constraint                                                      |
| --------------- | ------------------------------------ | ----------------------------------- | --------------------------------------------------------------- |
| **Transfer**    | Write gesture across resources       | `cp`, `mv`                          | Destination must support `write`; `mv` also needs `delete` on source |
| **Comparison**  | Two reads feeding one comparison     | `diff`, `cmp`                       | Both sides need `read` only, works for every resource          |
| **Aggregation** | Many reads, one merged output stream | `cat`, `grep`, `wc` across mounts   | All sides need `read` only, works for every resource           |

Transfer handshakes own both sides of the motion. Comparison and aggregation handshakes only read, the output lives in Mirage, not in the resources.

### Asymmetric Resources

Not every resource can play both sides of a transfer. A read-only resource (today: Google Drive) can be the *source* of `cp` but never the *destination*, and can never participate in `mv` at all (since `mv` deletes the source after writing the destination). The dispatcher detects this at execution time and surfaces a clean failure rather than corrupting state. Aggregation and comparison handshakes are unaffected, they only need reads.

## How Mirage Handles Them

Handshakes are implemented as cross-mount handlers inside the command dispatcher. When a gesture receives paths that span resources, Mirage:

1. **Groups paths by resource** so each side's finger work can run locally.
2. **Picks a handler**, a registered cross-mount handler for the specific `(src, dst)` pair, or a generic aggregator across resources, or an error if no handshake exists for this gesture.
3. **Coordinates the transfer** as one streaming pipeline, with cleanup on failure.

See [Commands, Cross-Resource Dispatch](/home/design/commands#cross-resource-dispatch) for the concrete dispatch rules and the current cross-mount gesture list.

## Why Handshakes Are Still One Motion

The handshake metaphor lands because the two resources are both within reach of the same <Icon icon="user" /> **[Arm](/home/design/arm)**. A resource is an Eye the Arm can reach toward; a Hand at the end of that reach is what actually touches it. A handshake is the Arm (or two Arms, if you prefer the symmetric read) performing one coordinated motion whose endpoints happen to live on two different resources.

Without this framing, cross-resource commands feel like a special case bolted on. With it, they are just the gesture the Arm performs when two of its reachable Eyes need to exchange what they see.

## Tested Matrix

`tests/integration/test_cross_mount_matrix.py` exercises every handshake type across 14 ordered `(src, dst)` pairs spanning RAM, disk, Redis, S3, and Google Drive, 126 parametrized assertions in total. The matrix confirms both the working combinations and that the unsupported ones (e.g. `cp` into Google Drive, `mv` touching any read-only end) fail cleanly rather than leaving partial state behind.

## Related

- [Gestures](/home/design/gestures), single-resource commands, of which handshakes are a variant.
- [Commands, Cross-Resource Dispatch](/home/design/commands#cross-resource-dispatch), the concrete dispatch rules.
- [Fingers](/home/design/fingers), the `read`/`write` ops a handshake composes.
- [Eyes](/home/design/eyes), the resources a handshake reaches across.
