# hand

Run Code Mode cells and tools on another machine, a hand, for a runtime that keeps inference and durability.

> [!NOTE]
> The `nanocode::hand` module exists only on native targets.

`nanocode::hand::connect` and `nanocode::hand::listen` return a `Hand` with Claude's file and shell tools plus the workspace tools for one directory. `Hand.run` serves the brain until the brain revokes or replaces the hand. The model then selects a hand per tool call.

```rust title="main.rs"
type Result<T> =
    std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
    // 1. Read the brain URL and the shared token
    let url = std::env::args()
        .nth(1)
        .ok_or("usage: hand ws://BRAIN:PORT")?;
    let token = std::env::var("NANOCODE_RUNNER_TOKEN")?;

    // 2. Dial the brain and run tools in the current directory
    nanocode::hand::connect(url, token, std::env::current_dir()?)?
        .name("build-box")
        .run(|event| eprintln!("{event:?}"))
        .await?;
    Ok(())
}
```

## Signature

```rust
// Each function builds the hand's tools for directory
pub fn tools(
    directory: impl Into<PathBuf>,
) -> Result<Tools, NanocodeError>;
pub fn connect(
    url: impl Into<String>,
    token: impl Into<String>,
    directory: impl Into<PathBuf>,
) -> Result<Hand, NanocodeError>;
pub fn listen(
    address: SocketAddr,
    token: impl Into<String>,
    directory: impl Into<PathBuf>,
) -> Result<Hand, NanocodeError>;
```

## Functions

### tools

* **Type:** `fn tools(directory) -> Result<Tools, NanocodeError>`

Builds the catalog that a hand serves: Claude's file and shell tools and the workspace tools, all rooted at `directory`. `connect` and `listen` call it, and `Hand::connect` takes a custom catalog.

```rust
use nanocode::hand::{Hand, tools};

// Builds the default catalog for a hand made with Hand::connect
let hand =
    Hand::connect(url, token, tools("/srv/app")?).directory("/srv/app");
```

### connect

* **Type:** `fn connect(url, token, directory) -> Result<Hand, NanocodeError>`

Makes a hand that dials a brain at a `ws://` or `wss://` URL. If the connection drops, the hand redials with a backoff.

```rust
// Dials a brain that runs RunnerServerConfig::listen("127.0.0.1:9000")
let hand =
    nanocode::hand::connect("ws://127.0.0.1:9000", token, "/srv/app")?;
```

### listen

* **Type:** `fn listen(address, token, directory) -> Result<Hand, NanocodeError>`

Makes a hand that waits for a brain to dial it. A plaintext listener requires a loopback address, so remote brains reach it through a TLS proxy.

```rust
// Waits for a brain built with
// .runner(Runner::connect("ws://127.0.0.1:9000"))
let hand = nanocode::hand::listen(
    "127.0.0.1:9000".parse()?,
    token,
    "/srv/app",
)?;
```

## Hand

The configured hand. Each builder method returns the hand.

### name

* **Type:** `impl Into<String>`
* **Default:** the machine's host name, normalized

The name that the brain and the model use to select this hand. `Hand::machine_name()` returns the default.

```rust
// Names the hand for the model
let hand = nanocode::hand::connect(url, token, ".")?.name("build-box");
```

### directory

* **Type:** `impl Into<PathBuf>`
* **Default:** the current directory

The directory that tools start in. `run` resolves it to a canonical path.

```rust
// Runs tools in another checkout
let hand = Hand::connect(url, token, tools).directory("/srv/other");
```

### policy

* **Type:** `SocketPolicy`
* **Default:** `SocketPolicy::runner()`

The frame size and request timeout of the connection. `SocketPolicy` has the public fields `max_frame_bytes` and `request_timeout`. The default uses the WebSocket default message size and a 40-second timeout.

```rust
use nanocode::hand::SocketPolicy;

// Accepts frames up to 8 MiB and times out after 60 seconds
let policy = SocketPolicy {
    max_frame_bytes: 8 << 20,
    request_timeout: std::time::Duration::from_secs(60),
};
let hand = nanocode::hand::connect(url, token, ".")?.policy(policy);
```

### run

* **Type:** `fn run(self, events: impl FnMut(HandEvent) + Send + 'static) -> BoxFuture<'static, Result<(), CodeModeHostError>>`

Serves the brain and reports each connection change to `events`. The future resolves after `HandEvent::Revoked` or `HandEvent::Replaced`, or with an error that retrying cannot fix, such as a token that the brain rejects with HTTP 401 or 403.

```rust
use nanocode::hand::HandEvent;

// Logs only connection changes
nanocode::hand::connect(url, token, ".")?
    .run(|event| match event {
        HandEvent::Connected { endpoint } => {
            eprintln!("connected to {endpoint}")
        }
        HandEvent::Retrying { delay } => {
            eprintln!("retrying in {delay:?}")
        }
        _ => {}
    })
    .await?;
```

## HandEvent

| Variant                   | Meaning                                                       |
| ------------------------- | ------------------------------------------------------------- |
| `Connecting { endpoint }` | The hand is dialing the brain.                                |
| `Connected { endpoint }`  | The hand is connected and serving.                            |
| `Listening { endpoint }`  | A listening hand is bound and waits for a brain.              |
| `BrainConnected { peer }` | A brain connected to a listening hand.                        |
| `Disconnected { reason }` | The connection ended.                                         |
| `Retrying { delay }`      | The hand dials again after `delay`.                           |
| `Revoked`                 | The brain revoked the hand, and `run` returns.                |
| `Replaced`                | Another hand attached under the same name, and `run` returns. |

```rust
use nanocode::hand::HandEvent;

// HandEvent is non-exhaustive, so a match needs a wildcard arm
fn describe(event: &HandEvent) -> &'static str {
    match event {
        HandEvent::Revoked | HandEvent::Replaced => "stopped",
        _ => "running",
    }
}
```

## HandEndpoint

`HandEndpoint::Connect(String)` holds the URL that a hand dials, and `HandEndpoint::Listen(SocketAddr)` holds the address that it listens on.

```rust
use nanocode::hand::HandEndpoint;

// Describes a hand that dials a brain
let endpoint = HandEndpoint::Connect("ws://127.0.0.1:9000".into());
```

## Errors

### NanocodeError

`tools`, `connect`, and `listen` return `NanocodeError::InvalidRequest` when the tools cannot use `directory`. `run` returns a `CodeModeHostError` for an invalid name, policy, or directory, a non-loopback listen address, or a rejected token.

```rust
// Reports a directory that the file tools cannot use
if let Err(error) = nanocode::hand::connect(url, token, "/missing") {
    eprintln!("{error}");
}
```
