SC Node is an experimental, provider-neutral Rust agent harness for executing tool-using AI agents across local and cloud models.
Public Alpha · Windows and Linux tested · Ollama and NVIDIA NIM live-tested · API may change
Most agent frameworks are large, opinionated, and tied to a single vendor or a heavyweight runtime. SC Node is the opposite: a small Rust execution layer that runs a tool-using agent loop against whichever model you point it at — a local Ollama model or an OpenAI-compatible cloud endpoint — with the safety controls you actually want and nothing you don't. It is designed for low overhead, local-first defaults, and honest, inspectable behaviour.
This is an experimental public alpha. The API may change. Windows and Linux are exercised by the full CI gate; macOS remains unverified. Feedback is welcome.
A lightweight Rust execution layer for agent loops. Its core provides:
- an agent loop that sends a task to a model, receives text and/or tool-call requests, executes tools, and feeds the results back for the next round (up to a bounded number of rounds);
- streaming responses (incremental SSE for OpenAI-compatible cloud providers);
- tool dispatch through a central permission gate;
- deterministic routing that resolves which provider/model handles a task, with local-first defaults and explicit cloud opt-in;
- a set of optional control layers you can turn on or off (see below).
- Not a desktop app. It is a command-line binary (
sc-agent) and a set of Rust crates. - Not a no-code builder. Agents are configured in TOML and driven from a CLI.
- Not a ChatGPT replacement. It orchestrates models you bring; it is not a hosted chat product.
- Not a finished end-user application. It is an alpha harness for developers.
- Provider-neutral agent loop with a tool-result feedback cycle.
- Real Ollama provider (local, on by default) and real NVIDIA NIM provider (cloud, opt-in), plus a shared OpenAI-compatible client used by cloud adapters.
- Deterministic 5-step provider/model routing with a hard cloud gate.
- File tools (
read_file,write_file,list_dir) and ashelltool. - Append-only JSONL audit log wired into every tool execution.
- No telemetry: zero outbound calls unless you enable a provider.
These controls are modular and configurable — not mandatory overhead. Enable what fits your risk tolerance:
| Layer | What it does | Default |
|---|---|---|
Workspace boundary (sc-sandbox) |
Canonicalizes tool paths and checks them against an allow/deny list | On for file tools (empty allowlist = deny all) |
Permissions (sc-tool-core) |
Per-tool allow/ask/deny policies + allow/deny patterns, fail-closed |
On (ask by default) |
| Approval gate | Interactive y/N/a prompt in the REPL on a TTY; run fails closed |
REPL only |
Audit (sc-audit) |
Append-only JSONL record of every tool call | On (can be disabled) |
Contracts (sc-contract) |
Strict, fail-closed TOML execution-policy documents + policy hash | Opt-in (contract subcommand) |
Proof (sc-proof) |
Proof bundles with a SHA-256 hash chain over audit events + secret redaction | Opt-in (proof subcommand) |
Memory / RAG (sc-memory) |
Backend-agnostic memory store + reference backend | Present, not yet wired into the runtime |
git clone https://github.com/SC-LABS-ai/sc-node
cd sc-node
cargo build --release --locked
# Create the default config at ~/.sc-agent/config.toml
./target/release/sc-agent init
# Run a one-shot task (requires Ollama running locally by default)
./target/release/sc-agent run "List the Rust files in the current directory"
# Or start the interactive REPL (interactive approval prompts require a TTY)
./target/release/sc-agent replOn Windows the binary is target\release\sc-agent.exe. You can also run via
cargo run --release -- <args> during development.
- A recent stable Rust toolchain. The workspace uses edition 2024, which requires Rust 1.85 or newer.
- No system libraries beyond what Cargo pulls in (TLS is via
rustls, not OpenSSL).
Built and tested on Windows and Linux through separate CI jobs. macOS is unverified so far.
Ollama is enabled by default and needs no API key.
ollama serve
ollama pull llama3.2:3b
./target/release/sc-agent run "Summarize the README in two sentences"See examples/ollama/ for a fuller walkthrough.
Cloud providers are opt-in. Enable NVIDIA NIM in your config and supply the key via an environment variable — never in the config file.
# Windows PowerShell
$env:SC_AGENT_NVIDIA_API_KEY = "<your key>"# ~/.sc-agent/config.toml
[providers.nvidia]
enabled = true
base_url = "https://integrate.api.nvidia.com/v1"
default_model = "meta/llama-3.3-70b-instruct"See examples/nvidia-nim/ for details.
The sc-provider-core crate provides a shared OpenAI-compatible client that
normalizes the differences between endpoints:
- tools are serialized in the
{"type":"function","function":{…}}envelope that strict endpoints (e.g. NVIDIA NIM) require; - a top-level
systemparameter is folded into a leadingsystemmessage; - responses are decoded as incremental Server-Sent Events (SSE).
NVIDIA NIM uses this client and is live-tested. The OpenRouter adapter has verified public-catalog metadata and deterministic adapter tests; an authenticated completion remains pending. See docs/PROVIDERS.md.
| Command | Description |
|---|---|
sc-agent run <task> |
Execute a single task and exit |
sc-agent repl |
Start the interactive REPL |
sc-agent init (alias config-init) |
Create the default config at ~/.sc-agent/config.toml |
sc-agent config-show |
Print the parsed config |
sc-agent config-set <key> <value> |
Stub — prints "not yet implemented" |
sc-agent providers-list |
List configured providers |
sc-agent models-list |
List models from enabled providers |
sc-agent audit-show [--last N] |
Show recent audit entries (default 50) |
sc-agent workspace-add <path> |
Stub — prints the manual config edit to make |
sc-agent doctor |
Check provider health, config, tools, workspace |
sc-agent contract validate <path> |
Validate a contract (fail-closed) and print its policy hash |
sc-agent contract explain <path> |
Print a human-readable contract summary |
sc-agent proof verify <path> |
Verify a proof bundle's hash chain and event count |
The workspace crates are not published to crates.io yet. An intentional alpha compatibility baseline is documented in docs/API_STABILITY.md and compiled from a separate consumer crate on Windows and Linux. Transport internals and runtime wiring remain experimental. You can use the reviewed crates as path dependencies, e.g. the fail-closed execution-contract parser:
// Cargo.toml: sc-contract = { path = "…/sc-node/crates/sc-contract" }
use sc_contract::ExecutionContract;
let text = std::fs::read_to_string("task.contract.toml")?;
let contract = ExecutionContract::parse(&text)?; // strict, fail-closed
println!("policy hash: {}", contract.policy_hash()?);flowchart TD
CLI["sc-agent (CLI)"] --> LOOP["agent core loop"]
LOOP --> ROUTER["deterministic router"]
ROUTER --> PROV["provider adapters"]
PROV --> OLL["Ollama (local)"]
PROV --> NIM["NVIDIA NIM"]
PROV --> OAI["OpenAI-compatible / OpenRouter"]
LOOP --> DISPATCH["tool dispatch + permission gate"]
DISPATCH --> FILE["file tools"]
DISPATCH --> SHELL["shell tool"]
SANDBOX["workspace boundary"] -.-> DISPATCH
PERMS["permissions / approval"] -.-> DISPATCH
AUDIT["audit log"] -.-> DISPATCH
CONTRACT["contracts"] -.-> DISPATCH
PROOF["proof bundles"] -.-> AUDIT
MEMORY["memory / RAG (not yet wired)"] -.-> LOOP
Solid arrows are the live execution path; dotted boxes are optional control
layers. memory / RAG exists as a crate but is not yet wired into the
runtime. Full detail: docs/ARCHITECTURE.md.
main.rsparses the CLI, loads the config, builds the enabled providers, the audit logger, and the tool registry into aSession.- The deterministic router resolves one provider/model for the task (explicit override → matching rule → configured fallback → first enabled local provider, with cloud gated behind an explicit opt-in).
- The selected provider streams a response; text is printed and tool-call requests are collected.
- Each tool call passes through the central permission gate: the decision is
resolved (
Allow/Ask/Deny) before any I/O;Askis prompted in the REPL or denied inrun. - Allowed tools execute; file paths are checked against the workspace boundary and shell commands run as an argument vector with a timeout.
- Every outcome is written to the audit log, and the tool result is fed back into the conversation for the next round (bounded by a max-rounds limit).
Config lives at ~/.sc-agent/config.toml on every platform (literally
<home>/.sc-agent/config.toml — not %APPDATA% or ~/.config). Set the
SC_AGENT_CONFIG environment variable to use an alternate path.
[workspace]
allow = ["~/projects", "~/Documents"]
[permissions]
default_policy = "ask"
[providers.ollama]
enabled = true
base_url = "http://localhost:11434"
default_model = "llama3.2:3b"
[providers.nvidia]
enabled = false # opt-in; key via SC_AGENT_NVIDIA_API_KEY
[routing]
fallback_provider = "ollama"
fallback_model = "llama3.2:3b"The fully-commented reference is
examples/config.example.toml.
- API keys are read from environment variables only
(
SC_AGENT_NVIDIA_API_KEY,SC_AGENT_OPENROUTER_API_KEY). The key field is#[serde(skip)], so a key placed in the TOML is ignored and a key is never written back byconfig-show. - Keys are never written to disk and are redacted from provider error messages.
- Cloud providers are off by default; there is no silent cloud fallback (local-first routing, cloud only via an explicit rule/fallback/override).
- The shared cloud client refuses to attach a key to a non-
https, non-local base URL.
cargo test --workspace --lockedCross-platform smoke check (PowerShell 7 on Linux, Windows PowerShell or PowerShell 7 on Windows):
./scripts/smoke-check.ps1Additional Windows-oriented verification helpers:
powershell -ExecutionPolicy Bypass -File .\scripts\verify-local.ps1
powershell -ExecutionPolicy Bypass -File .\scripts\verify-public-beta.ps1The workspace ships an inline unit-test suite across the crates (routing, permissions, sandbox, providers, contracts, proof, audit, config). Tests are offline and deterministic; live provider calls only run when a real API key is present in the environment.
A first reproducible reference run is published with all raw measurements. On
the measured Windows/qwen:latest configuration, the independent warm medians
were 533.4315 ms direct and 534.5057 ms through SC Node — an observed
difference of 1.0742 ms (0.2014%). A deterministic fixture measured a paired
internal median of +0.2135 ms. These are configuration-specific measurements,
not a universal framework or Rust-vs-Python claim. See
docs/BENCHMARKING.md and the
published evidence.
- Interactive approval is available only in the REPL on a real TTY;
sc-agent run(and a piped REPL) fail closed — anaskdecision is denied, not prompted. - The shell deny-list is a substring blocklist, not a command parser; flag reordering can evade it (documented and tested).
- Shell command argument paths are not workspace-bounded — only the working directory is.
- No per-process resource limits (CPU/memory/pids); no Windows Job Object or Linux cgroup containment.
- The audit log is append-only, not cryptographically tamper-evident.
- The sandbox has a check-then-open (TOCTOU) window.
sc-memoryis present but not wired into the runtime.- Ollama streaming is batch-collect; cloud (OpenAI-compatible) streaming is incremental.
- Verified on Windows and Linux; macOS remains unverified. OpenRouter's public catalog is verified, while authenticated completion remains pending.
See docs/SECURITY_MODEL.md and THREAT_MODEL.md for the full analysis.
Near-term direction (no dates promised) — see docs/ROADMAP.md:
stabilize the public Rust API, verify macOS, wire sc-memory into the runtime,
and complete authenticated OpenRouter verification. The first reproducible benchmark set is now
published.
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
See SECURITY.md. Report vulnerabilities privately to contact@sclabs.uk rather than opening a public issue.
Dual-licensed under either of MIT or Apache-2.0 at your option.