# A New AI Agent Oriented Programming Language

> Source: <https://github.com/itsmedit/grenat>
> Published: 2026-10-07 20:09:43+00:00

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**.

``` php
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`](https://github.com/itsmedit/grenat/blob/main/SPEC.md)
- A compact reference for LLMs writing Grenat: [`llms.txt`](https://github.com/itsmedit/grenat/blob/main/llms.txt)
- Examples: [`basics.grn`](https://github.com/itsmedit/grenat/blob/main/examples/basics.grn) ,[` reviews.grn`](https://github.com/itsmedit/grenat/blob/main/examples/reviews.grn) (native statistics + validated LLM analysis),[`explorer.grn`](https://github.com/itsmedit/grenat/blob/main/examples/explorer.grn) (a real agent),[`support_desk.grn`](https://github.com/itsmedit/grenat/blob/main/examples/support_desk.grn) (multi-agent, human approval),[`triage.grn`](https://github.com/itsmedit/grenat/blob/main/examples/triage.grn) (tests with mocks, evals with an LLM judge),[`macros.grn`](https://github.com/itsmedit/grenat/blob/main/examples/macros.grn) (compile-time code generation),[`usecases/`](https://github.com/itsmedit/grenat/blob/main/examples/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.

``` php
## 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.

``` php
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`.

```
# config/models.yml — the first model is the default one
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` finds`lib/grenat/libgrenat_{host,standalone}.a` | 
| `CC` | the linker of `grenat build` (default:`cc` ) | 

[`llms.txt`](https://github.com/itsmedit/grenat/blob/main/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
# options: sh -s -- --version v0.1.1 | --prefix DIR | --no-modify-path | --uninstall
```

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
# your application's image
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/`](https://github.com/itsmedit/grenat/blob/main/facets): native code the application trusts
explicitly, built by `setter install`.

```
# Facetfile
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`](https://github.com/itsmedit/grenat/blob/main/facets/sheets/README.md) and [` facets/html`](https://github.com/itsmedit/grenat/blob/main/facets/html/README.md).

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> { … }
# Facetfile
facet "sheets", "~> 0.1", native: true

# generated: native def read_sheet(path: String) -> ~Array(Array(String)) uses fs.read
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):

```
# bridge/server.rb, started by `[bridge] command = ["ruby", "bridge/server.rb"]` in grenat.toml
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
# Facetfile
facet "texts", "~> 0.1", bridge: true

# generated: native def slug(title: String) -> String pure
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 what`on_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` and`grenat 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, loading 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](https://github.com/itsmedit/grenat/blob/main/LICENSE-MIT) or [Apache 2.0](https://github.com/itsmedit/grenat/blob/main/LICENSE-APACHE).
