Skip to content

Latest commit

 

History

History
282 lines (192 loc) · 8.72 KB

File metadata and controls

282 lines (192 loc) · 8.72 KB

Nexus Toolkit for Rust

concerns nexus-toolkit-rust repo

This library exports useful functionality to streamline the development of Nexus Tools in Rust. It is mainly used by Tool developers to bootstrap their efforts to extend the Nexus ecosystem.

This documentation will go over the main features of the library and how to use them.

Installation

Using the CLI run the $ nexus tool new --help command to see the available options. This command creates a fresh Rust project with the necessary dependencies to get started.

Alternatively, you can add the following to your Cargo.toml file:

[dependencies.nexus-toolkit]
git = "https://github.com/Talus-Network/nexus-sdk"
tag = "v1.0.0"
package = "nexus-toolkit"

Exports

trait nexus_toolkit::NexusTool

If using nexus-toolkit, NexusTool is the trait that must be implemented by the tool developer. It defines functions that define the Tool interface, metadata, health and the main logic.


NexusTool::new

Tells the nexus-toolkit how to create a new instance of the Tool. This is where you can initialize dependencies, especially those that need to be injected for testing.

The new function takes no arguments and is called on every request. Current design of Nexus Tools is for them to be stateless.

use nexus_toolkit::*;

struct HttpStatus {
    client: reqwest::Client,
}

impl NexusTool for HttpStatus {
    // ...
    async fn new() -> Self {
        Self {
            client: reqwest::Client::new(),
        }
    }
    // ...
}

NexusTool::Input

This associated type defines the input that the Tool expects. This type must derive the serde::Deserialize and schemars::JsonSchema traits.

The Tool's input schema is then derived from this type via the schemars::schema_for! macro.

use nexus_toolkit::*;

#[derive(Deserialize, JsonSchema)]
struct Input {
    url: String,
}

struct HttpStatus;

impl NexusTool for HttpStatus {
    type Input = Input;
    // ...
}

NexusTool::Output

This associated type defines the output that the Tool produces. This type must derive the serde::Serialize and schemars::JsonSchema traits.

The Tool's output schema is then derived from this type via the schemars::schema_for! macro.

To comply with Nexus Workflow output variants, the output schema must include a top-level oneOf. This is also enforced by the Tool's runtime and achievable in Rust simply by using an enum.

use nexus_toolkit::*;

#[derive(Serialize, JsonSchema)]
enum Output {
    Ok { status: u16 },
    Err { reason: String },
}

struct HttpStatus;

impl NexusTool for HttpStatus {
    type Output = Output;
    // ...
}

NexusTool::fqn

Defines the Tool's fully qualified name. This is used to uniquely identify the Tool in the Nexus ecosystem. Read more about FQNs in the Nexus Tool documentation.

use nexus_toolkit::*;

impl NexusTool for HttpStatus {
    // ...
    fn fqn() -> ToolFqn {
        fqn!("com.example.http-status@1")
    }
    // ...
}

NexusTool::path

Defines the Tool's path relative to its webserver. The Toolkit allows for multiple tools to run on the same server, so this is used to differentiate between them.

This defaults to the root route.

NexusTool::health

Defines the Tool's health check. This is a simple function that returns a anyhow::Result<warp::http::StatusCode>. The Tool is considered healthy if this function returns Ok(StatusCode::OK).

The health check should check for the health of dependent services and return an error if they are not healthy.

use nexus_toolkit::*;

struct HttpStatus;

impl NexusTool for HttpStatus {
    // ...
    async fn health(&self) -> AnyResult<StatusCode> {
        Ok(StatusCode::OK)
    }
    // ...
}

NexusTool::invoke

Defines the Tool's main logic. This is where the Tool processes the input and produces the output.

use nexus_toolkit::*;

struct HttpStatus;

impl NexusTool for HttpStatus {
    // ...

    /// Fetches the HTTP status of a given URL.
    async fn invoke(&self, input: Self::Input) -> Self::Output {
        let response = reqwest::Client::new().get(&input.url).send().await;

        match response {
            Ok(response) => Output::Ok { status: response.status().as_u16() },
            Err(e) => Output::Err { reason: e.to_string() },
        }
    }
    // ...
}

{% hint style="info" %} Notice that the invoke function does not return a Result. This is because errors are valid output variants of a Nexus Tool. The invoke function should handle any errors and return them as part of the output. {% endhint %}


NexusTool::authorize (optional)

authorize is an optional hook that runs after the request has been authenticated via signed HTTP (when enabled).

It receives an AuthContext that includes the verified Leader node identity (invoker_id), key id (invoker_kid), request validity window (iat_ms/exp_ms), nonce, and the HTTP target (method/path/query).

Use it for Tool-side policy such as:

  • allowing only specific Leader nodes,
  • rate limiting by Leader node id,
  • gating sensitive functionality.

If authorize returns an error, the runtime returns 403 (and the response is still signed after authentication).

{% hint style="info" %} If signed HTTP is disabled, authorize is not invoked. {% endhint %}


nexus_toolkit::bootstrap!

The bootstrap! macro hides away the boilerplate code needed to create the underlying HTTP server that adheres to the Nexus Tool interface.

It has a flexible interface that accepts an Into<SocketAddr> value and a struct that impl NexusTool.

If using the bootstrap! macro without the Into<SocketAddr> argument, a BIND_ADDR environment variable can be provided. This variable needs to be a string that can .parse::<SocketAddr>.

use nexus_toolkit::*;

// ...

/// Bootstrap a single Tool at 127.0.0.1:8080.
#[tokio::main]
async fn main() {
    bootstrap!(MyTool)
}

/// Bootstrap multiple Tools at 127.0.0.1:8080.
///
/// When defining multiple Tools, their `NexusTool::path` must be unique.
#[tokio::main]
async fn main() {
    bootstrap!([MyTool, MyOtherTool])
}

/// Bootstrap a single Tool at a custom address.
#[tokio::main]
async fn main() {
    bootstrap!(([0, 0, 0, 0], 8081), MyTool)
}

/// Bootstrap multiple Tools at a custom address.
///
/// When defining multiple Tools, their `NexusTool::path` must be unique.
#[tokio::main]
async fn main() {
    bootstrap!(([0, 0, 0, 0], 8081), [MyTool, MyOtherTool])
}

Signed HTTP and HTTPS

Nexus expects off-chain Tools to be reachable over HTTPS and to require signed HTTP for POST /invoke.

  • HTTPS provides transport security; Leader nodes validate Tool certificates using the system root trust store (similar to curl).
  • TLS authenticates the Tool endpoint to the Leader node. Nexus Leader nodes do not currently present client certificates when calling Tools, so Tools cannot authenticate callers at the TLS layer (no mTLS client authentication today).
  • Signed HTTP provides application-layer request/response signatures so both sides can verify provenance and prevent replay.
    • Signed HTTP is the authentication mechanism for /invoke: Tools authenticate the calling Leader node by verifying the request signature, and Leader nodes authenticate Tool responses by verifying the response signature.

The toolkit runtime itself is an HTTP server. Run it behind a TLS terminator (reverse proxy / load balancer).

Signed HTTP is enabled via a JSON config file loaded from NEXUS_TOOLKIT_CONFIG_PATH. This config contains:

  • a local allowed_leaders.json allowlist file (Leader node ids + public keys), and
  • the Tool’s signing key + tool_kid (key id) used to sign responses.

{% hint style="info" %} Signed HTTP is enforced only when signed_http.mode = "required". If signed HTTP is disabled, POST /invoke accepts unsigned requests. {% endhint %}

{% hint style="info" %} Signed HTTP verification happens after the TLS handshake. If you want to reduce unwanted TLS handshakes/traffic, apply policy at your TLS terminator (rate limiting, firewall/WAF, mTLS, or private ingress such as Cloudflare Tunnel). {% endhint %}

{% hint style="info" %} Nexus currently relies on system-root certificate validation and signed HTTP for Tool authentication. In a future update, Nexus will support self-signed certificates and TLS client authentication (mTLS) for Tool communication. {% endhint %}

For a full end-to-end setup guide (TLS termination options + key registration + runtime config), see: