Install
Oska is pre-alpha. The latest release is v0.2.0. You can install oskac, the compiler and runner, from a signed binary with a one-line installer, or build it from source.
Set up an AI agent
Paste this prompt into Claude Code, Cursor, Codex, or any other coding agent. It installs Oska, checks the signature and the checksum, and sets up your project, and it asks before it changes a file. Or tell your agent: Read https://oskalang.dev/agent-setup.md and follow it.
agent-setup.md agent-setup.txt
# Set up Oska for AI-driven development
Install Oska, the secure-by-default programming language from https://oskalang.dev, and set up the project in the current directory so that agents can work in it. Do the steps in order, and stop at the first one that fails. Report the failure to me instead of working around it.
## Rules for this task
- Say what you are about to run before you run it.
- Pipe nothing to a shell except the official installer from oskalang.dev in step 1 (and, only if I approve it in step 4, the same installer in a CI file).
- Never ask for, read, print, or handle secrets: tokens, keys, passwords, or the contents of `.env` files. Nothing here needs one.
- Do not change an existing file until you have shown me the diff and I have agreed. Show me a new file's content before you create it.
- Do not change any permission or approval setting, and do not touch user-level or global settings. Write only inside this project, into the install directory `~/.oska`, and into the temporary directory of step 2, which you delete afterwards.
- Never skip a verification, and never work around a failed one.
## 1. Install
On Linux and macOS, run:
```sh
curl -fsSL https://oskalang.dev/install.sh | sh -s -- --json
```
On Windows, in PowerShell (it needs the OpenSSH Client), run:
```powershell
& ([scriptblock]::Create((irm https://oskalang.dev/install.ps1))) --json
```
The installer never prompts. It prints one line of JSON and nothing else. If it printed no JSON, the download failed: stop and tell me.
Read the JSON:
- If `ok` is not `true`, stop and tell me the `error` and the `message`.
- If `checksum_verified` and `signature_verified` are not both `true`, stop. Do not install by any other route: not by hand, not from a mirror, not with the checks turned off.
- Note `version`, `path`, `bin_dir`, and `on_path`. Run `oskac` below by its full `path`. If `on_path` is `false`, tell me to add `bin_dir` to my `PATH`. Do not edit my shell profile unless I ask.
## 2. Check the toolchain
Run `oskac --version --format=json` and confirm that it prints the same `version`. Then, in a temporary directory outside the project, save this as `hello.osk`:
```oska
fn main(io: Stdout) -> Result<(), IoError> {
io.println("Hello from Oska!")
}
```
Run `oskac check hello.osk` and then `oskac run hello.osk`. The first must report no diagnostic. The second must print `Hello from Oska!`. Delete the temporary directory afterwards.
## 3. Configure the project
Download each template below with `curl -fsSL` or your fetch tool and read it. A template is plain text. Do not run it, except the hook script in step 3c, and only if I opt in.
**3a. AGENTS.md.** The template is https://oskalang.dev/agent-setup/AGENTS.template.md. It holds a `## Working with Oska` section: the agent loop (check, apply machine fixes, run, test, read the authority report), the machine-readable flags (`--format=json`, `oskac explain <code>`), and the links to https://oskalang.dev/llms.txt and the error-code catalog. If `AGENTS.md` does not exist, create it with that section. If it does, append the section, and show me the diff first.
**3b. CLAUDE.md.** Only if I am using Claude Code. The template is https://oskalang.dev/agent-setup/CLAUDE.template.md, which points to `AGENTS.md`. If `CLAUDE.md` exists, add only its first line to it.
**3c. The check-after-edit hook (optional, Claude Code only).** The hook is a POSIX shell script, so it works on Linux and macOS only: on Windows, skip this step and tell me it is not available there yet. Ask me whether I want it, and do nothing if I do not say yes. It runs `oskac check --format=json` on a `.osk` file after Claude Code edits it, and it reads only the edit notification that Claude Code gives it. It sends nothing anywhere and approves no permission. If I say yes:
1. Show me https://oskalang.dev/agent-setup/oska-check.sh, and save it as `.claude/hooks/oska-check.sh` in this project.
2. Show me the diff that merges https://oskalang.dev/agent-setup/claude-settings.json into the project's `.claude/settings.json` (the `hooks` key only; keep every other key), and apply it when I agree. Never edit `~/.claude/settings.json`.
## 4. Run the tests in CI
Look for the project's CI or task runner: `.github/workflows`, a `Makefile`, a `justfile`, or scripts in `package.json`. If there is one, propose a step that runs `oskac test <file.osk>` for each file that has tests, show me the diff, and apply it when I agree. A CI job needs `oskac` too: install it with the same installer, pinned with `--version` to the version you installed. If there is no CI or task runner, say so and add nothing.
## 5. Report
Tell me:
- the `version` you installed, and its `path`;
- each file you added or changed, and each one you left alone;
- how to update: run the installer again. It installs the latest release over the old one, and there is no self-update command.
The documentation index for agents is https://oskalang.dev/llms.txt.
Build from source
You need a stable Rust toolchain, version 1.77 or newer, and a checkout of the repository:
cargo build --release -p oskac
The binary is target/release/oskac. Copy it somewhere on your PATH, or let Cargo install it into ~/.cargo/bin:
cargo install --path crates/oskac
Confirm that it works:
oskac --version
It prints oskac and the version on one line. A build from this checkout prints oskac 0.2.0. Then continue with the quickstart.
One-line installers
On Linux and macOS:
curl -fsSL https://oskalang.dev/install.sh | sh
On Windows, in PowerShell:
irm https://oskalang.dev/install.ps1 | iex
What the installers do:
- They never prompt. They are safe to run from a script, a CI job, or an AI agent.
- They install to
~/.oska/bin. SetOSKA_HOMEto install into$OSKA_HOME/bininstead. They don't change yourPATH; if~/.oska/binis not on it, they print the line to add. - They verify before they install. The installer checks the SSH signature on
SHA256SUMS(ssh-keygen -Y verify, so it needs OpenSSH 8.1 or newer) against the release key built into the installer. The signed file's first line,# oska-release v0.2.0, names the release, and it must be the one the installer asked for, so an old release cannot be served as a newer one. Then the installer checks the download's SHA-256 checksum againstSHA256SUMS, and checks that the binary reports the expected version. If any check fails, nothing is installed and the installer exits with a non-zero status. - They download over HTTPS only. They refuse a redirect to a plain
http://URL. - They take
--versionand--json.--versioninstalls that release instead of the latest.--jsonprints one line of JSON on stdout and nothing else.
To pass options through the pipe:
curl -fsSL https://oskalang.dev/install.sh | sh -s -- --version 0.2.0 --json
iex cannot pass options, so in PowerShell run the script as a script block:
& ([scriptblock]::Create((irm https://oskalang.dev/install.ps1))) --version 0.2.0 --json
A successful --json run prints:
{"ok": true, "action": "installed", "version": "0.2.0", "tag": "v0.2.0", "previous_version": null, "platform": "linux-x86_64", "path": "/home/you/.oska/bin/oskac", "bin_dir": "/home/you/.oska/bin", "on_path": false, "checksum_verified": true, "signature_verified": true}
actionisinstalled,updated(with the old version inprevious_version), orreinstalled.platformis one oflinux-x86_64,linux-aarch64,macos-x86_64,macos-aarch64, orwindows-x86_64.- On Windows,
pathends inoskac.exe.
A failed run prints {"ok": false, "error": "<code>", "message": "..."} and exits non-zero: 2 for a usage error, 1 for anything else. The codes are:
error | Meaning |
|---|---|
usage | An unknown option, or a --version that is not a version. |
no_signing_key | The installer has no release key built in. Download it again from oskalang.dev. |
insecure_url | OSKA_SITE_URL is not an https:// URL, or a download was redirected to one that is not. |
unsupported_platform | There is no build for this OS or CPU. |
unsupported_libc | Linux only: the C library is not glibc 2.35 or newer (for example musl, as on Alpine). |
missing_tool | curl, ssh-keygen, or a SHA-256 tool is missing. |
download_failed | A file could not be downloaded, for example because that version does not exist. |
bad_signature | SHA256SUMS is not signed by the release key. |
checksum_mismatch | The binary does not match SHA256SUMS. |
version_mismatch | The signed SHA256SUMS is for another release than the one asked for, or the binary reports another version. |
install_failed | The binary could not be written into place. |
The binaries are published on oskalang.dev, together with SHA256SUMS and its signature, for Linux x86_64 and aarch64, macOS x86_64 and aarch64, and Windows x86_64. The Linux binaries are built on Ubuntu 22.04 and need glibc 2.35 or newer. They do not run on an older glibc or on musl systems such as Alpine; there the installer stops with unsupported_libc, and you can build from source instead.
Verify the installation
oskac --version
If oskac is not found, add ~/.oska/bin to your PATH. The quickstart runs a first program.
Update
Re-run the installer. It installs the latest release over the old one, and with --version it installs a specific one. There is no self-update command.
Uninstall
Delete ~/.oska, which holds bin/oskac and a version file recording what the installer put there.
Upgrading to v0.2.0
v0.2.0 is a breaking change to the filesystem capabilities, with no deprecation period (ADR 0008). Fs, FsRead, and FsWrite no longer read or write anything. They only attenuate, and every read and write goes through a directory capability (DirRead, DirWrite) or a file capability (FileRead, FileWrite). A program that calls a removed method fails oskac check with E0273, whose message shows the fix. The check cannot apply it for you, because only you know the root.
Each removed method has a one-line migration. Replace <root> with the directory the program should reach:
| Before (v0.1.0) | After (v0.2.0) |
|---|---|
fs.read_to_string(path) on an FsRead | fs.restrict_to("<root>").read_to_string(path) |
fs.list_dir(path) on an FsRead | fs.restrict_to("<root>").list_dir(path) |
fs.write(path, text) on an FsWrite | fs.restrict_to("<root>").write(path, text) |
fs.create_dir(path) on an FsWrite | fs.restrict_to("<root>").create_dir(path) |
fs.restrict_to("file") naming a single file | fs.restrict_to_file("file"), then .read_to_string() or .write(text) with no path |
Reading anywhere through an FsRead | fs.restrict_to("/"), which the authority report shows |
Other things change with it:
- A function that took an
FsReador anFsWriteto read or write files now takes aDirReador aDirWrite, or aFileReador aFileWrite. mainmay not declare a directory or file capability (E0217). DeclareFs,FsRead, orFsWrite, and attenuate.restrict_toon a file no longer works. The capability it returns is broken, and every operation on it fails with a "not a directory"IoError. Userestrict_to_file.- Every symlink beneath a root is refused, including one that points inside the root.
oskac checknow lists each attenuation under its function, in the report and in the JSONattenuationsarray.- Contracts are now enforced. In v0.1.0 a
requiresorensuresclause was type-checked but never evaluated, so a contract that was silently false did nothing. In v0.2.0 every clause runs on every call, in every build, including--release. A falserequirespanics withP0006at the call, and a falseensurespanics withP0007at the clause, both with exit code 101. A program that relied on a wrong contract being ignored now panics: fix the call or the function, or correct the contract. Two placements that v0.1.0 accepted are now errors: a contract on a method ofimpl Trait for Type(E0218, move it to the trait's declaration of the method, where it binds every impl) and a contract onmain(E0219). A?orreturndirectly in a contract isE0282. A contract's==,!=and interpolation now count as calls of theEqandDisplayimpls they reach, so using them on a capability-bearing value isE0280. Capabilities can no longer hide in generic code or in closures. v0.1.0 let some programs pass a capability where only pure code was allowed, and these are now errors:
- A type argument that breaks what a generic assumes of its type parameter is the new
E0274: a capability-bearing type for a parameter that a contract uses, or that a closure used as a plainfncaptures, and a type holding aScopefor a parameter that an unscoped closure captures. That includes a trait'sSelf: a capability-bearing type cannot implement a trait whose contract usesself. Pass the capability as its own parameter, or take acap fn. - A closure is a
cap fnwhen something it captures turns out capability-bearing later in the body, as inlet g = || xsbeforexs.push(out). If it was already used as a plainfn, that isE0270at the closure. A value already used as a plainfnalso rejects acap fnassigned to it later (E0270). - A mutating method writes its receiver back, so
pushof untrusted text into aList<String>is nowE0300, andpushof acap fninto aList<fn()>isE0270. Declare the list withUntrusted<String>orcap fn. - A closure that captures a value whose type turns out to hold a
Scopeonly later in the body, as invar xs = []andlet g = || xs.len()beforexs.push(s), isE0292. Declare the value with its type first.
Where values meet, a plain
fnand acap fnnow join tocap fninstead of being a type error, so some programs that v0.1.0 rejected now check.- A type argument that breaks what a generic assumes of its type parameter is the new
An example of the migration:
fn count(fs: FsRead, path: Path) -> Result<Int, IoError> {
Ok(fs.read_to_string(path)?.lines().count())
}
fn count(fs: FsRead, path: Path) -> Result<Int, IoError> {
Ok(fs.restrict_to("/srv/app").read_to_string(path)?.lines().count())
}
Set up an AI agent
The site publishes a copy-and-paste prompt that has an AI coding agent install Oska, verify the toolchain, and configure your project. It is at the top of this page and at https://oskalang.dev/agent-setup.md as plain Markdown. The agent guide describes the loop the prompt sets up.