Fountain Coach Gitowner-controlled · read only

toolsmith.git · README.md

toolsmith.git / README.md

revision 4903a757ff71b3ebdbafc44fe1ff9564346c9916 · complete file

# FountainToolsmith

**FountainToolsmith** is a stand‑alone Swift Package Manager (SPM) library that
extracts the tool orchestration components from the original FountainAI project.
It provides a clean, reusable package for driving external command‑line tools in secure containers, exposing both library and command‑line interfaces.

## Use Cases

FountainToolsmith solves the problem of executing arbitrary tools in a
predictable and observable manner. Typical scenarios include:

* Integrating third‑party tools (e.g. media converters, format
  transformers) into an AI pipeline without exposing them to the
  network.
* Running untrusted executables in an isolated sandbox while capturing
  logs and tracing information.
* Delegating tool invocation to a separate service (the Tools Factory) and controlling it from Swift via generated client
  stubs.
* Building a custom command‑line interface on top of Toolsmith’s API.

## Installation

Add FountainToolsmith to your SPM dependencies in `Package.swift`:

```swift
.package(url: "https://github.com/Fountain-Coach/toolsmith-package.git", from: "1.0.0"),
```

Then import the modules you need:

```swift
import Toolsmith
import SandboxRunner
```

The package supports macOS 14 and later and depends only on `swift-crypto`.

## Quick Start

Here is a minimal example demonstrating how to run an `echo` command in a sandboxed environment and capture its output:

```swift
import Toolsmith
import SandboxRunner
import Foundation

let work = FileManager.default.temporaryDirectory.appendingPathComponent("work")
try FileManager.default.createDirectory(at: work, withIntermediateDirectories: true)

let toolsmith = Toolsmith()
let runner = BwrapRunner()

defer { try? FileManager.default.removeItem(at: work) }

let requestID = toolsmith.run(tool: "echo") {
    let result = try runner.run(
        executable: "/bin/echo",
        arguments: ["hello"],
        inputs: [],
        workDirectory: work,
        allowNetwork: false,
        timeout: 5,
        limits: nil
    )
    print(result.stdout)
}

print("Ran tool with request ID", requestID)
```

See [docs/index.md](docs/index.md) for more examples.

## VM-first execution model

Toolsmith prefers to execute tools inside a managed virtual machine (VM)
whenever a compatible image is available in the manifest. On the first
run the image is hydrated into `.toolsmith/cache/<image>/<version>` and
its SHA-256 digest is checked before the VM is booted. Subsequent runs
reuse the cached image as long as the digest still matches the manifest.

The VM exposes a command channel back to the host process for dispatching
tool invocations, streaming status updates, and delivering shutdown
signals. By default the workspace is mounted read-only; if a tool needs
to export artifacts you can opt into writable mounts via the adapters in
`Sources/Toolsmith/Adapters/`.

### Distributing the VM image via GHCR (OCI)

You can publish the QCOW2 as an OCI artifact on GitHub Container Registry (GHCR) and let Toolsmith hydrate it with the `oras` CLI:

- Publish
  - Compute the digest of the final QCOW2: `sha256sum image.qcow2`
  - Push with ORAS (example):
    - `oras login ghcr.io -u $GITHUB_ACTOR -p $GHCR_TOKEN`
    - `oras push ghcr.io/OWNER/REPO:VERSION image.qcow2:application/vnd.fountain.toolsmith.qcow2 \
        --annotation org.opencontainers.image.title=image.qcow2 \
        --artifact-type application/vnd.fountain.toolsmith.qcow2`
  - Record the digest in your `.toolsmith/tools.json` as `image.qcow2_sha256`.

- Consume
  - Set `image.qcow2` in the manifest to `oci://ghcr.io/OWNER/REPO:VERSION` and `image.qcow2_sha256` to the QCOW2 SHA-256.
  - Ensure `oras` is installed and on PATH (or set `TOOLSMITH_ORAS` to its path).
  - For private artifacts, set `GHCR_USERNAME` and `GHCR_TOKEN` (or `GITHUB_ACTOR` + `GHCR_TOKEN`).

Toolsmith detects the `oci://` scheme and runs `oras pull` under the hood, then verifies the file’s SHA‑256 before booting the VM.

### Overriding execution placement

You can switch between host and VM execution without code changes by
setting the `TOOLSMITH_EXECUTION` environment variable:

```bash
export TOOLSMITH_EXECUTION=host   # force host execution
export TOOLSMITH_EXECUTION=vm     # force VM execution
```

Individual `Toolsmith.run` calls accept an `execution:` parameter so you
can override placement per invocation:

```swift
try toolsmith.run(tool: "ffmpeg", execution: .host) { context in
    // Runs on the host even if VM mode is enabled globally
}
```

This override is useful when mixing trusted helper binaries with
untrusted workloads in the same process.

## Package Modules

| Module             | Description                                                      |
|--------------------|------------------------------------------------------------------|
| **ToolsmithSupport** | Internal support types: logging, cryptographic helpers, metadata structures. |
| **Toolsmith**      | High‑level API for orchestrating tool runs with spans and logging. |
| **SandboxRunner**  | Concrete runners such as `BwrapRunner` and `QemuRunner` that execute commands in isolated sandboxes. |
| **ToolsmithAPI**   | Generated client for the Tools Factory HTTP service. |
| **toolsmith-cli**  | A command‑line interface that wraps `ToolsmithAPI` for user‑friendly interactions. |

## Documentation

Full documentation is available in the [docs directory](docs/index.md). For
details on publishing versioned VM images alongside source releases, see the
[release process guide](docs/release-process.md).

---
© 2025 Contexter alias Benedikt Eickhoff 🛡️ All rights reserved.