Ruby's syntax, Rust's speed, agents as first-class citizens.
Grenat is a compiled programming language for building AI agent systems: typed prompts, tools, supervised actor agents, budgets, durable workflows, and an effect system that turns prompt injection into a compile-time error.
prompt summarize(article: String) -> ~Summary using :fast
user "Summarize: #{article}"
end
agent Researcher
model :smart
tools search_web, read_url
budget usd: 2.00, time: 10.min
on Research(topic: String) -> ~Report
run "Investigate #{topic}"
end
end
- Specification:
SPEC.md - A compact reference for LLMs writing Grenat:
llms.txt - Examples:
basics.grn,reviews.grn(native statistics + validated LLM analysis),explorer.grn(a real agent),support_desk.grn(multi-agent, human approval),triage.grn(tests with mocks, evals with an LLM judge),macros.grn(compile-time code generation),usecases/(twelve agent use cases: support, code review, research, data, documents, a weekly digest, operations, a chat with memory, a team of agents, MCP tools, a knowledge base searched by meaning, meeting minutes from a recording)
Every snippet below passes grenat check, grenat test and grenat fmt --check.
A prompt is a function a model implements. Its return type becomes a JSON schema, and the
## comments describe the fields to the model. A model's answer is untrusted (~T): it
must be checked, approved by a human, or explicitly trusted before it reaches the network, a
file, a command, an email or a page β otherwise grenat check fails (E0412).
struct Summary
title: String ## 8 words at most
bullets: Array(String) ## 3 to 5 key points
end
## Summarizes an article.
prompt summarize(article: String) -> ~Summary using :fast
user "Summarize:\n#{article}"
end
def headline(article: String) -> String uses llm
summarize(article).check { |s| s.bullets.size.between?(3, 5) }?.title
end
A tool is a function a model may call; an agent is an actor whose run loop calls the
model and its tools until it produces the handler's return type, within a budget. Effects
(uses β¦) are capabilities checked by the compiler, then again at run time.
## Reads a page of the handbook.
tool read_page(name: String) -> String uses fs.read("./handbook")
File.read("./handbook/#{name}")
end
## Opens a ticket. A human approves it first.
tool open_ticket(title: String) -> Int uses net("tracker.acme.io"), human, env
approve! "Open β#{title}β?"
token = Credentials.fetch(:tracker, :token) # a Secret: never printed, never sent to a model
Http.post(
"https://tracker.acme.io/tickets",
json: {title:},
headers: {"Authorization" => "Bearer #{token}"},
).status
end
agent Support
model :smart
tools read_page, open_ticket
budget usd: 0.50, time: 2.min
max_turns 12
instructions "Answer from the handbook only. Open a ticket for bugs."
on Ask(question: String) -> ~String
run "Customer question: #{question}"
end
end
def answer(question: String) -> ~String uses llm, fs.read("./handbook"), net("tracker.acme.io"), human, env
spawn(Support).ask(Ask(question:))
end
Each step of a workflow is journaled: after a crash, or a human answering days later, the
run resumes where it stopped and no model call is billed twice.
def recent_releases(repo: String) -> Array(String) uses net("api.github.com"), env, time
token = Credentials.fetch(:github, :token)
res = Http.get(
"https://api.github.com/repos/#{repo}/releases",
headers: {"Authorization" => "Bearer #{token}"},
)
week_ago = Time.now - 7.days
res.json.trust!.select { |r| Time.parse(r["published_at"]) > week_ago }.map { |r| r["tag_name"] }
end
workflow weekly_digest(monday: String) uses llm, net("api.github.com"), net("smtp.acme.io"), env, human, time
tags = step(:fetch) { recent_releases("rust-lang/rust") }
digest = step(:summarize) { summarize(tags.join(", ")).trust! }
step(:review) { approve! "Send β#{digest.title}β?" }
step(:email) do
Mail.connect(Credentials.fetch(:smtp, :url)).send(
from: "bot@acme.io",
to: ["team@acme.io"],
subject: digest.title,
body: digest.bullets.join("\n"),
)
end
end
every cron: "0 8 * * MON" do # UTC, run by `grenat serve`
weekly_digest(Time.today)
end
## Answers a customer, three sentences at most.
prompt reply(question: String) -> ~String using :fast
user question
end
get "/chat" do |req|
stream do |out| # Server-Sent Events, as the model writes
reply(req.params["q"]) { |chunk| out << chunk }
end
end
grenat serve reads a mailbox β Gmail, Microsoft 365, any IMAP server β and hands each new
email to its handler, then marks it seen or moves it; every field of it is untrusted.
on_email Credentials.fetch(:support, :imap_url), every: 1.minute, move_to: "Done" do |email|
answer = reply(email.text).check { |a| a.size < 2000 }?
puts "#{email.attachments.size} attachments, answer ready: #{answer.size} characters"
end
struct Passage
table :passages
id: Int?
text: String
embedding: Vector(1024)
end
migration "001_create_passages" do |db|
db.migrate("CREATE TABLE passages (id #{db.primary_key}, text TEXT NOT NULL, embedding #{db.vector(1024)} NOT NULL)")
end
def index(parts: Array(String)) uses llm, db
vectors = embed(:docs, parts) # one request for many texts
parts.each_with_index { |text, i| Passage.create(text:, embedding: vectors[i]) }
end
def search(question: String) -> Array(Passage) uses llm, db.read
Passage.nearest(:embedding, embed(:docs, question), limit: 3) # pgvector, or brute force on SQLite
end
php
def restart(server: SshSession) -> Bool uses ssh("api.acme.com")
server.run(["systemctl", "restart", "shop"]).ok? # an argument vector: no shell injection
end
def main uses ssh("api.acme.com"), env
server = Ssh.connect("deploy@api.acme.com", key: Credentials.fetch(:deploy, :ssh_key))
puts restart(server)
end
test "only this week's releases" do
freeze_time("2026-10-05T08:00:00Z") do
mock_http "GET https://api.github.com/repos/rust-lang/rust/releases", json: [
{tag_name: "1.95.0", published_at: "2026-10-01T10:00:00Z"},
{tag_name: "1.94.0", published_at: "2026-08-20T10:00:00Z"},
]
assert_equal ["1.95.0"], recent_releases("rust-lang/rust")
assert_equal "Bearer test-github-token", Http.requests.last["headers"]["Authorization"]
end
end
test "the agent reads the handbook, then answers" do
File.write("./handbook/refunds.md", "Refunds: within 30 days.")
mock :smart, replies: [
call(:read_page, name: "refunds.md"),
"Refunds are possible within 30 days.",
]
assert_equal "Refunds are possible within 30 days.", answer("Can I get a refund?").trust!
end
test "a model answer out of bounds is refused" do
mock :fast, replies: [{title: "Rust 2.0", bullets: ["only one"]}]
assert_raises CheckError do
headline("β¦")
end
end
test "the chat streams its answer" do
mock :fast, replies: ["Hello, Ada"]
assert_equal "Hello, Ada", request(:get, "/chat?q=hi")["events"].first["data"]
end
test "the passage about refunds is found" do
mock_embed :docs # vectors made from the texts' words
index(["A refund is asked for within 30 days.", "Invoices export to CSV."])
assert_equal "A refund is asked for within 30 days.", search("How do I get a refund?").first&.text
end
Other doubles: mock_shell, mock_ssh, mock_mcp, mock_transcribe, mock_env,
mock_mail(raise: "SMTP down"), mock_credentials, cassette (real calls recorded once,
then replayed), with_human(approve_all | deny_all), deliver_webhook, Jobs.perform,
Mail.deliveries.
fast:
provider: anthropic # anthropic, openai, gemini, mistral, xai, openrouter, groq, deepseek, together, ollama
name: claude-haiku-4-5
smart:
provider: openai
name: gpt-5
docs:
provider: voyage # embeddings: voyage, openai, gemini, mistral, ollama
name: voyage-3.5
kind: embedding
dimensions: 1024
grenat credentials edit # config/credentials.yml.enc, AES-256-GCM, key in config/master.key
grenat credentials edit --env production # one per environment, chosen by GRENAT_ENV
Keys are read from the credentials (openai: {api_key: β¦}), else from the provider's variable
(OPENAI_API_KEYβ¦). Anthropic agents use prompt caching by default ( cache: true extends it to
prompts and conversations).
grenat new <name> a package: grenat.toml, src/, tests/
grenat new --app <name> an application: database, models, routes, src/app.grn, tests/
grenat generate agent|workflow|record|tool|eval <name> [field:Typeβ¦]
a part of the application, with its tests (alias: grenat g)
grenat check [<file.grn>β¦] names, types, effects, taint and secrets
grenat run [--log] [--unchecked] [--no-jit] [<file.grn>] [argsβ¦]
check, then run `main`
grenat test [<file.grn>β¦] the `test` blocks, offline (mocks and cassettes)
grenat eval <file.grn> [name] the `eval` blocks, against the real models, scored
grenat serve [--listen host:port] routes, webhooks, schedules, mailboxes, exposed tools and agents, job workers
grenat console [--listen host:port] [--token <token>]
the operations console: approvals, jobs, journals, costs, evals
grenat migrate apply the migrations the database has not seen
grenat credentials edit|show [--env <environment>]
the application's encrypted secrets
grenat build [--native] [--release] [<file.grn>] [-o <executable>]
an executable (--native: without the interpreter;
--release: optimized by LLVM)
grenat fmt [--check] <file.grn | dir>β¦ the canonical layout
grenat lsp the language server
grenat update the latest commits of git dependencies
grenat parse | tokens <file.grn> the syntax tree, the tokens
grenat --version
setter init add a Facetfile to the current package
setter new <name> create a facet (a library to share)
setter add <name> ["~> 1.2"] use a facet from the indexes (or --path <dir>, --git <url> [--tag <tag>])
setter install | update | list install (and build trusted native facets), update, list
setter publish tag this facet's version for the indexes
| Variable | Effect |
|---|---|
ANTHROPIC_API_KEY ,OPENAI_API_KEY , β¦ |
a provider's key, when the credentials have none |
GRENAT_LOG=1 |
log every model, tool, HTTP and SSH call, and what the JIT compiled (same as --log ) |
GRENAT_RECORD=1 |
record every cassette again, with real calls |
GRENAT_ENV |
the environment ( development by default): which credentials |
GRENAT_MASTER_KEY |
the credentials' key, rather than config/master.key |
GRENAT_CONSOLE_TOKEN |
the token of grenat console (rather than--token ) |
GRENAT_JIT=0 |
interpret everything (same as --no-jit ) |
GRENAT_HOME |
where grenat build findslib/grenat/libgrenat_{host,standalone}.a |
CC |
the linker of grenat build (default:cc ) |
llms.txt is a reference of about 4,000 tokens written for models: the syntax, the
effects, taint, agents, workflows, the standard library, the test doubles and the mistakes to
avoid. Every code block in it passes grenat check and grenat test.
It was measured: agents were given two real tasks β a support agent (tools, a ticket opened after
human approval, a structured answer) and a weekly release digest (GitHub, a validated summary,
email, resumption after a crash without calling the model again) β once in Python with the
official Anthropic SDK, once in Grenat with llms.txt as its only documentation, twice each.
Every program passes its tests.
| Average of 2 runs | Python | Grenat |
|---|---|---|
| Tokens, support agent | 79,700 | 43,000 (β46%) |
| Tokens, weekly digest | 38,300 | 45,000 (+18%) |
| Tokens, both tasks | 118,000 | 88,000 (β25%) |
| Lines of program, support / digest | 237 / 207 | 64 / 79 |
The agent loop, structured answers, approvals and journaled steps are part of the language, so the code a model writes is 2.5 to 3.5 times shorter; where a task is mostly plumbing (HTTP, dates, email), Python's familiarity still pays.
The latest release, v0.1.2, has phases 0 to 11; phases 12 to 15 are on main.
Phase 15 β email in: on_email reads a mailbox (Gmail, Microsoft 365, any IMAP server, over
TLS; app passwords or OAuth tokens) under grenat serve, each new email β its PDFs and images
ready for a prompt β handled once then marked seen or moved, a failing one retried then flagged,
every field untrusted, a crafted email flagged before it is parsed; deliver_email hands a handler messages in tests.
Phase 14 β the gaps LLMs found: a benchmark of models writing Grenat from llms.txt showed
what they reach for. Time (Time.parse for ISO 8601, Time.iso, Time.date, Time.weekday,
Time.at, Time.now - 7.days, freeze_time in tests), Ruby's everyday methods (s[0, 4],
s[1..], flatten, each_slice, reduce(:+), transform_values, format, thenβ¦), test
doubles (Http.requests to assert what was sent, mock_env, mock_mail(raise:)), and email
held to the net effect by the checker.
Phase 13 β what agents need in production: embeddings and search by meaning (embed,
Vector(n) fields, nearest, pgvector or brute force), prompt caching (Anthropic breakpoints
placed by Grenat, cached tokens in the ledger and the console), streaming (blocks receiving the
answer as it is written, Server-Sent Events from routes), and audio (Audio.read, transcribe,
audio in prompts).
Phase 12 β libraries in other languages: native facets (Rust code called as ordinary
functions, behind a versioned ABI), bridge facets (Ruby or Python functions in a sandboxed
process over JSON-RPC β no interpreter embedded), and two official facets: sheets (Excel,
OpenDocument, CSV) and html (CSS selectors, links, tables).
Phase 11 β reaching servers: Ssh.connect, commands as argument vectors, upload,
download, SFTP ( list, read, write, renameβ¦), host keys always verified; proxies for
Http (SOCKS5, SOCKS4, HTTP) and for SSH.
Phase 10 β configured, not coded: secrets encrypted per environment as with Rails
(grenat credentials edit), as Secret values the language keeps away from models and logs;
models of ten providers in config/models.yml, each reached by the right connector with its key
found in the credentials β the provider's name is enough.
Phase 9 β agents operated from a browser: grenat console, open source like the rest β
approvals waiting for a human, jobs and their workflow journals (retry), what the models cost
by agent, workflow and day, eval scores over time, failures and refusals, MCP servers. The
runtime records what it shows in the application's database.
Phase 8 β applications of agents, in the language and its toolchain, with no
framework on top: routes, records and migrations (SQLite and PostgreSQL), jobs, approvals
that wait days for a human in the database, tools and agents served to other programs over
MCP and HTTP (expose), and generators β grenat new --app, then
grenat generate agent|workflow|record|tool|eval, each part with its tests.
Phase 7 β agents in production, measured by ten real use cases (examples/usecases):
an HTTP client, databases, email, a sandboxed Shell, MCP servers, PDFs and images,
conversations with long-term memory, the Batch API, schedules and webhooks
(grenat serve), and facets β libraries installed by setter from a Facetfile.
Phase 6 β ecosystem: programs of several files and packages (grenat.toml, path
and git dependencies, grenat.lock), a language server ( grenat lsp), compile-time
macros, and release builds optimized by LLVM (grenat build --release).
Phase 5 β production-ready: workflows are durable β each step is journaled, and an
interrupted run resumes where it stopped, without paying twice for a model call. Tests
never reach a real model: mock gives the model's replies as plain values, cassette
records real calls once and replays them. eval measures quality on a dataset, with
judge (an LLM as a judge), and fails under a threshold.
Phase 4 β native code: functions over numbers, strings, arrays and structs are
compiled to machine code by a Cranelift JIT when the program loads β fib(35) runs in
0.05 s, about 1.7Γ Rust with the same overflow semantics, 200Γ faster than the
interpreter. Objects are reference counted, Perceus style: no garbage collector, no leak,
in-place updates of uniquely owned values. grenat build compiles a program ahead of time
into a standalone executable. Tasks are M:N green threads: 100,000 concurrent tasks fit in
~1 GB on a few OS threads. Agents are
actors (one message at a time, deadlocks detected, supervision with restarts), and
parallel_map and race run truly in parallel. Before running anything, grenat checks
names, types, effects and taint: an unvalidated model answer that reaches the network is
a compile-time error.
macOS or Linux, with Homebrew:
brew install itsmedit/grenat/grenat
Any Linux with glibc (Ubuntu 20.04+, Debian 11+, Fedora, RHEL 9, Amazon Linux 2023β¦) or macOS, without a package manager β the install script puts grenat and setter in ~/.grenat (in /usr/local as root), checks the archive's SHA-256, and adds them to your PATH:
curl -sSL https://github.com/itsmedit/grenat/releases/latest/download/install.sh | sh
A fresh EC2 instance (Amazon Linux 2023), for instance:
sudo dnf install -y gcc # the C linker `grenat build` uses (run, test and serve need none)
curl -sSL https://github.com/itsmedit/grenat/releases/latest/download/install.sh | sh
exec $SHELL -l # a new shell, with grenat on the PATH
grenat new --app hello && cd hello && grenat test
apt or dnf, with the packages of a release:
curl -LO https://github.com/itsmedit/grenat/releases/download/v0.1.2/grenat_0.1.2_amd64.deb
sudo apt install ./grenat_0.1.2_amd64.deb # Ubuntu, Debian (arm64: _arm64.deb)
sudo dnf install https://github.com/itsmedit/grenat/releases/download/v0.1.2/grenat-0.1.2-1.x86_64.rpm # Fedora, RHEL, Amazon Linux (aarch64: .aarch64.rpm)
Docker β the official image, for amd64 and arm64, with a C linker for grenat build:
docker run --rm -v "$PWD":/app ghcr.io/itsmedit/grenat test
docker run --rm -v "$PWD":/app -p 3000:3000 ghcr.io/itsmedit/grenat serve --listen 0.0.0.0:3000
FROM ghcr.io/itsmedit/grenat:0.1.2
COPY . /app
CMD ["serve", "--listen", "0.0.0.0:3000"]
From the sources (Rust, and a C linker: Xcode's command line tools on macOS):
cargo install --locked --path crates/grenat_cli && cargo install --locked --path crates/grenat_setter
cargo build --release -p grenat_host -p grenat_standalone # the libraries `grenat build` links
mkdir -p ~/.cargo/lib/grenat && cp target/release/libgrenat_{host,standalone}.a ~/.cargo/lib/grenat/
Alpine (musl) is not supported by the binaries: use a glibc distribution, or the Docker image.
cargo build
target/debug/grenat run examples/basics.grn # the core language, no LLM
target/debug/grenat run --log examples/fib.grn # native code: see what the JIT compiled
target/debug/grenat run --log examples/objects.grn # strings, arrays, structs, natively
target/debug/grenat build examples/objects.grn && ./objects # a standalone executable (needs `cc`)
target/debug/grenat build --native examples/objects.grn # without the interpreter: ~0.5 MB
target/debug/grenat build --native --release examples/fib.grn # optimized by LLVM (needs clang)
export ANTHROPIC_API_KEY=sk-ant-β¦ # or, in an application: grenat credentials edit
target/debug/grenat run --log examples/explorer.grn crates/grenat_parser # a real agent
target/debug/grenat run examples/support_desk.grn examples/tickets.jsonl # multi-agent + approval
target/debug/grenat new --app desk && cd desk # an application: database, models, routes, tests
grenat generate agent triage # a part and its tests (also workflow, record, tool, eval)
grenat generate record doc text:String "embedding:Vector(1536)" # a record searched by meaning
grenat migrate && grenat test && grenat serve
grenat console # its operations console: http://127.0.0.1:4000
target/debug/grenat new hello && cd hello # a package: grenat.toml, Facetfile, src/, tests/
setter add http_tools # a facet (library) from an index, like a gem
grenat run && grenat test # in a package, no file to name
target/debug/grenat check examples/*.grn # names, types, effects, taint
target/debug/grenat fmt examples # canonical layout (--check: only report)
target/debug/grenat test examples/triage.grn # `test` blocks: mocks and cassettes, never a real model
target/debug/grenat eval examples/triage.grn # `eval` blocks: the real model, scored on a dataset
cargo test # ~950 tests: unit, integration, CLI, HTTP, MCP, SSH, JIT, build
scripts/test-linux.sh # the same suite on Linux, in Docker
Two facets ship with Grenat, in facets/: native code the application trusts
explicitly, built by setter install.
facet "sheets", path: "../grenat/facets/sheets", native: true
facet "html", path: "../grenat/facets/html", native: true
require "sheets"
require "html"
def main uses fs.read, fs.write, net("acme.io")
orders = Sheets.records("orders.xlsx", sheet: "2026").trust! # .xlsx, .xls, .ods, CSV
big = orders.select { |o| o["total"].to_f > 1000.0 }
Sheets.write_csv("big_orders.csv", Sheets.table(big, ["id", "customer", "total"]))
page = Http.get("https://acme.io/pricing").body
prices = table_records(page, "table.prices") # untrusted, as the page
puts prices.size
end
See facets/sheets and facets/html.
A facet can ship Rust code, as a gem ships C: a crate depending on grenat_ext, whose
exported functions Grenat calls as ordinary ones. The application trusts it explicitly β
it runs outside Grenat's sandbox β and setter install builds it and writes its declarations:
/// Reads a sheet: a line per row, cells separated by commas.
#[grenat_ext::export(effects = "fs.read")]
pub fn read_sheet(path: String) -> Result<Vec<Vec<String>>, String> { β¦ }
facet "sheets", "~> 0.1", native: true
def main uses fs.read
puts read_sheet("sales.csv").trust!.size
end
Types and effects are checked like any call's; the result is untrusted unless the function is
pure, and no secret is ever handed to native code (see SPEC.md, phase 12).
A facet can also ship Ruby or Python functions β no interpreter is embedded in Grenat: the facet's server is a separate process, speaking JSON-RPC 2.0 on its standard input and output, with a helper library Grenat ships (standard library only):
require "grenat/bridge"
Grenat::Bridge.export(:slug, params: {title: :string}, returns: :string, pure: true) do |title:|
title.downcase.gsub(/[^a-z0-9]+/, "-")
end
Grenat::Bridge.run
python
facet "texts", "~> 0.1", bridge: true
def main
puts slug("Hello, World")
end
In Python, @export on an annotated function, then run() (from grenat_bridge import export, run).
The server runs sandboxed in the facet's directory β a clean environment, no network unless a
function declares net; a relative path it is given resolves there, so pass it absolute ones β
one per facet, kept alive, each call within a timeout; types, effects, taint and secrets are
checked as for native code (see SPEC.md, phase 12).
grenat lsp is a language server (diagnostics as you type, formatting, hover, go to
definition, symbols). In Neovim:
vim.filetype.add({ extension = { grn = "grenat" } })
vim.api.nvim_create_autocmd("FileType", { pattern = "grenat", callback = function()
vim.lsp.start({ name = "grenat", cmd = { "grenat", "lsp" } })
end })
| Crate | Role |
|---|---|
grenat_lexer |
tokens, interpolation, heredocs, ## doc comments |
grenat_ast |
syntax tree |
grenat_parser |
recursive descent + Pratt, diagnostics with error recovery |
grenat_llm |
model providers: the catalog, Anthropic's Messages API and Chat Completions (OpenAI, Gemini, Mistral, Ollamaβ¦), streaming, prompt caching, embeddings (those and Voyage), transcriptions (OpenAI's, uploaded as multipart/form-data ) and audio in prompts (OpenAI, Gemini); mocks, fake embeddings and transcripts, cassettes and a scripted provider for tests |
grenat_types |
checker: names, types, effects, ~T taint, secrets (E0100βE0500) |
grenat_codegen |
Cranelift: typing, liveness (Perceus), translation, boundary; JIT and object files; LLVM IR for release builds |
grenat_runtime |
reference-counted strings, arrays and records called by native code |
grenat_driver |
load, check and run a program (shared by the CLI and built executables) |
grenat_host |
static library linked into the executables of grenat build |
grenat_standalone |
static library linked into grenat build --native executables |
grenat_report |
diagnostic rendering, in the file each error points into |
grenat_db |
databases: SQLite (embedded) and PostgreSQL behind one interface; vectors (pgvector, or bytes searched by brute force) |
grenat_mcp |
the Model Context Protocol: a client (stdio and HTTP), and the server side of expose |
grenat_ssh |
SSH and SFTP: host keys verified, commands quoted, SOCKS5 proxies, a blocking API; a real server in process for tests |
grenat_imap |
IMAP over TLS (implicit or STARTTLS): password or OAuth ( XOAUTH2 ) logins, unseen messages by UID, flags and moves, MIME parsed into whaton_email gives; a server in process for tests |
grenat_serve |
triggers: cron schedules, calendar arithmetic, webhook signatures, the HTTP server of grenat serve , streamed responses |
grenat_generate |
grenat new --app andgrenat generate : an application's parts, with their tests |
grenat_ops |
the operations store: jobs, approvals, model calls, events, eval runs, workflow journals |
grenat_console |
grenat console : pages and actions of the operations console, and who may use it |
grenat_config |
the application's configuration: encrypted credentials per environment, config/*.yml |
grenat_setter |
setter : creates, adds, installs and publishes facets (libraries) |
grenat_package |
grenat.toml ,require , facets (Facetfile , versions, indexes, trusted native code and bridges), path and git dependencies |
grenat_ext |
the SDK of native facets: Rust functions exported to Grenat behind a versioned JSON ABI, and their manifest |
grenat_ext_macros |
#[grenat_ext::export] and#[derive(GrenatType)] |
grenat_native |
native facets, Grenat's side: building a facet's library, it (ABI checked), declaring and calling its functions |
grenat_bridge |
bridge facets, Grenat's side: a facet's Ruby or Python functions served by a process over JSON-RPC 2.0, described, declared and called |
grenat_sandbox |
sandboxed processes ( Shell.run , bridges): an argument vector, a clean environment, no network unless allowed |
grenat_fmt |
the formatter |
grenat_lsp |
the language server |
grenat_macros |
macro expansion: templates of declarations |
grenat_green |
M:N green threads: scheduler, green locks, channels, timers |
grenat_interp |
interpreter: values, evaluation, prompts, agents, budgets, taint, capabilities, workflows, test doubles, evals |
grenat_cli |
the grenat binary |
External dependencies: ureq (HTTP + rustls), serde_json, toml, yaml-rust2, rusqlite (SQLite, compiled in), postgres, russh (SSH), imap and mail-parser (email in, over rustls with ring), lettre (email out), and Cranelift for native code.
Your choice of MIT or Apache 2.0.