# sandlet-sdk

`sandlet-sdk` is a Rust library for building small scripted agents with [Roto](https://github.com/NLnetLabs/roto).

It provides a runtime for compiling and executing Roto scripts, a simple module registration model for exposing Rust
functions to scripts, a built-in CLI entrypoint for local testing, a gRPC transport, and a transport-agnostic execution
service internally.

## Status

This repository is an SDK workspace, not a full platform.

What works today:

- register functions from Rust and call them from Roto
- run local scripts through the SDK CLI
- build separate example agent crates on top of the SDK
- gRPC transport via `Agent::serve`
- register opaque Rust types safely via the runtime (e.g. file handles)

What is not finished yet:

- long-lived session mode
- backend orchestration
- sandbox execution

## Workspace Layout

```text
.
├── src/                      # sandlet-sdk library crate
├── examples/basic-agent/     # minimal example agent
└── examples/weather-agent/   # example with HTTP-backed modules
```

## SDK Shape

An embedding binary is expected to:

1. create an `Agent`
2. register one or more modules
3. call `run()`

Minimal example:

```rust
use sandlet_sdk::Agent;

fn main() -> sandlet_sdk::Result<()> {
    Agent::builder()
        .name("my-agent")
        .build()?
        .run()
}
```

Modules expose Rust functions to Roto scripts. The SDK does not require a plugin system or separate process model for
this. Modules are compiled into the agent binary.

Exported function names must follow the `<module>_<function>` naming contract (e.g. `strings_uppercase`, `ip_public_ip`).
This is enforced when the agent is built.

For the current intended embedding surface, see [docs/public-api.md](docs/public-api.md). For the current execution
model, see [docs/runtime-contract.md](docs/runtime-contract.md).

## Examples

This workspace includes two example agent crates:

- `basic-agent`
  Minimal example with one built-in SDK module and one crate-local module.
- `weather-agent`
  Example with separate `ip` and `weather` modules implemented in the example crate.

Run the basic example:

```sh
cargo run -p basic-agent -- run --script examples/basic-agent/scripts/basic.roto
```

Run the args-based basic example:

```sh
cargo run -p basic-agent -- run --script examples/basic-agent/scripts/args.roto -- hello
```

Run the weather example:

```sh
cargo run -p weather-agent -- run --script examples/weather-agent/scripts/weather.roto
```

## CLI

The SDK currently exposes two subcommands from the embedding binary:

```sh
<agent-binary> run --script <path> [--entrypoint <name>] [-- arg1 arg2 ...]
<agent-binary> serve [--listen <addr>]
```

`run` executes a local script file and prints the result.

`serve` starts the gRPC server on the configured address. Only inline scripts are accepted over gRPC; file paths must be read by the caller and sent as source strings.

```protobuf
service AgentService {
  rpc Describe(DescribeRequest) returns (DescribeResponse);
  rpc RunOnce(RunRequest) returns (RunResponse);
}
```

`Describe` returns agent metadata (name and registered modules).

`RunOnce` accepts an inline script source, optional string arguments, and optional limits. Over gRPC the runtime always executes `main`; callers cannot select another entrypoint. Results are returned as either `RunSuccess` or `RunFailure`.

`timeout_s` is enforced cooperatively. The SDK checks timeouts at runtime boundaries, but pure Roto code and registered host functions are not forcibly interrupted mid-call in the current in-process runtime.

## Notes

Roto uses JIT compilation. On systems with stricter security policies, local execution may fail if executable memory
mappings are denied. If that happens, check your local SELinux or similar policy first.

## License

Licensed under [Apache-2.0](LICENSE).
