From 078605d66990839af0c7076236364361592264cd Mon Sep 17 00:00:00 2001 From: kouks <3684840+kouks@users.noreply.github.com> Date: Thu, 12 Mar 2026 18:37:31 +0000 Subject: [PATCH 1/2] Update docs from nexus-sdk (commit: b83530a6ad93144c3453373ae23fccc2b687668d) --- nexus-sdk/cli.md | 119 +++--- nexus-sdk/client.md | 64 ++-- nexus-sdk/guides/dag-construction.md | 36 +- nexus-sdk/guides/llm-openai-chat-prep-tool.md | 31 +- .../guides/math-branching-dag-builder.md | 7 +- nexus-sdk/guides/math-branching-dag-entry.md | 26 +- nexus-sdk/guides/math-branching-quickstart.md | 1 - nexus-sdk/guides/math-branching-with-chat.md | 24 +- nexus-sdk/guides/onchain-tool-development.md | 48 +-- nexus-sdk/guides/setup.md | 88 +---- nexus-sdk/guides/tool-communication.md | 362 ++++++++++++++++++ nexus-sdk/guides/tool-firewall.md | 33 +- nexus-sdk/index.md | 2 +- nexus-sdk/tool-development.md | 20 + nexus-sdk/toolkit-rust.md | 74 +++- 15 files changed, 649 insertions(+), 286 deletions(-) create mode 100644 nexus-sdk/guides/tool-communication.md diff --git a/nexus-sdk/cli.md b/nexus-sdk/cli.md index f5c3801..592c336 100644 --- a/nexus-sdk/cli.md +++ b/nexus-sdk/cli.md @@ -1,6 +1,6 @@ # Nexus CLI -> concerns [`nexus-cli` repo](https://github.com/Talus-Network/nexus-sdk/tree/main/cli) +> concerns [`nexus-cli` repo][nexus-cli-repo] The Nexus CLI is a set of tools that is used by almost all Actors in the Nexus ecosystem. @@ -38,7 +38,7 @@ Validate an off-chain Nexus Tool on the provided URL. This command checks whethe As an improvement, the command could take a `[data]` parameter that invokes the Tool and checks the response against the output schema. {% endhint %} -This command should also check that the URL is accessible by the Leader node. It should, however, be usable with `localhost` Tools for development purposes, printing a warning. +This command should also check that the URL is accessible by Leader nodes. For local testing, it should still be usable with `localhost` Tools, printing a warning. --- @@ -116,6 +116,70 @@ This command requires that a wallet is connected to the CLI... --- +**`nexus tool auth`** + +Commands for operating signed HTTP (message signatures) for off-chain Tools. + +Signed HTTP is Nexus’ application-layer authentication for `POST /invoke`: + +- Leader node → Tool requests are signed so the Tool can verify which Leader node is calling and prevent replay. +- Tool → Leader node responses are signed so the Leader node can verify provenance and bind a response to a specific request. + +These commands help Tool operators: + +- generate an Ed25519 Tool message-signing keypair, +- register/rotate the Tool’s message-signing public key on-chain (Network Auth), and +- export a local `allowed_leaders.json` file consumed by the Tool runtime for request verification (no RPC at runtime). + +See also: + +- [Tool Communication (HTTPS + Signed HTTP)](guides/tool-communication.md) + +--- + +**`nexus tool auth keygen [--out ]`** + +Generates a new Ed25519 Tool message-signing keypair. + +- If `--out` is provided, writes a JSON file containing `private_key_hex` and `public_key_hex`. +- You will use the private key in the Tool runtime config (`tool_signing_key`). +- You will register the public key on-chain via `register-key`. + +--- + +**`nexus tool auth register-key --tool-fqn --signing-key [--owner-cap ] [--description ] ...gas`** + +Registers (or rotates) the Tool’s message-signing key in the on-chain Network Auth registry. + +- Requires an `OwnerCap` (the tool ownership cap) to prove Tool identity. +- Requires a proof-of-possession signature so the chain can verify the registrant controls the private key. +- Returns the registered `tool_kid` (key id) which must match the Tool runtime config. + +If `--owner-cap` is omitted, the CLI will try to use the OwnerCap saved in the CLI config for that Tool. + +--- + +**`nexus tool auth export-allowed-leaders (--all | --leader
...) --out `** + +Exports a local allowlist file (JSON) of permitted Leader nodes and their active signing keys. + +Use `--all` to export entries for all leaders registered in `network_auth` (recommended), or pass one or more `--leader` capability IDs. + +This file is consumed by the Rust toolkit runtime (`allowed_leaders_path`) so the Tool can verify signed requests without performing Sui RPC calls at runtime. + +--- + +**`nexus tool auth sync-allowed-leaders --out [--interval ] [--once]`** + +Continuously syncs an `allowed_leaders.json` file from on-chain `network_auth` (polling). + +- `--interval` accepts human durations like `500ms`, `5s`, `2m`, `1h` (default: `30s`). +- Use `--once` to sync a single time and exit. + +This is useful for running as a sidecar next to a Tool so leader allowlists stay up-to-date without restarting the Tool. + +--- + **`nexus tool list`** List all Nexus Tools available in the Tool Registry. This reads the dynamic object directly from Sui. @@ -172,8 +236,6 @@ The input `` is a JSON string with the following structure: - Each top-level value is an object and its keys refer to the _input port names_ of each vertex (this object can be empty if the vertex has no input ports) - Values of the second-level object are the data that should be passed to each input port -Data for encrypted ports are automatically encrypted before being sent on-chain. - The `--inspect` argument automatically triggers `nexus dag inspect-execution` upon submitting the execution transaction. The `--remote` argument accepts a list of `{vertex}.{port}` strings that refer to entry ports and their vertices. The data associated with these ports is stored remotely based on the configured preferred remote storage provider. Note that it is required that the user configure these remote storage providers via the `$ nexus conf set --help` command. @@ -226,8 +288,6 @@ Creates a new scheduler task tied to the specified DAG. Key options: Initial schedule arguments (`--schedule-*`) are only valid for queue-based tasks. Selecting `--generator periodic` prepares the task for periodic execution, but you must configure the recurring schedule separately via `nexus scheduler periodic set`. -Data for encrypted entry ports is automatically encrypted when a session is available. - {% hint style="info" %} This command requires that a wallet is connected to the CLI and holds sufficient SUI for gas. {% endhint %} @@ -288,50 +348,6 @@ Removes the periodic schedule while leaving any existing sporadic occurrences un This command requires that a wallet is connected to the CLI and holds sufficient SUI for gas. {% endhint %} -### `nexus crypto` - -Set of commands for managing the CLI’s encrypted secrets (master key, passphrase, identity key) and establishing secure sessions that power DAG data encryption. - ---- - -**`nexus crypto auth [--sui-gas-coin ] [--sui-gas-budget ]`** - -Runs the two-step handshake with the Nexus network to claim a pre-key, perform X3DH with your local identity key, and store a fresh Double Ratchet session on disk. The claimed pre-key bundle is what enables the CLI to complete a Signal-style secure session with the network: X3DH bootstraps shared secrets, and the Double Ratchet derived from that bundle encrypts every DAG payload going forward. The command returns both claim/associate transaction digests and prints the initial message in JSON format, enabling you to audit the handshake. - -Before sending the associate transaction, the CLI automatically generates an identity key if one is missing and persists the session in `~/.nexus/crypto.toml`. All subsequent `nexus dag` commands load that session to encrypt entry-port payloads or decrypt remote results, so run `auth` whenever you rotate keys or see “No active sessions found.” - -{% hint style="info" %} -This command requires that a wallet is connected to the CLI and spends gas for **two** programmable transactions. Use `--sui-gas-coin` / `--sui-gas-budget` if you need explicit control. -{% endhint %} - ---- - -**`nexus crypto generate-identity-key`** - -Creates a brand-new long-term identity key and stores it (encrypted) inside `~/.nexus/crypto.toml`. Because peers can no longer trust sessions tied to the previous identity, the CLI makes it clear that all stored sessions become invalid. Run `nexus crypto auth` immediately after to populate a replacement session. - ---- - -**`nexus crypto init-key [--force]`** - -Generates a random 32‑byte master key with [`OsRng`](https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html) and writes it to the OS keyring under the `nexus-cli-store/master-key` entry. The master key controls access to every encrypted field (`Secret`) in the CLI configuration. Rotating it without also wiping the encrypted data would leave the ciphertext inaccessible, so this command automatically truncates the cryptographic configuration after a successful write. - -Use `--force` to overwrite an existing raw key or stored passphrase; doing so deletes all saved sessions and identity material because it can no longer be decrypted. - ---- - -**`nexus crypto set-passphrase [--stdin] [--force]`** - -Stores a user-provided passphrase in the OS keyring (`nexus-cli-store/passphrase`) and derives the same 32‑byte master key via Argon2id whenever secrets need to be decrypted. By default the command prompts interactively; `--stdin` allows piping from scripts or CI. - -Like `init-key`, it refuses to overwrite an existing persistent key unless `--force`. Empty or whitespace-only passphrases are rejected to avoid unusable configs. - ---- - -**`nexus crypto key-status`** - -Reports where the current master key will be loaded from, following the same priority order as the runtime resolver: `NEXUS_CLI_STORE_PASSPHRASE` environment variable, keyring passphrase entry, or raw key entry. If a raw key is in use the CLI prints the first 8 hex characters so you can distinguish multiple installations; otherwise it notes the source or that no persistent key exists yet. - --- ### `nexus gas` @@ -452,3 +468,6 @@ This command requires that a wallet is connected to the CLI... Provides completion for some well-known shells. + + +[nexus-cli-repo]: https://github.com/Talus-Network/nexus-sdk/tree/main/cli diff --git a/nexus-sdk/client.md b/nexus-sdk/client.md index 780db0c..02c4763 100644 --- a/nexus-sdk/client.md +++ b/nexus-sdk/client.md @@ -1,6 +1,6 @@ # 🪐 [`NexusClient`] (Rust) -The [`NexusClient`] provides a high-level interface for interacting with Nexus. It wraps key functionality including transaction signing and execution, gas management, cryptographic handshakes, and DAG workflow execution. +The [`NexusClient`] provides a high-level interface for interacting with Nexus. It wraps key functionality including transaction signing and execution, gas management, and DAG workflow execution. --- @@ -8,15 +8,15 @@ The [`NexusClient`] provides a high-level interface for interacting with Nexus. The [`NexusClient`] provides access to: -- [`GasActions`]: manage gas coins and budgets -- [`CryptoActions`]: perform cryptographic handshakes with Nexus +- [`GasActions`]: manage gas coins, budgets and tickets - [`WorkflowActions`]: publish and execute workflows (DAGs) - [`SchedulerActions`]: create and manage scheduler tasks, occurrences, and periodic schedules +- [`NetworkAuthActions`]: manage message-signing key bindings for Tools/Leader nodes You can initialize a `NexusClient` via [`NexusClient::builder()`] with: - a Sui **ed25519 private key** -- an RPC URL (and optional GraphQL URL for event fetching) +- an RPC URL - one or more gas coins + a gas budget - the on-chain [`NexusObjects`] @@ -46,6 +46,18 @@ async fn main() -> anyhow::Result<()> { --- +## 🔏 Network Auth (signed HTTP) + +`NexusClient` exposes `network_auth()` for Tool/Leader node message-signing key operations: + +- register/rotate a Tool message-signing key on-chain, and +- export a local allowlist file of permitted Leader nodes for Tool-side verification (no RPC at runtime). +- continuously sync an allowlist file from on-chain `network_auth` (polling). + +This is the same functionality exposed via the CLI under `nexus tool auth ...`. + +--- + ## 🔑 Signer ### Signing Mechanism @@ -85,37 +97,9 @@ println!("Gas budget added in tx: {:?}", result.tx_digest); **Returns:** ---- - -## 🔐 Cryptographic Actions - -These actions manage secure handshakes and session setup. - -### Perform a Handshake - -```rust -use nexus_sdk::crypto::x3dh::IdentityKey; - -let ik = IdentityKey::generate(); - -let handshake = nexus_client.crypto().handshake(&ik).await?; - -println!("Session established!"); -println!("Claim TX: {:?}", handshake.claim_tx_digest); -println!("Associate TX: {:?}", handshake.associate_tx_digest); - -assert!(handshake.session); -``` - -**What it does:** - -1. Claims a pre-key object from Nexus. -1. Fetches the corresponding [`PreKeyBundle`]. -1. Initiates an [`X3DH`] handshake to create a secure [`Session`]. -1. Associates the pre-key with the sender on-chain. - -**Returns:** +[`AddBudgetResult`]: includes the transaction digest. +--- ## ⚡ Workflow Actions @@ -140,13 +124,14 @@ println!("Published DAG ID: {:?}", publish_result.dag_object_id); **Returns:** +[`PublishResult`]: includes the transaction digest and DAG object ID. --- + ### 2. Execute a Workflow ```rust use nexus_sdk::types::{PortsData, StorageConf}; -use nexus_sdk::crypto::session::Session; use std::{collections::HashMap, sync::Arc}; use tokio::sync::Mutex; @@ -155,7 +140,6 @@ let mut entry_data: HashMap = /* your input data */ // Prepare storage and session let storage_conf = StorageConf::default(); -let session = Arc::new(Mutex::new(Session::default())); let execute_result = nexus_client .workflow() @@ -164,7 +148,6 @@ let execute_result = nexus_client entry_data, None, // use default entry group &storage_conf, - session, ) .await?; @@ -179,9 +162,12 @@ println!("Execution object ID: {:?}", execute_result.execution_object_id); **Returns:** +[`ExecuteResult`]: includes the transaction digest and execution object ID. --- +### 3. Inspect Workflow Execution + ```rust use tokio::time::Duration; @@ -214,10 +200,12 @@ println!("✅ Execution finished successfully!"); **Returns:** +[`InspectExecutionResult`]: includes an event stream and a poller handle. --- ## ⏱️ Scheduler Actions + The [`SchedulerActions`] API allows you to create and manage **on-chain scheduler tasks**. A scheduler task is split into: @@ -376,7 +364,6 @@ nexus_client | Module | Purpose | | ------------------ | ------------------------------------------------------- | | `nexus::client` | Core client, builder, signer, gas management | -| `nexus::crypto` | Cryptographic operations (X3DH, sessions, handshakes) | | `nexus::gas` | Gas management for Nexus workflows | | `nexus::workflow` | DAG workflow publishing, execution, and event streaming | | `nexus::scheduler` | Scheduler tasks, occurrences, and periodic schedules | @@ -393,4 +380,3 @@ The `NexusError` enum categorizes issues from configuration errors to RPC and tr ## 🪶 Summary The [`NexusClient`] aims to make building, publishing, and executing Nexus workflows _simple, safe, and async-ready_. It abstracts away Sui transaction signing and gas management while providing a clean modular interface. - diff --git a/nexus-sdk/guides/dag-construction.md b/nexus-sdk/guides/dag-construction.md index 0bade64..dac3796 100644 --- a/nexus-sdk/guides/dag-construction.md +++ b/nexus-sdk/guides/dag-construction.md @@ -2,7 +2,7 @@ This guide explains how to construct DAG (Directed Acyclic Graph) JSON files for the Nexus platform. DAGs define the workflow that an Agent will execute. -For an explanation of the terms and rules used below, refer to [the Nexus workflow documentation](../../nexus-next/packages/workflow.md). +For an explanation of the terms and rules used below, refer to [the Nexus workflow documentation][nexus-next-workflow]. {% hint style="info"%} Note that for all DAG related terms in the configuration JSON file, snake casing is applied. @@ -35,8 +35,7 @@ A DAG JSON file consists of sections defining the graph's components: "name": "unique_vertex_name", "entry_ports": [ { - "name": "input_port_name", // Must match the tool's input schema - "encrypted": false // Optional, default is false + "name": "input_port_name" // Must match the tool's input schema } // ... potentially more entry ports ] @@ -69,8 +68,7 @@ Edges define the flow of data between vertices, connecting an output port of a s "from": { "vertex": "source_vertex_name", // Name from the "vertices" list "output_variant": "ok", // e.g., ok, err, gt, lt, eq - "output_port": "output_port_name", - "encrypted": true // Optional, default is false + "output_port": "output_port_name" }, "to": { "vertex": "target_vertex_name", // Name from the "vertices" list @@ -82,7 +80,6 @@ Edges define the flow of data between vertices, connecting an output port of a s - The `source_vertex_name` and `target_vertex_name` refer to the `name` field of vertices defined in the `vertices` list. - The `target_input_port_name` must be a valid input port for the tool used by the `target_vertex_name`. -- If the `encrypted` field is set to `true`, the data will be encrypted before being sent on-chain. This is useful for sensitive data that should not be exposed in plaintext. ## 3.1 Special edge types @@ -92,7 +89,7 @@ Edges define the flow of data between vertices, connecting an output port of a s - **`do_while`** → Repeats execution by looping back as long as a condition is satisfied. - **`break`** → Exits a `do_while` loop when the condition is no longer met. -Read more about looping and flow controls in the [looping documentation](../../nexus-next/flow-controls/looping.md). +Read more about looping and flow controls in the [looping documentation][looping]. ## 4. Default Values @@ -111,8 +108,8 @@ Default values provide static inputs to vertices: **Important Constraints:** -- An _input port_ can receive data either from an _incoming edge_ or a _default value_, but **never both**. ([workflow rules](../../nexus-next/packages/workflow.md) Rule 4) -- Entry ports **cannot** have default values (by definition). Default values are only permitted for input ports that are _not_ entry ports. ([workflow rules](../../nexus-next/packages/workflow.md) Rule 11) +- An _input port_ can receive data either from an _incoming edge_ or a _default value_, but **never both**. ([workflow rules][nexus-next-workflow] Rule 4) +- Entry ports **cannot** have default values (by definition). Default values are only permitted for input ports that are _not_ entry ports. ([workflow rules][nexus-next-workflow] Rule 11) ## 5. Entry Groups (Optional) @@ -148,8 +145,7 @@ Outputs can be defined on vertices that have no outgoing edges. These can be tho { "vertex": "vertex_name", // Name must exist in the `vertices` list "output_variant": "ok", // e.g., ok, err, gt, lt, eq - "output_port": "output_port_name", // e.g., result - "encrypted": true // Optional, default is false + "output_port": "output_port_name" // e.g., result } ``` @@ -169,21 +165,18 @@ Outputs can be defined on vertices that have no outgoing edges. These can be tho ## 7. Validation Rules -The [Nexus CLI](../cli.md) (`nexus dag validate`) performs static analysis to enforce the critical rules defined in [workflow rules](../../nexus-next/packages/workflow.md). +The [Nexus CLI][nexus-cli] (`nexus dag validate`) performs static analysis to enforce the critical rules defined in [workflow rules][nexus-next-workflow]. ## 8. Best Practices 1. **Naming Conventions**: - - Use descriptive names for vertices. 1. **Organization**: - - Keep the DAG as simple as possible. (But no simpler! For example, branching and entry groups can make powerful composite DAG structures. ) - Use entry groups to provide different ways of starting DAG execution. 1. **Error Handling**: - - Consider all possible `output_variant`s (e.g., `ok`, `err`) from tools. - Explicitly handle error paths or ensure they lead to acceptable end states. - Use appropriate comparison/logic tools for branching. @@ -198,29 +191,24 @@ The [Nexus CLI](../cli.md) (`nexus dag validate`) performs static analysis to en Here's a step-by-step process to create a DAG: 1. **Define Requirements**: - - What inputs are needed? - What outputs are expected? - What processing steps are required? 1. **Design the Flow**: - - Map out the vertices (tools) needed - Determine the connections - Identify branching points 1. **Create Entry Points**: - - Specify entry ports and default values - Set up entry groups if needed 1. **Add Processing Vertices**: - - Define intermediate vertices (tools) - Set up default values 1. **Connect the Dots**: - - Create edges between vertices - Handle all output variants - Ensure proper data flow @@ -241,5 +229,11 @@ For working examples, see the following files in the `cli/src/dag/_dags` directo - `ig_story_planner_valid.json`: Example of a complex workflow - `entry_groups_valid.json`: Example of using entry groups. -For examples of invalid DAGs and common mistakes to avoid (especially regarding Rule 5 - Race Conditions), see the diagrams in [workflow documentation](../../nexus-next/packages/workflow.md) and the `*_invalid.json` files in the [testing DAG directory](https://github.com/Talus-Network/nexus-sdk/tree/v0.5.0/sdk/src/dag/_dags). +For examples of invalid DAGs and common mistakes to avoid (especially regarding Rule 5 - Race Conditions), see the diagrams in [workflow documentation][nexus-next-workflow] and the `*_invalid.json` files in the [testing DAG directory][example-dags]. + + +[nexus-next-workflow]: ../../nexus-next/packages/workflow.md +[example-dags]: https://github.com/Talus-Network/nexus-sdk/tree/v0.6.0/sdk/src/dag/_dags +[nexus-cli]: ../cli.md +[looping]: ../../nexus-next/flow-controls/looping.md diff --git a/nexus-sdk/guides/llm-openai-chat-prep-tool.md b/nexus-sdk/guides/llm-openai-chat-prep-tool.md index 75e5863..52c9e03 100644 --- a/nexus-sdk/guides/llm-openai-chat-prep-tool.md +++ b/nexus-sdk/guides/llm-openai-chat-prep-tool.md @@ -258,8 +258,7 @@ This tool is typically used in a DAG to convert the output of a mathematical ope "name": "chat", "entry_ports": [ { - "name": "api_key", - "encrypted": true + "name": "api_key" } ] } @@ -329,8 +328,7 @@ This tool is typically used in a DAG to convert the output of a mathematical ope { "vertex": "chat", "output_variant": "ok", - "output_port": "message", - "encrypted": false + "output_port": "message" } ] } @@ -344,27 +342,27 @@ Note that you'll have to add the chat completion api key still. It is recommende 1. Build the tool: - ```bash - cargo build - ``` + ```bash + cargo build + ``` 1. Start the tool server: - ```bash - cargo run - ``` + ```bash + cargo run + ``` 1. Validate the tool: - ```bash - nexus tool validate --off-chain http://localhost:8080 - ``` + ```bash + nexus tool validate --off-chain http://localhost:8080 + ``` 1. Register the tool: - ```bash - nexus tool register --off-chain http://localhost:8080 - ``` + ```bash + nexus tool register --off-chain http://localhost:8080 + ``` {% hint style="info" %} Tool registration is currently restricted during the beta phase. To register your tool, please contact the team to be added to the allow list. @@ -381,4 +379,3 @@ Consider for what use cases you could use this tool to prepare it to add as an L ## Next Steps This tool provides a simple but essential bridge between mathematical operations and chat completion, enabling the creation of more sophisticated DAGs that combine numerical computation with natural language processing. Follow along with the developer guides to expand the [Math Branching Example DAG with LLM chat completion](math-branching-with-chat.md). - diff --git a/nexus-sdk/guides/math-branching-dag-builder.md b/nexus-sdk/guides/math-branching-dag-builder.md index eb4e346..e493441 100644 --- a/nexus-sdk/guides/math-branching-dag-builder.md +++ b/nexus-sdk/guides/math-branching-dag-builder.md @@ -101,8 +101,7 @@ First, we define all the nodes (steps) in our graph. Each vertex needs a unique // 'b' will be provided by a default value. "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" } ] }, @@ -359,8 +358,7 @@ Combining these sections gives us the complete `math_branching.json`: "name": "add_input_and_default", "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" } ] }, @@ -487,4 +485,3 @@ This example showcases how to combine simple tools and DAG structure definitions ## Up Next Want to extend this example? Follow the next part of the guide to see how we can add another entry point to the DAG and manage this through entry groups. - diff --git a/nexus-sdk/guides/math-branching-dag-entry.md b/nexus-sdk/guides/math-branching-dag-entry.md index b6f4154..34a1bf5 100644 --- a/nexus-sdk/guides/math-branching-dag-entry.md +++ b/nexus-sdk/guides/math-branching-dag-entry.md @@ -1,6 +1,6 @@ # Extending Your DAG with Entry Groups -This guide builds on the [Build the Quickstart guide](./math-branching-dag-builder.md) by extending the example to support multiple entry points using entry groups. You'll take the original [`math_branching.json`](https://github.com/Talus-Network/nexus-sdk/blob/main/sdk/src/dag/_dags/math_branching.json) DAG and add an alternative entry path that allows users to directly provide two numbers for multiplication instead of adding a constant to the input. +This guide builds on the [Build the Quickstart guide][math-branching-dag-builder-guide] by extending the example to support multiple entry points using entry groups. You'll take the original [`math_branching.json`](https://github.com/Talus-Network/nexus-sdk/blob/main/sdk/src/dag/_dags/math_branching.json) DAG and add an alternative entry path that allows users to directly provide two numbers for multiplication instead of adding a constant to the input. {% hint style="info" %} Prerequisites Follow the [setup guide](setup.md) to get properly setup in case you haven't. @@ -112,8 +112,7 @@ We'll start with the vertices from our original DAG and add the new `mul_inputs` "name": "add_input_and_default", "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" } ] }, @@ -125,12 +124,10 @@ We'll start with the vertices from our original DAG and add the new `mul_inputs` "name": "mul_inputs", "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" }, { - "name": "b", - "encrypted": false + "name": "b" } ] }, @@ -360,8 +357,7 @@ Combining these sections gives us the complete `math_branching_entry_group.json` "name": "add_input_and_default", "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" } ] }, @@ -373,12 +369,10 @@ Combining these sections gives us the complete `math_branching_entry_group.json` "name": "mul_inputs", "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" }, { - "name": "b", - "encrypted": false + "name": "b" } ] }, @@ -608,5 +602,9 @@ In this guide, we extended our original branching math DAG to support multiple e Entry groups are a powerful feature of Nexus DAGs that enable more flexible and modular workflows while maintaining the safety guarantees of the DAG execution model. They allow a single DAG to support multiple different starting states and input combinations while preventing potential race conditions. -For more advanced usage of entry groups and other DAG features, refer to the [DAG Construction Guide](./dag-construction.md). +For more advanced usage of entry groups and other DAG features, refer to the [DAG Construction Guide][dag-construction]. + + +[dag-construction]: ./dag-construction.md +[math-branching-dag-builder-guide]: ./math-branching-dag-builder.md diff --git a/nexus-sdk/guides/math-branching-quickstart.md b/nexus-sdk/guides/math-branching-quickstart.md index 5a00707..3e17b06 100644 --- a/nexus-sdk/guides/math-branching-quickstart.md +++ b/nexus-sdk/guides/math-branching-quickstart.md @@ -149,4 +149,3 @@ When you execute the DAG via the `nexus dag execute` command, a successful outpu - Read the full [Agent Builder Guide](math-branching-dag-builder.md) to understand how this DAG is constructed - Study the [DAG Construction Guide](dag-construction.md) for more advanced DAG features - Try building your own DAG with different tools and logic flows - diff --git a/nexus-sdk/guides/math-branching-with-chat.md b/nexus-sdk/guides/math-branching-with-chat.md index a5ca1c3..80166c6 100644 --- a/nexus-sdk/guides/math-branching-with-chat.md +++ b/nexus-sdk/guides/math-branching-with-chat.md @@ -1,6 +1,6 @@ # Extending the Math Branching DAG with Chat Completion -This guide builds on the [Math Branching DAG with Entry Groups](./math-branching-dag-entry.md) by adding a chat completion tool that explains the mathematical results. You'll learn how to: +This guide builds on the [Math Branching DAG with Entry Groups][math-branching-entry-guide] by adding a chat completion tool that explains the mathematical results. You'll learn how to: 1. Understand the need for a custom tool to bridge between math operations and chat completion 1. Add the chat completion tool to the DAG @@ -15,7 +15,7 @@ Follow the [setup guide](setup.md) to get properly setup in case you haven't. Before you can connect our math operations to the chat completion tool, you need to understand a key challenge: type safety. The [LLM chat completion tool](../../tools/llm-openai-chat-completion/README.md)expects a `Message` struct as input, but the [math tool](../../tools/math/README.md) outputs numbers. You can't directly connect these without proper type conversion. -This is where you need a custom tool to bridge this gap. You'll use the `xyz.taluslabs.llm.openai.chat-prep@1` tool that you developed in the [Build the Missing Tool guide](./llm-openai-chat-prep-tool.md). This tool converts numbers into the proper message format that the chat completion tool expects. +This is where you need a custom tool to bridge this gap. You'll use the `xyz.taluslabs.llm.openai.chat-prep@1` tool that you developed in the [Build the Missing Tool guide][llm-openai-chat-prep-tool]. This tool converts numbers into the proper message format that the chat completion tool expects. ## Step 1: Adding the Required Tools @@ -33,8 +33,7 @@ First, let's add both the number-to-message tool and the chat completion tool to "name": "chat_completion", "entry_ports": [ { - "name": "api_key", - "encrypted": true + "name": "api_key" } ] }, @@ -292,7 +291,6 @@ Here's the complete DAG definition that combines all the components we've discus "entry_ports": [ { "name": "a" - "encrypted": false } ] }, @@ -304,12 +302,10 @@ Here's the complete DAG definition that combines all the components we've discus "name": "mul_inputs", "entry_ports": [ { - "name": "a", - "encrypted": false + "name": "a" }, { - "name": "b", - "encrypted": false + "name": "b" } ] }, @@ -349,8 +345,7 @@ Here's the complete DAG definition that combines all the components we've discus "name": "chat_completion", "entry_ports": [ { - "name": "api_key", - "encrypted": true + "name": "api_key" } ] }, @@ -609,7 +604,7 @@ The `--inspect` flag will show you detailed information about the execution, inc - Any errors that occurred - The final chat completion response -The `api_key` entry port is defined as `encrypted` in the JSON structure, so the CLI will encrypt the data before sending it to the Nexus network. +Note that the `api_key` entry port is contains sensitive data which will be sent on-chain. For production apps, we suggest using tools that contain the API keys internally. ### 2. Integration with Applications @@ -620,7 +615,6 @@ To integrate this DAG into your application: 1. **Handle API Keys**: Securely manage the OpenAI API key. Consider using environment variables or a secrets management service. 1. **Create Entry Points**: Implement the two entry points in your application: - - Addition path: Takes a single number and adds -3 - Multiplication path: Takes two numbers and multiplies them @@ -668,3 +662,7 @@ This extended DAG demonstrates how to combine mathematical computation with natu This extended DAG demonstrates how to combine mathematical computation with natural language processing, creating a more interactive and engaging experience for users. + + +[math-branching-entry-guide]: ./math-branching-dag-entry.md +[llm-openai-chat-prep-tool]: ./llm-openai-chat-prep-tool.md diff --git a/nexus-sdk/guides/onchain-tool-development.md b/nexus-sdk/guides/onchain-tool-development.md index 516e75b..8e499db 100644 --- a/nexus-sdk/guides/onchain-tool-development.md +++ b/nexus-sdk/guides/onchain-tool-development.md @@ -72,8 +72,9 @@ In `sources/my_onchain_tool.move`: ```move module my_onchain_tool::my_onchain_tool; +use nexus_primitives::data; use nexus_primitives::proof_of_uid::ProofOfUID; -use nexus_workflow::tool_output::{Self, ToolOutput}; +use nexus_primitives::tagged_output::{Self, TaggedOutput}; use sui::bag::{Self, Bag}; use sui::clock::Clock; use sui::transfer::share_object; @@ -103,7 +104,7 @@ public struct MyToolState has key { ```move /// Tool execution output variants. /// This enum is used for automatic schema generation during registration. -/// It's not used during execution. Only the ToolOutput object is used. +/// It's not used during execution. Only the TaggedOutput object is used. public enum Output { Ok { result: u64, @@ -149,7 +150,7 @@ This is the core of your tool, the function that performs the actual execution: /// CRITICAL REQUIREMENTS: /// 1. First parameter: worksheet: &mut ProofOfUID /// 2. Last parameter: ctx: &mut TxContext -/// 3. Return type: ToolOutput +/// 3. Return type: TaggedOutput /// 4. Must stamp worksheet with witness ID public fun execute( worksheet: &mut ProofOfUID, @@ -158,7 +159,7 @@ public fun execute( input_value: u64, clock: &Clock, ctx: &mut TxContext, -): ToolOutput { +): TaggedOutput { // Get the witness for stamping. let witness = state.witness(); @@ -168,37 +169,44 @@ public fun execute( // Implement your tool logic here if (input_value == 0) { // Return error variant - tool_output::err(b"Input value cannot be zero") + Self::new(b"err") + .with_named_payload(b"reason", data::inline_one(b"Input value cannot be zero").as_string()) } else if (input_value > 1000) { // Return custom variant - tool_output::variant(b"custom_result") - .with_field(b"data", tool_output::string_value(b"large_value_processed")) - .with_field(b"timestamp", tool_output::number_value(sui::clock::timestamp_ms(clock).to_string().into_bytes())) + Self::new(b"custom_result") + .with_named_payload(b"data", data::inline_one(b"large_value_processed").as_string()) + .with_named_payload(b"timestamp", data::inline_one(sui::clock::timestamp_ms(clock).to_string().into_bytes()).as_number()) } else { // Return success variant let result = input_value * 2; - tool_output::ok() - .with_field(b"result", tool_output::number_value(result.to_string().into_bytes())) + Self::new(b"ok") + .with_named_payload(b"result", data::inline_one(result.to_string().into_bytes()).as_number()) } } ``` #### Understanding Field Value Types -When adding fields to `ToolOutput`, you must use typed constructor functions to ensure proper JSON formatting: +When adding fields to `TaggedOutput`, you must use the fluent API with type hints to ensure proper JSON formatting: ```move // Numeric values (u8, u16, u32, u64, u128, u256) -.with_field(b"count", tool_output::number_value(value.to_string().into_bytes())) +.with_named_payload(b"count", data::inline_one(value.to_string().into_bytes()).as_number()) // String values (will be wrapped in quotes in JSON) -.with_field(b"message", tool_output::string_value(b"Hello world")) +.with_named_payload(b"message", data::inline_one(b"Hello world").as_string()) // Boolean values (true/false without quotes in JSON) -.with_field(b"success", tool_output::bool_value(b"true")) +.with_named_payload(b"success", data::inline_one(b"true").as_bool()) // Address values (prefixed with "0x" and wrapped in quotes in JSON) -.with_field(b"sender", tool_output::address_value(address.to_string().into_bytes())) +.with_named_payload(b"sender", data::inline_one(address.to_string().into_bytes()).as_address()) + +// Raw JSON values (objects, arrays, null - passed through as-is) +.with_named_payload(b"metadata", data::inline_one(b"{\"key\":\"value\"}").as_raw()) + +// Many values (for loops in nexus) +.with_named_payload(b"items", data::inline_many(items).as_number()) ``` This typing ensures that the Nexus framework correctly parses and processes your tool's outputs. @@ -327,12 +335,10 @@ An example JSON DAG using the onchain tool is as follows: "name": "just_execute_first", "entry_ports": [ { - "name": "0", - "encrypted": false + "name": "0" }, { - "name": "1", - "encrypted": false + "name": "1" } ] }, @@ -344,8 +350,7 @@ An example JSON DAG using the onchain tool is as follows: "name": "just_execute_second", "entry_ports": [ { - "name": "0", - "encrypted": false + "name": "0" } ] } @@ -374,4 +379,3 @@ This workflow only executes the onchain tool twice if the output variant is Succ - **Examples**: Study the [onchain tool example modules](../../nexus-next/sui/examples/) and [corresponding json dag workflows](../../sdk/src/dag/_dags/) Remember that onchain tools are powerful building blocks in the Nexus ecosystem. Well-designed tools can be composed with others to create sophisticated autonomous agents and workflows. - diff --git a/nexus-sdk/guides/setup.md b/nexus-sdk/guides/setup.md index fc6e027..47196fc 100644 --- a/nexus-sdk/guides/setup.md +++ b/nexus-sdk/guides/setup.md @@ -38,7 +38,7 @@ To install directly from the source using `cargo`, run: ```bash cargo install nexus-cli \ --git https://github.com/talus-network/nexus-sdk \ - --tag v0.5.0 \ + --tag v0.6.0 \ --locked ``` @@ -51,7 +51,7 @@ nexus --version ## Download the Nexus objects ```bash -wget -O ~/.nexus/objects.devnet.toml https://storage.googleapis.com/production-talus-sui-objects/v0.5.0/objects.devnet.toml +wget -O ~/.nexus/objects.devnet.toml https://storage.googleapis.com/production-talus-sui-objects/v0.6.0/objects.devnet.toml ``` ## Configure the Talus devnet @@ -61,7 +61,6 @@ Configure your Nexus CLI to connect to the Talus `devnet` by running: ```bash nexus conf set \ --sui.rpc-url https://grpc.ssfn.devnet.production.taluslabs.dev \ - --sui.gql-url https://graphql.devnet.production.taluslabs.dev/graphql \ --nexus.objects ~/.nexus/objects.devnet.toml ``` @@ -79,7 +78,7 @@ sui client --yes {% endhint %} ```bash -sui client new-env --alias devnet --rpc https://grpc.ssfn.devnet.production.taluslabs.dev +sui client new-env --alias devnet --rpc https://rpc.ssfn.devnet.production.taluslabs.dev sui client switch --env devnet ``` @@ -141,86 +140,6 @@ nexus gas add-budget \ Note that this coin can only be used to pay for Nexus and tool invocation fees only if the DAG is executed from the **same address**. {% endhint %} -## Configure Encryption for Nexus workflows - -Nexus encrypts every sensitive value in your CLI config and every DAG payload using a [Signal-inspired](https://signal.org/docs/) stack: a persistent master key protects secrets at rest, an [X3DH](https://signal.org/docs/specifications/x3dh/) identity key authenticates you to the network, and a [Double-Ratchet](https://signal.org/docs/specifications/doubleratchet/) session derived from an on-chain pre-key encrypts runtime traffic. Follow the steps below in order; each one builds on the previous. - -### 1. Initialize the CLI master key - -The CLI stores encrypted blobs (identity key, sessions, Walrus credentials, etc.) in `~/.nexus/*.toml`. Those blobs are decrypted using a 32-byte master key that lives either in your OS keyring or is derived from a passphrase via [Argon2id](https://en.wikipedia.org/wiki/Argon2). - -```bash -# Option A: generate a raw master key inside the OS keyring -nexus crypto init-key - -# Option B: store a passphrase (useful for headless or CI) -printf "my-strong-passphrase" | nexus crypto set-passphrase --stdin -``` - -Both commands refuse to overwrite existing credentials unless you add `--force`, because rotating the master key invalidates every encrypted entry. You can confirm which source will be used by running: - -```bash -nexus crypto key-status -``` - -### 2. Generate an identity key - -Your long-term identity key represents the "public face" of the CLI in the Signal/X3DH handshake. It only needs to be generated once per installation or when you intentionally rotate it, which also invalidates stored sessions. - -```bash -nexus crypto generate-identity-key -``` - -This writes a freshly generated X25519 key pair into `~/.nexus/crypto.toml`, encrypted with the master key from Step 1. - -### 3. Establish a Signal-style session - -With persistence in place, run the auth flow to claim a pre-key bundle on-chain and derive a per-session Double Ratchet. The CLI handles the entire exchange for you: - -```bash -nexus crypto auth -``` - -Behind the scenes the command: - -1. Submits a programmable transaction that calls `pre_key_vault::claim_pre_key_for_self`, emitting the pre-key bundle bytes for your Sui address. -1. Runs the X3DH sender flow locally using your identity key and that bundle, deriving shared secrets and the first Double-Ratchet message. -1. Persists the resulting session (encrypted) to `~/.nexus/crypto.toml`. -1. Sends a second programmable transaction that associates the claimed pre-key object with your address and delivers the initial encrypted message to the network. - -Every `nexus dag execute` / `inspect-execution` call now loads this session to encrypt entry ports and decrypt remote-hosted outputs. If you delete the session or rotate keys, simply rerun `nexus crypto auth` to mint a replacement. - -#### What `nexus crypto auth` does under the hood - -At a high level the CLI glues an on-chain pre-key vault (for rendezvous) with an offline Signal stack (X3DH + Double Ratchet): - -```mermaid -sequenceDiagram - participant CLI as Nexus CLI - participant Conf as CryptoConf - participant Sui as Sui chain - participant Vault as PreKeyVault contract - participant Network as Nexus network - - CLI->>Conf: Load config + decrypt identity key - CLI->>Sui: Submit claim_pre_key_for_self PTB (request) - Sui->>Vault: execute pre_key_vault::claim_pre_key_for_self - Vault-->>Sui: Emit PreKeyRequestedEvent (no bundle yet) - Leader->>Sui: Submit fulfill_pre_key_for_user PTB - Sui->>Vault: execute pre_key_vault::fulfill_pre_key_for_user - Vault-->>Sui: Emit PreKeyFulfilledEvent (carries pre_key bytes) - CLI->>Sui: poll events, read pre_key bytes from PreKeyFulfilledEvent - CLI->>CLI: Deserialize bytes -> PreKeyBundle - CLI->>CLI: Initialize double-ratche Session - CLI->>Conf: Save Double-Ratchet Session - CLI->>Sui: Submit associate_pre_key PTB + initial_message - Sui->>Network: Route initial message so nexus completes X3DH receiver flow -``` - -{% hint style="info" %} -`claim_pre_key_for_self` is rate limited and each transaction requires gas. Make sure you have uploaded budget via `nexus gas add-budget` and keep an eye on `--sui-gas-coin` / `--sui-gas-budget` if you need precise control. -{% endhint %} - ## (Optional) Access Devnet Sui Explorer Open the [Talus Sui Explorer](https://explorer.devnet.taluslabs.dev/). @@ -228,4 +147,3 @@ Open the [Talus Sui Explorer](https://explorer.devnet.taluslabs.dev/). --- After completing these steps, you are ready to build and execute workflows using the Nexus SDK. To build your first workflow, check the [Dev Quickstart guide](math-branching-quickstart.md). - diff --git a/nexus-sdk/guides/tool-communication.md b/nexus-sdk/guides/tool-communication.md new file mode 100644 index 0000000..f11fe71 --- /dev/null +++ b/nexus-sdk/guides/tool-communication.md @@ -0,0 +1,362 @@ +# Tool Communication (HTTPS + Signed HTTP) + +This guide explains how Nexus communicates with off-chain Tools (via its Leader nodes), why Nexus requires both **HTTPS** and **signed HTTP messages**, and what Tool developers/operators must do to deploy Tools correctly. + +It is written for: + +- **Tool developers** (build the Tool binary and its schemas). +- **Tool operators** (deploy the Tool and manage keys/certs). +- **Agent developers** (understand what guarantees Tool calls do and do not provide). + +--- + +## TL;DR (what Nexus expects) + +1. **Tools are HTTP servers** that expose (at minimum) `GET /health`, `GET /meta`, and `POST /invoke`. +1. **Nexus Leader nodes call Tools over HTTPS** and validate the Tool’s TLS certificate using the **system root store** (the same trust model as `curl`). + - Your Tool must present a certificate chain that validates against standard roots. +1. **Tool invocations are signed** at the application layer (a.k.a. “signed HTTP”): + - Leader node → Tool requests carry `X-Nexus-Sig-*` headers that authenticate the calling Leader node, bind the request metadata, and bind the request body bytes. + - Tool → Leader node responses also carry `X-Nexus-Sig-*` headers so the Leader node can verify provenance and bind the response to the exact request. +1. **Tools should not do on-chain reads at runtime** to validate Leader nodes. Instead, the Tool operator exports a local allowlist file of permitted Leader nodes and deploys it next to the Tool. + +{% hint style="info" %} +Terminology: a **Leader node** is the Nexus node calling the Tool. In signed HTTP claims, `leader_id` identifies the Leader node (a Sui like `address`) that signed the request. +{% endhint %} + +--- + +## Terminology: “TLS termination” + +Tools are plain HTTP servers. Run them behind a reverse proxy / gateway / load balancer that serves **HTTPS** on the ingress and forwards requests to the Tool over HTTP. + +This setup is commonly called **TLS termination** (sometimes people informally say “HTTPS terminal”). + +--- + +## Architecture overview + +### Actors and their responsibilities + +- **Leader node** + - Discovers Tool URL from the on-chain Tool Registry. + - Calls the Tool over HTTPS (certificate validated via system roots). + - Signs `/invoke` requests with the Leader node message-signing key. + - Verifies signed `/invoke` responses using the Tool message-signing public key registered on-chain. + +- **Tool** + - Runs an HTTP server that implements the Nexus Tool interface. + - Verifies signed `/invoke` requests using a local allowlist of Leader node public keys. + - Signs `/invoke` responses with its Tool message-signing key. + +--- + +## Why both HTTPS and signed HTTP? + +Nexus uses two layers because they solve different problems. + +### HTTPS (TLS) solves transport security + +HTTPS provides: + +- **Confidentiality**: protects Tool inputs/outputs from passive observers. +- **Integrity**: prevents tampering in transit. +- **Server authentication**: a Leader node can verify it is talking to the expected server for `https://your-tool-domain/...`. + +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). + +{% 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 %} + +### Certificate verification policy + +Leader nodes validate Tool certificates using the **system root CA store** (similar to `curl`). Tool operators must: + +- use a publicly trusted certificate (e.g., Let’s Encrypt, cloud managed cert) + +### Signed HTTP solves identity, auditability, and replay resistance + +Signed HTTP provides: + +- **Strong identity binding**: “this invocation was signed by Leader node `0x...` using key id `kid`”. +- **Request/response binding**: “this response corresponds to this specific request”. +- **Body integrity at the application layer**: binds the exact body bytes via SHA-256. +- **Replay resistance**: prevents an attacker from replaying old requests, while still allowing safe retries. +- **Auditability / dispute support**: the signed claims are small and can be logged and verified later. + +Signed HTTP is Nexus’ authentication mechanism for `POST /invoke`: + +- Tools authenticate the calling Leader node by verifying the request signature. +- Leader nodes authenticate Tool responses by verifying the response signature (and binding it to the request). + +{% hint style="info" %} +Signed HTTP does **not** guarantee that a Tool’s output is correct or safe. It only proves who signed the message and what bytes they signed. Nexus uses collateral + slashing as an after-the-fact enforcement mechanism for malicious behavior. +{% 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 / edge (rate limiting, firewall/WAF, mTLS, or private ingress such as Cloudflare Tunnel). +{% endhint %} + +--- + +## Signed HTTP protocol (v1) – what is signed? + +Signed HTTP uses three headers on every signed request/response: + +- `X-Nexus-Sig-V`: protocol version (currently `"1"`). +- `X-Nexus-Sig-Input`: base64url (no padding) of the raw JSON “claims” bytes. +- `X-Nexus-Sig`: base64url (no padding) of the 64-byte Ed25519 signature. + +The signature is computed over: + +- a protocol-specific **domain separator** (request vs response), and +- the exact `sig_input` bytes (the JSON-encoded claims). + +This avoids fragile “HTTP canonicalization” and keeps Tool schemas unchanged: the Tool input/output remains the normal HTTP body. + +### Request claims (Leader node → Tool) + +The signed request claims include: + +- `leader_id`, `leader_kid`: identify the calling Leader node and its active signing key id. +- `tool_id`: the Tool’s on-chain identifier (typically the Tool FQN string form). +- `iat_ms`, `exp_ms`: a validity window (milliseconds since the Unix epoch, UTC). +- `nonce`: unique token per invocation to prevent replay (UUID/random is fine). +- `method`, `path`, `query`: bind the HTTP request target. +- `body_sha256`: hex-encoded SHA-256 of the raw request body bytes. + +### Response claims (Tool → Leader node) + +The signed response claims include: + +- `tool_id`, `tool_kid`: identify the Tool and its signing key id. +- `owner_leader_id`: Leader node id that currently “owns” the nonce (for observability / failover). +- `iat_ms`, `exp_ms`: a validity window. +- `nonce`: echo of the request nonce. +- `req_sig_input_sha256`: hex-encoded SHA-256 of the **request** `sig_input` bytes (binds response to the exact request). +- `status`: HTTP status code the Tool claims it produced. +- `body_sha256`: hex-encoded SHA-256 of the raw response body bytes. + +### Replay rules (how retries stay safe) + +Tools MUST track nonce usage (keyed by `(tool_id, nonce)`): + +- If a request with the same `(tool_id, nonce)` arrives and the request identity matches (`method`, `path`, `query`, `body_sha256`), it is a safe **retry** (including Leader failover). +- If the same `(tool_id, nonce)` is used with a different request identity, it is a **conflicting replay** and MUST be rejected. + +The Rust toolkit runtime implements this in-memory for you. + +--- + +## Key registration and rotation (Network Auth) + +Signed HTTP requires public keys to be discoverable and verifiable. + +Nexus uses an on-chain **Network Auth registry** to bind identities to message-signing public keys: + +- **Leader node identity**: a Sui address (the Leader node’s on-chain identifier). +- **Tool identity**: the Tool FQN (the Tool’s on-chain identifier). + +Each identity has: + +- a `next_key_id` counter (key id / `kid`), +- a set of keys, +- an optional active key id, +- optional metadata (e.g., description). + +Registration uses a **proof-of-possession** (PoP) signature so the chain can verify the registrant actually controls the private key corresponding to the public key being registered. + +--- + +## How to deploy a Tool (end-to-end checklist) + +### 1) Implement the Tool as an HTTP server + +Use `nexus-toolkit` to implement the `NexusTool` trait and bootstrap an HTTP server. + +- `/health` and `/meta` stay unsigned. +- `/invoke` is the authenticated endpoint and is signed when enabled. + +### 2) Put the Tool behind HTTPS (TLS termination) + +The toolkit runtime is an **HTTP server**. Run it behind a reverse proxy / load balancer that provides TLS and forwards requests to the Tool over HTTP. + +Common options: + +- **Caddy (recommended for simplicity)** – automatic HTTPS (Let’s Encrypt): [Automatic HTTPS](https://caddyserver.com/docs/automatic-https) +- **Nginx + Certbot (Let’s Encrypt)**: [Certbot instructions](https://certbot.eff.org/instructions?ws=nginx) +- **Traefik (ACME)**: [ACME](https://doc.traefik.io/traefik/https/acme/) +- **Cloudflare (Proxy / Tunnel)**: [SSL](https://developers.cloudflare.com/ssl/) and [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) +- **Cloud load balancers** (AWS ALB/ACM, GCP HTTPS LB, etc.) + +Local certificate option (useful for local testing): + +- **mkcert** (local trusted dev certs): [mkcert](https://github.com/FiloSottile/mkcert) + +{% hint style="warning" %} +If you use a self-signed certificate, Leader nodes will reject it unless the Nexus deployment is explicitly configured to trust your CA. Use a publicly trusted cert whenever possible. +{% endhint %} + +#### TLS termination example (Caddy) + +If your Tool listens on `127.0.0.1:8080` and you want to serve it at `https://tool.example.com`, a minimal `Caddyfile` looks like: + +```caddyfile +tool.example.com { + reverse_proxy 127.0.0.1:8080 +} +``` + +Then run: + +```bash +sudo caddy run --config ./Caddyfile +``` + +Operational notes: + +- Ensure your proxy forwards `X-Nexus-Sig-*` headers (most do by default; but some “API gateways” may drop unknown headers). +- Avoid middleware that rewrites request/response bodies. The signature binds the raw body bytes. + +### 3) Generate a Tool message-signing keypair + +You need an Ed25519 keypair dedicated to signing Tool responses. + +Using the CLI: + +```bash +nexus tool auth keygen --out ./tool_keypair.json +``` + +Store the private key securely. Treat it like an API credential. + +### 4) Register the Tool in the Tool Registry (off-chain Tool) + +Register the Tool URL and schema on-chain. The CLI persists the resulting OwnerCaps locally (unless `--no-save`), so you can reuse them for future operations: + +```bash +nexus tool register --off-chain https://tool.example.com/ \ + --collateral-coin 0x... \ + --invocation-cost 0 +``` + +The Tool URL should be `https://...`. + +### 5) Register the Tool message-signing public key on-chain (Network Auth) + +Using the CLI (requires the tool’s OwnerCap object id and a gas coin): + +```bash +nexus tool auth register-key \ + --tool-fqn com.example.my-tool@1 \ + --owner-cap 0x... \ + --signing-key ./tool_signing_key.hex \ + --sui-gas-coin 0x... \ + --sui-gas-budget 50000000 +``` + +Notes: + +- `--signing-key` can be a hex/base64/base64url private key string **or** a path to a file containing it. +- If `--owner-cap` is omitted, the CLI will try to use the OwnerCap saved in the CLI config for that Tool. + +This creates (or updates) the Tool’s key binding in `network_auth` and returns the `tool_kid` you must configure in the Tool runtime. + +### 6) Export `allowed_leaders.json` for Tool-side verification + +Tools should not RPC to Sui on every request. Instead, generate a local allowlist file of permitted Leader nodes: + +```bash +nexus tool auth export-allowed-leaders \ + --all \ + --out ./allowed_leaders.json +``` + +Deploy `allowed_leaders.json` next to your Tool. + +If you want this file to stay up-to-date automatically, run the syncer (polling): + +```bash +nexus tool auth sync-allowed-leaders \ + --out ./allowed_leaders.json \ + --interval 30s +``` + +### 7) Configure the toolkit runtime to require signed HTTP + +Set `NEXUS_TOOLKIT_CONFIG_PATH` to point at a JSON config file that includes: + +- the allowlist file (`allowed_leaders_path`), and +- the Tool signing key + `tool_kid`. + +Example `toolkit.json`: + +```json +{ + "version": 1, + "invoke_max_body_bytes": 10485760, + "signed_http": { + "mode": "required", + "allowed_leaders_path": "./allowed_leaders.json", + "tools": { + "com.example.my-tool@1": { + "tool_kid": 0, + "tool_signing_key": "0000000000000000000000000000000000000000000000000000000000000000" + } + } + } +} +``` + +Then run your Tool with: + +```bash +export NEXUS_TOOLKIT_CONFIG_PATH=./toolkit.json +./my-tool-binary +``` + +{% hint style="info" %} +If you run multiple tools on the same server, add each tool id under `signed_http.tools` with its own signing key and `tool_kid`. +{% endhint %} + +--- + +## Optional: additional Tool-side authorization + +Signed HTTP tells the Tool “this request was signed by Leader node `X`”. Tool authors may still want additional policy: + +- allow only a subset of Leader nodes (beyond the allowlist file), +- add rate limiting, allow only certain task types, etc. + +`nexus-toolkit` exposes an `authorize(ctx)` hook (and an `AuthContext`) for this. + +--- + +## Troubleshooting + +### Tool rejects all requests (401 auth_failed) + +Common causes: + +- Reverse proxy is stripping `X-Nexus-Sig-*` headers. +- Tool has the wrong `allowed_leaders.json` file (missing Leader node key). +- Clock skew is too high (Tool host time is wrong). +- `tool_id` mismatch (Tool is configured for a different Tool FQN / id). + +### Leader node rejects Tool responses + +Common causes: + +- Tool is using the wrong signing key or `tool_kid`. +- Tool key is not registered (or not the active key) on-chain. +- Tool response body is being modified by middleware. + +### TLS / certificate errors from Leader nodes + +Common causes: + +- Certificate is self-signed or missing intermediate chain. +- Certificate hostname does not match the Tool URL. +- Leader node environment does not include the required root CA (custom CA deployments). diff --git a/nexus-sdk/guides/tool-firewall.md b/nexus-sdk/guides/tool-firewall.md index 8b2f782..a4c0b53 100644 --- a/nexus-sdk/guides/tool-firewall.md +++ b/nexus-sdk/guides/tool-firewall.md @@ -1,15 +1,25 @@ -# Restricting access to Leader nodes +# Restricting access to Leader nodes (optional) ## Overview -To enhance security on your Tool, it's essential to configure your server to accept incoming connections solely from the authorized Leader nodes. This guide provides step-by-step instructions to achieve this using UFW. +Nexus Tools authenticate `POST /invoke` requests via **signed HTTP** (Ed25519 signatures) and are typically deployed behind **HTTPS**. +In most deployments, this cryptographic authentication is the primary access control mechanism and you do **not** need to rely on IP allowlists. + +However, you may still want a firewall as defense-in-depth to reduce generic internet traffic to your Tool’s ingress (for example, only allowing traffic from your reverse proxy / load balancer, or from a fixed set of Leader node egress IPs if Nexus provides them). + +This guide provides step-by-step instructions to restrict inbound traffic using UFW. + +See also: + +- [Tool Communication (HTTPS + Signed HTTP)](tool-communication.md) ## Leader Node IP Addresses -Ensure that only the following IP addresses are permitted: +If Nexus provides fixed egress IP addresses for its Leader nodes, ensure that only those IPs are permitted. -- `35.211.137.107` -- `35.207.42.45` +{% hint style="warning" %} +Do not assume Leader node IPs are permanent. If IPs change, your Tool will become unreachable even if signed HTTP is configured correctly. +{% endhint %} ## Prerequisites @@ -37,11 +47,11 @@ sudo ufw default allow outgoing ### 3. Allow Connections from Leader Nodes -Permit incoming connections from each Leader node IP address: +Permit incoming connections from each allowed IP address: ```bash -sudo ufw allow from 35.211.137.107 -sudo ufw allow from 35.207.42.45 +sudo ufw allow from +sudo ufw allow from ``` {% hint style="info" %} @@ -49,8 +59,8 @@ sudo ufw allow from 35.207.42.45 If you wish to restrict access to specific ports (e.g., SSH on port 22), modify the commands as follows: ```bash -sudo ufw allow from 35.211.137.107 to any port 22 proto tcp -sudo ufw allow from 35.207.42.45 to any port 22 proto tcp +sudo ufw allow from to any port 22 proto tcp +sudo ufw allow from to any port 22 proto tcp ``` {% endhint %} @@ -72,5 +82,4 @@ You should see entries indicating that connections from the specified IP address --- -By following this guide, your Tool will be configured to accept connections only from the specified Leader nodes, enhancing the security of your deployment. - +By following this guide, your Tool will be configured to accept connections only from the specified Leader nodes, enhancing security as defense-in-depth. diff --git a/nexus-sdk/index.md b/nexus-sdk/index.md index c098f87..5ec7e1e 100644 --- a/nexus-sdk/index.md +++ b/nexus-sdk/index.md @@ -41,4 +41,4 @@ Docs: - [Tool Development Guidelines](tool-development.md) - [Nexus Toolkit Rust](toolkit-rust.md) - +- [Tool Communication (HTTPS + Signed HTTP)](guides/tool-communication.md) diff --git a/nexus-sdk/tool-development.md b/nexus-sdk/tool-development.md index 8289dbb..1f8da22 100644 --- a/nexus-sdk/tool-development.md +++ b/nexus-sdk/tool-development.md @@ -71,3 +71,23 @@ If using Rust, the `main.rs` file should include this documentation via `#![doc See also [an example README file](../tools/llm-openai-chat-completion/README.md). +--- + +## Security and Deployment + +Nexus Tools are off-chain HTTP services and must be deployed with: + +- **HTTPS**: Nexus Leader nodes connect to Tools over HTTPS and validate the Tool’s certificate using the system root trust store. +- **Signed HTTP for `/invoke`**: Tool invocations are authenticated and replay-protected using Ed25519 signatures in `X-Nexus-Sig-*` headers. + +Tool developers should design with these constraints in mind: + +- Do not assume the caller is trusted just because they can reach your server. Authentication is done at the signed-message layer. +- Signed HTTP verification happens after the TLS handshake. If you want to reduce unwanted TLS handshakes/traffic, apply policy at your TLS terminator / edge (rate limiting, firewall/WAF, mTLS, or private ingress such as Cloudflare Tunnel). +- Do not rely only on static IP allowlists (Leader node IPs can change). If you add a firewall, treat it as defense-in-depth. +- Keep Tool signing keys secret and rotate them if exposed. +- Ensure your host clock is accurate (NTP), because signed requests use validity windows. + +For a detailed guide (TLS termination options, key registration, runtime config, and troubleshooting), see: + +- [Tool Communication (HTTPS + Signed HTTP)](guides/tool-communication.md) diff --git a/nexus-sdk/toolkit-rust.md b/nexus-sdk/toolkit-rust.md index b083442..f560977 100644 --- a/nexus-sdk/toolkit-rust.md +++ b/nexus-sdk/toolkit-rust.md @@ -1,6 +1,6 @@ # Nexus Toolkit for Rust -> concerns [`nexus-toolkit-rust` repo](https://github.com/Talus-Network/nexus-sdk/tree/main/toolkit-rust) +> concerns [`nexus-toolkit-rust` repo][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. @@ -8,14 +8,14 @@ This documentation will go over the main features of the library and how to use ## Installation -Using the [CLI](./cli.md) 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. +Using the [CLI][nexus-cli-docs] 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: ```toml [dependencies.nexus-toolkit] git = "https://github.com/Talus-Network/nexus-sdk" -tag = "v0.5.0" +tag = "v0.6.0" package = "nexus-toolkit" ``` @@ -83,7 +83,7 @@ This associated type defines the output that the Tool produces. This type must d The Tool's output schema is then derived from this type via the `schemars::schema_for!` macro. -To comply with [Nexus Workflow output variants](../nexus-next/packages/workflow.md), 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`. +To comply with [Nexus Workflow output variants][nexus-next-workflow-docs], 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`. ```rs use nexus_toolkit::*; @@ -106,7 +106,7 @@ impl NexusTool for HttpStatus { #### `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](../nexus-next/tool.md). +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][nexus-next-tool-docs]. ```rs use nexus_toolkit::*; @@ -181,10 +181,30 @@ Notice that the `invoke` function does not return a `Result`. This is because er --- +#### `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](../nexus-next/tool.md). +underlying HTTP server that adheres to the [Nexus Tool interface][nexus-next-tool-docs]. It has a flexible interface that accepts an `Into` value and a struct that `impl NexusTool`. @@ -224,3 +244,45 @@ async fn main() { } ``` +--- + +## 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: + +- [Tool Communication (HTTPS + Signed HTTP)](guides/tool-communication.md) + + + +[nexus-toolkit-rust-repo]: https://github.com/Talus-Network/nexus-sdk/tree/main/toolkit-rust +[nexus-next-tool-docs]: ../nexus-next/tool.md +[nexus-next-workflow-docs]: ../nexus-next/packages/workflow.md +[nexus-cli-docs]: ./cli.md From 625399b9951137c8f0949bbfd4136fefcc5d0afa Mon Sep 17 00:00:00 2001 From: devops-talus Date: Thu, 12 Mar 2026 18:37:44 +0000 Subject: [PATCH 2/2] Update SUMMARY.md with new files under 'Looking for a home' --- SUMMARY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SUMMARY.md b/SUMMARY.md index 47d1dc9..4ee0f31 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -103,3 +103,7 @@ - [Echanges: Coinbase](tools/exchanges-coinbase/README.md) - [HTTP](tools/http/README.md) - [Templating: Jinja](tools/templating-jinja/README.md) + +## Looking for a home + +* [nexus-sdk/guides/tool-communication.md](nexus-sdk/guides/tool-communication.md)