Docs / Start here

Getting started

You need Rust 1.89 or later. It runs on Linux and macOS; see platform evidence. Python, Node and model weights are not needed.

#Install

From a source checkout, until the first crates.io release:

sh
cargo install --path .

This installs one command, affordance. Everything below uses it; you do not write Rust to build an app.

#Render an app

sh
echo '{"name":"world"}' | affordance render views/demo/hello.tsx --state -

It prints Hello, world! [greet] and its spans. The greeting span marks text; the greet span marks a visible action and the handler it runs. --json prints ids and spans for another program. With no --tokenizer, render uses its bundled default (GPT-2), enough for an app that writes no model-specific chat format.

The app is views/demo/hello.tsx:

tsx
export default function Hello({name = "world"}) {
  return <box>
    <span label="greeting">{`Hello, ${name}!`}</span>
    <aff name="greet" onGreet={({name}) => ({name})}>{" [greet]"}</aff>
  </box>;
}

This is Affordance's TypeScript/JSX subset, interpreted in Rust (not React or Node). A render evaluates the app against its state, then layout decides its exact token IDs and the actions that survive. An action's handler changes the state; the next render shows it. See the language.

#Your first agent

sh
affordance init my-agent
cd my-agent

init writes these files:

Start a model server. No key is needed for a local one:

sh
llama-server -m your-model.gguf --port 8080 --jinja   # llama.cpp
# or: ollama serve      (then use --set model.url=http://127.0.0.1:11434/v1 --set model.model=<name>)
# or: vllm serve <model> --port 8080

Then run the agent:

sh
affordance run

affordance run finds affordance.yaml in the current directory (or a parent), runs the agent and prints a report when it stops: "stopped": {"answer": "..."}. Each agent's journal and trace are in .affordance/<agent>/. The journal records every input the agent received, so affordance run --resume continues the run exactly; affordance run --fresh starts over.

#Change a setting

A hosted API takes its key from an environment variable (read at each call, never written to the journal):

yaml
model:
  kind: chat
  url: https://api.example.com/v1
  model: some-model
  context: 128000
  key_env: EXAMPLE_API_KEY

See model bindings for SGLang and vLLM token-id serving, Anthropic and OpenAI Responses, and running agents for the full deployment format.

#The library

An app imports the bundled TSX library with @affordance/ paths:

tsx
import { useLog } from "@affordance/lib/log.tsx";
import { ModelContext } from "@affordance/lib/model.tsx";

@affordance/ names the library as an import map names a package: the copy bundled with the affordance command, or a directory the deployment names with library: path/to/views. Your own files import each other by relative path.

#Next

Source: docs/GETTING_STARTED.md, about 3 minutes to read.