Skip to content

IronShell v14.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 06 Nov 21:59
· 3 commits to master since this release

AetherShell is now IronShell. The binary is ish (ae remains as an
alias), the crate iron-shell, the Python SDK ironshell, the editor extension
admercs.ironshell, and the repository nervosys/IronShell (old links redirect).
Scripts use .ish and variables IRONSHELL_*; the old .ae and AETHER_*
names are not read. A security audit on 2026-11-04 reported 13 findings. This
release fixes them, apart from the residual risks the write-up lists; see
SECURITY_AUDIT_2026-11-04.md.

14.0.0 is a major release because calls that used to be quietly accepted now
fail. Every dispatched builtin carries a declared signature, and the dispatcher
holds calls to it: surplus or mistyped arguments, and pipeline input a builtin
does not take, are refused with E_BAD_ARG before anything runs. Builtins that
used to report success without doing the work now say so with a coded error.

Breaking changes

Calls are checked against their declarations.

  • Surplus arguments, wrong types and unwanted pipeline input are E_BAD_ARG.
    Before, journal_clear("a") ignored its argument and cleared the whole
    journal.
  • Optional arguments no longer fall back to a default when they are malformed.
    In fs_chmod, the string "0" used to mean 0644; it now means mode 0, and a
    malformed mode is refused. A malformed fs_chown group is refused instead of
    being read as the owner.
  • A pipeline stage's input goes only to that stage. A failing stage stops the
    pipeline with an error that names its index and builtin.

Fake successes are now errors. These used to answer as if they had
worked:

  • sso_auth minted a token for any input. It is now E_UNIMPLEMENTED and
    mints nothing, so sso_validate answers valid: false.
  • finetune_start, job_submit, the cloud_* instance builtins,
    remote_exec, repl_serve/repl_connect/repl_broadcast,
    workspace_sync, plugin_load, marketplace_install and
    marketplace_update reported work that nothing did.
  • compliance_check and compliance_report called every standard compliant,
    and audit_verify called an empty log valid.
  • agent_with_mcp reported a run that hit its step limit as completed; it is
    now E_BUDGET_EXCEEDED.
  • web_fetch, web_get, web_scrape, web_download and web_upload fail on
    HTTP 4xx and 5xx. web_download of a 404 used to answer true.

Agent-mode policy. Each of these refuses with E_POLICY_DENY or asks for
approval:

  • Network: egress needs approval unless IRONSHELL_NET_ALLOW names the host
    (* restores the old behaviour). ai_backends charges, and in agent mode
    asks approval for, each endpoint it probes.
  • Clipboard: clipboard_get, clipboard_get_image and
    clipboard_history read nothing unless IRONSHELL_SECRETS=allow or
    IRONSHELL_POLICY=permissive.
  • Undo and redo: undo, rewind, session_undo and session_redo leave
    a recorded file outside the workspace jail alone. The step stays in the
    journal for a human-mode rewind.
  • Mode variables: set_env, env_set and env_unset cannot change
    IRONSHELL_MODE, IRONSHELL_APPROVE_ALL or IRONSHELL_POLICY from agent mode.
  • Commands that imply agent mode: ish mcp stdio, ish agent execute and
    ish agent interactive now do, as agent serve and mcp serve already did.
  • Reclassified effects: many builtins changed effect class, which decides
    when agent mode asks.
    • rsync_sync is Destructive.
    • helm_install, helm_upgrade, uv_pip and test_watch are Exec.
    • Undo, redo, journal_clear and session_end are WriteLocal.
    • The full list is in CHANGELOG.md.

Renamed or reshaped results.

Builtin 12.x 14.0.0
proc_kill true / false {pid, signal, sent: true}, or a coded error
net_latency Int Float (average round trip)
nanda_quorum total_agents electorate
monitor_sockets (Windows) foreign_address peer_address, as on every OS
pkg_list status, arch fields; rc packages {name, version} for installed packages
proc_list memory (Unix) %MEM bytes, as on Windows
from_json, JSON readers JSON null as the string "null" null
grep substring POSIX basic regular expression
npm/pnpm/yarn failure record with exit_code E_TOOL_FAILED with the stderr tail
docker_logs / k8s logs all output last 1,000 / 200 lines by default
crypto_cert_info openssl's -text output as a String an Array with a Record per certificate
crypto_cert_verify, no trust store false E_NOT_FOUND
crypto_generate_key private key PKCS#1 or SEC1 PEM from openssl PKCS#8 PRIVATE KEY PEM; RSA 2048/3072/4096 only; refused under IRONSHELL_FIPS

Argument shapes.

12.x 14.0.0
docker_run("alpine", "echo hi") docker_run("alpine", ["echo", "hi"]); likewise docker_exec, the podman twins and k8s_exec
tmux_new(name, cmd) tmux_new(name), then tmux_send
age_keygen() returned the key an output file is required, and no key is returned
git_checkout("src/app.rs") restored the file the name is read as a revision only
zip_create, scp_download, age, gpg, xz, zstd overwrote outputs an existing output is E_BAD_STATE

Error codes.

  • E_DECRYPT_UNAUTHENTICATED is folded into E_DECRYPT_FAILED (exit 65).
  • E_CRYPTO_FAILED (exit 70) and E_FIPS_DISALLOWED (exit 77) are new.
  • E_OS_DENIED (exit 77) is new: the operating system refused (file
    permissions, another user's or a protected process, a privilege not held).
    These were E_POLICY_DENY, which now means only the shell's own policy.
    Migration: catch e { if e.error.code == "E_POLICY_DENY" ... } written for
    an unreadable file or a foreign process tests "E_OS_DENIED" instead.
  • apply refuses a plan of more than 10,000 ops (E_BUDGET_EXCEEDED).
  • A tool stopped at its deadline is E_BUDGET_EXCEEDED.
  • An uncaught throw, or a failing assert used as a statement, ends the
    program with exit 1.

Removed.

  • The ask alias of ai was advertised but never dispatched. Call ai.
  • The fallbacks that quietly used another tool are gone: rg to grep, fd to
    find, and yq to a JSON-only evaluator. A missing tool is E_TOOL_MISSING.

Library and SDK.

  • The Python SDK's Agent.run and Swarm.run build the same call as the
    agent and swarm builtins, and raise ValueError for options those cannot
    honour.
  • In the Rust API, DQNAgent.step and MultiAgentRL.step return Result.

Migration

  • A script that passed extra arguments, or piped into a builtin that ignored the
    pipe, now gets E_BAD_ARG. The message names the parameter and prints the
    signature, and ish agent schema lists every declaration.
  • Agents that use the network: set IRONSHELL_NET_ALLOW to the hosts they
    need.
  • Agents that read the clipboard: set IRONSHELL_SECRETS=allow, or do it in
    human mode.
  • Modifying a SQLite database: use db_sqlite_exec. sql, sqlite_query
    and db_sqlite_query are read-only.
  • Legacy password hashes: Windows SHA-256 hex hashes and Unix SHA-crypt
    ($5$, $6$) hashes verify, but only outside IRONSHELL_FIPS, and should be
    rehashed after a successful verify. New hashes are Argon2id (PBKDF2 under
    IRONSHELL_FIPS).
  • Python SDK callers: catch ValueError from Agent.run and Swarm.run.

New in 14.0.0

  • ish -b runs the ordinary POSIX subset in-process: 32 of 32 byte-identical to
    bash on E6, and 32 of 32 on a held-out corpus. Anything else runs through the
    effect gate, never bash -c.
  • Data access:
    • SQL over typed values and files: sql, sql_value and a bundled SQLite.
    • open() parses JSON, JSONL, TOML, YAML and CSV, and .field row syntax
      works in pipeline stages.
  • Agent tooling: ish --explain and explain_effects(code) report a call's
    effect class and agent decision without running it.
  • Native builtins:
    • Cryptography on IronCrypto (ironcrypto 0.3.2): signing and
      verification with Ed25519, deterministic ECDSA P-256 and P-384, and RSA
      PKCS#1 v1.5 with SHA-256 (2048 to 4096-bit keys), interoperating with
      openssl dgst; key generation (RSA 2048/3072/4096, P-256, Ed25519) as
      PKCS#8 PEM; AES-256-GCM envelopes, Argon2id and PBKDF2 password hashes
      (compatible with those 12.x wrote); SHA-2, HMAC-SHA-2 and the random bytes
      for keys, nonces and salts; and TLS, through ic-rustls. No openssl
      process outside the openssl_* wrappers, and ring is not built.
    • Certificates on IronSocketLayer's X.509 (ironsocketlayer 0.7.1):
      crypto_cert_info returns a Record per certificate, and
      crypto_cert_verify checks the chain against the system trust store.
    • Argon2id password hashing (PBKDF2 under IRONSHELL_FIPS).
    • TCP sockets with handles.
    • Filesystem watch.
    • A one-exchange web_websocket.
  • Real scheduling: cron_* and at_* have working backends.
  • Linux system administration: accounts, groups, ACLs, mounts, services,
    sudo_exec and apt. Verified as root in a disposable Debian 13.
  • Package listing: pkg_list covers apt, pacman, rpm (dnf/yum), Homebrew
    and Windows Add/Remove Programs.
  • Windows: proc_env, proc_cwd, proc_suspend and proc_resume; file ACLs through
    icacls; window listing and operations; fs_mount/fs_unmount of VHD and
    VHDX files at an NTFS folder; sudo_exec in an already-elevated process
    (argv only, no shell; batch files refused); lsof(path), the processes
    holding a file open, from the Restart Manager.
  • macOS: services (launchd), accounts and groups (dscl), file ACLs
    (chmod +a), disk-image mounts (hdiutil), sudo_exec, proc_env, the libc++
    version, clipboard images,
    screenshots, and window listing and operations (System Events); read-only
    blkid (diskutil), journalctl (log show), monitor_ethtool
    (ifconfig) and pkg_history (/var/log/install.log); pointer, key and
    text input, the active window and the screen size (Quartz events, which
    need Accessibility).
  • Linux desktop: clipboard types and images (X11 and Wayland), and
    gui_screenshot_window (X11).
  • Rust refactoring and navigation: rename, extract function or variable,
    inline, move to file, change signature, definitions, references and callers.
  • Evaluator speed: in four paired Windows rounds against 570bff8, module
    calls ran 3.65x faster and two-argument reduce 2.27x. Workloads with no gain
    are listed in benches/agentic/results/perf-2026-09-29-followup.md. A Linux
    rerun of the final code reproduced both, at 4.56x and 1.91x
    (benches/agentic/results/perf-2026-10-27.md).

Supported platforms

  • Linux and Windows: the full workspace suite passes on both, each test
    binary under a memory cap: 264 binaries at b547789.
    • Linux system-administration and desktop backends are verified only in a
      disposable Debian distro.
    • The Windows service, account, group, scheduled-task, winget, ACL, mount,
      window and sudo_exec backends are verified with real changes only on a
      disposable GitHub runner.
  • macOS: the workspace suite (cargo test --workspace --features native)
    passes on GitHub's macos-26-arm64 image, and the x86_64 and aarch64 Apple
    builds succeed. Its service, account, ACL, mount, clipboard-image,
    screenshot, window, libc++ and process-environment backends are verified
    with real changes on a disposable GitHub runner, and sudo_exec runs
    sudo -n there as on Linux. Its crontab backend is verified there too.
  • WASM: the wasm32 library builds without default features. Its behaviour in
    a browser is not exercised.
  • Rust: 1.88 is the MSRV, checked with and without default features. CI
    runs Clippy and the local_inference library tests with the candle and
    onnx features; neither loads a real model there.

Known limitations

  • web_xpath implements a subset of XPath 1.0 over a lenient in-tree HTML
    parser (listed under "No new crates for 13.0.0" below). It is not a full
    XPath engine and does not validate XML. count(), following-sibling:: and
    other axes, variables, parenthesised paths such as (//li)[1], and
    comparisons between two node sets are refused with E_BAD_ARG. Use
    web_scrape for a plain tag selector.
  • The hyphenated PowerShell-style names (sort-object, where-object,
    get-content, get-files) cannot be written in the language, so they have
    no declarations.
  • Windows: clipboard images and window capture are E_UNIMPLEMENTED, and
    so are the Linux system-administration builtins.
  • Homebrew: pkg_list has run only against parser fixtures.
  • crypto_cert_verify: reads its trust store from SSL_CERT_FILE alone
    when it is set, and otherwise from the usual Unix bundle or Git for Windows'
    bundle, not the Windows certificate store or the macOS keychain; an unusable
    SSL_CERT_FILE, or no bundle, is E_NOT_FOUND. It checks
    for TLS server use and checks no host name. RSA keys over 4096 bits are
    E_UNIMPLEMENTED.
  • RSA: keys are 2048, 3072 or 4096 bits for signing, verifying and
    generation; crypto_generate_key("rsa", 8192) is E_BAD_ARG.
  • Key generation: ssh_keygen, openssl_genrsa and age_keygen write
    private keys unencrypted.
  • Processes: a deadline kills a tool's direct child only, so processes
    that child started can outlive it.
  • Output shown unredacted: env_vars and proc_env redact by variable
    name only, so a password inside DATABASE_URL is shown.
  • Not measured with a live model: the E7 agentic benchmark. It was not run
    before release because the current cli/cli corpus ties on one question.
  • Carried to the next release: the open items in docs/COMPLETION_PLAN.md,
    mostly unimplemented platform branches and end-to-end verification of
    operations against real effects. The release went ahead with them open.
  • Residual security risks are listed under "After the fixes" in the audit
    write-up: packages are unsigned, a link swapped between the jail's check and a
    write can still redirect it, and approved calls are trusted.
  • Not checked on real hardware: battery, GPU, sensor and CPU-frequency
    readings.
  • Building iron-shell-lsp on Windows: rustc overflowed its stack once
    compiling iron-shell in that crate's debug profile (0xc0000409). Setting
    RUST_MIN_STACK=134217728 let it build.

Full changelog: CHANGELOG.md