Pranav 0fed26a14f Sandbox: a crate that runs a helper program confined (Landlock and seccomp on Linux, sandbox-exec on macOS)
## Stack

The Python cells work is split by layer. It merges in this order, each with a merge commit rather than a squash. Nobody can switch Python on before #3481.

1. #3475 depends on `main`
2. #3476 depends on `main` **(this pull request)**
3. #3477 depends on [#3475](https://github.com/storytold/gridcraft/pull/3475) and [#3476](https://github.com/storytold/gridcraft/pull/3476)
4. #3478 depends on [#3477](https://github.com/storytold/gridcraft/pull/3477)
5. #3479 depends on [#3478](https://github.com/storytold/gridcraft/pull/3478)
6. #3480 depends on [#3479](https://github.com/storytold/gridcraft/pull/3479)
7. #3481 depends on [#3480](https://github.com/storytold/gridcraft/pull/3480)

## Summary

`crates/sandbox` runs a program that can read only the paths its policy lists, write nothing except `/dev/null`, start no process, and use no network. An application re-executes itself with a launcher flag, closes every inherited descriptor above standard error, applies the confinement, checks that it holds, and only then runs the program. If any of that fails, the program does not run.

## Changes

- **`crates/sandbox`:** the policy, the Linux backend (Landlock, and seccomp that denies all sockets, with an x32 filter), the macOS backend (`/usr/bin/sandbox-exec` with a deny-default profile), the probes and a canary.
- **`apps/gridcraft`, `apps/gridcraft-cli`:** the launcher flag comes first in `main`, before any window or log.
- **`gridcraft-cli sandbox-check`:** says whether this system confines programs, and can run a confined self-test.
- **`docs/sandbox.md`:** what is refused and how.
- **`nix`:** a new dependency on Linux and macOS, for closing descriptors without `unsafe`.

## Compatibility

Windows and FreeBSD have no backend here. There the crate reports that it can't confine, and nothing that needs it runs. On Linux a launch needs Landlock ABI 3, which is Linux 6.2 and later, so an older kernel is treated the same way: this excludes, for example, Debian 12's kernel (6.1) and Ubuntu 22.04's original kernel (5.15). Nothing changes for a process started without the launcher flag.

## Review fixes

- **File metadata on Linux.** Landlock has no right for a file's permissions, owner, extended attributes, times or flags, and the seccomp filter allows what it doesn't name, so a confined program could change them on any file its user owns, outside the policy too. The filter now refuses the `chmod`, `chown`, `setxattr`, `removexattr` and timestamp families with their `*at` variants, by path and by descriptor, and any `ioctl` whose number says it hands the kernel data, as `FS_IOC_SETFLAGS` and `FS_IOC_FSSETXATTR` do on a descriptor opened only to read. A filter allows what it doesn't name, so a call a later kernel adds is allowed until it is named; the newest named is `file_setattr` (Linux 6.17).
- **Truncation on Linux.** Landlock covers truncation only from ABI 3, so on ABI 1 or 2 `os.truncate(path, 0)` emptied any file its user can write, and `open` with `O_RDONLY | O_TRUNC` emptied one it may read. A launch now requires that right. A filter can't take its place: `openat2` hides its flags from seccomp, and refusing `O_TRUNC` would refuse opening `/dev/null` to write, as `open(os.devnull, "w")` does.
- **Standard streams.** They stay open with the rights they were opened with, so a stream that is a file can be written and truncated, and one that is a socket can still send. The page and `launch_command` say so, and that a parent gives the program pipes.
- **The enforcement check, again.** It now also tries to change the canary's permissions and to truncate it, asking for what it already has, so nothing changes if one works.
- **Other processes on Linux.** On a kernel without Landlock's signal scope, a confined program could signal any process of the same user, and `kill(-1, 9)` would reach them all. The seccomp filter now refuses signals to any process but the program itself, whose ID `exec` keeps; changes to the limits, priority or scheduling of any other process; `pidfd_open`, `pidfd_send_signal`, `kcmp`, `get_robust_list`, `migrate_pages` and `move_pages`; every System V shared memory, message queue and semaphore call and every POSIX message queue call, which no path rule covers; and `syslog`.
- **The terminal on Linux.** A program started from a shell could type into its terminal. `TIOCSTI` and `TIOCLINUX` are refused, compared in 32 bits as the kernel reads them, so high bits set on the argument don't get past the rule.
- **The enforcement check.** It now also tries signal 0 on the launcher's parent, which delivers nothing, so `sandbox-check` reports a gap rather than assuming none. The launcher tries the same signal before it confines itself and refuses if that fails, and the check refuses if its parent has changed since, since a parent that refuses anyone would prove nothing. A launcher's arguments must include the current executable, which that check starts; otherwise the file rules refused the start and the check proved nothing about new processes.
- **A refused launch.** The launcher read its arguments only after closing every inherited descriptor, so a refused launch had already closed them. It reads them first.
- **A kernel without Landlock.** The refusal says so in that case too, which the tests that skip on such a machine look for. Only the first ABI's rights being refused, or `ENOSYS` or `EOPNOTSUPP` from creating the ruleset, says so. Any other failure, such as `EINVAL` or too many layers, is reported as what it is and doesn't skip the tests.
- **Folders in the exec list on macOS.** A folder got a rule that matches only the folder, so a launcher under it failed to start where the same policy works on Linux. It now covers what is in it, in both rules.
- **The sandbox page** names only what this layer has: only `gridcraft-cli sandbox-check` starts the launcher here.

## Testing

All 10 steps of `cargo xtask ci` pass on macOS (arm64), and `cargo clippy` passes for this crate for both Linux architectures. The gate on ubuntu-24.04 is the hosted one on this pull request, and it executes the Linux backend through the real binary, including `a_confined_program_leaves_other_processes_alone` and `a_confined_program_changes_no_file`, in which a confined Python tries each refused call on a file inside the policy, one outside it and a descriptor, and the test then compares contents, permissions and modification time.

A separate run on the fork, on Linux 7.0 with Landlock ABI 8, covered the same commit's code with parts of the fix taken out. Without the metadata rules, the launch check refuses with `change the canary's permissions: it worked`. Without those rules or the new checks, `a_confined_program_changes_no_file` fails with `permissions of ...: it worked`. With the Landlock rights limited to ABI 2, as on an older kernel, it fails with `truncate ...: it worked`. A kernel that is actually older than ABI 3, or that has no Landlock at all, has not been run. On macOS, terminal input was checked by hand on a pseudo-terminal: `TIOCSTI` is refused when confined and works when not.

The commits group the change by area for reading. Only the last commit of the branch is expected to build.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **New Features**
  * Added sandbox support for running commands with policy-limited file access and restrictions on network and process operations. The sandbox is available on supported Linux and macOS systems; launches are refused if confinement cannot be established or verified.
  * Added the `sandbox-check` command to check sandbox support and enforcement, or run a specified program in the sandbox. It reports when sandboxing is unsupported.
* **Documentation**
  * Added guidance on sandbox behavior, platform support, access restrictions, launch checks, and `sandbox-check` usage.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-11 07:31:42 +01:00
2026-10-26 14:13:04 -06:00

ArtCraft

GridCraft icon: a spreadsheet page on a green tile

GridCraft

Spreadsheets and data; an open-source, high-performance modern spreadsheet application, built in pure Rust.

A fast, open-source, clean-room take on modern spreadsheet workflows. It runs natively on macOS, Windows, Linux and BSD, and in the browser via WebAssembly.
By the ArtCraft team.

Written in Rust Runs on macOS, Windows, Linux, BSD and the web License: MIT OR Apache-2.0 Agent-drivable over MCP
Scanned by SonarCloud Reviewed by CodeRabbit

Join the ArtCraft community on Discord

GridCraft on getartcraft.com · ArtCraft · All Crafting Apps


GridCraft showing a quarterly sales dashboard: a styled table with banded rows, filter buttons, a totals row, data bars, arrow icons and sparklines, next to a clustered column chart and a pie chart
Quarterly Sales Dashboard: a table with a totals row, conditional formatting, sparklines and two live charts — built entirely from GridCraft commands (File → New → Sample).

Note

ArtCraft is a community of artists from all walks of life. Painters, photographers, filmmakers, illustrators, designers, animators, hobbyists, and people who picked up a pencil last week. If you make things, you're one of us. Come say hi on Discord.

A look around · Why GridCraft · Quick start · For agents · Architecture · Roadmap · The Crafting Apps · License and credits

A look around

A household budget with accounting number formats, red highlights for overspent categories, a green-yellow-red colour scale on the percent-used column, a totals row with a double border, and a horizontal bar chart comparing budget with actual

Formatting. Accounting formats, cell styles, conditional rules and colour scales, frozen headers and a bar chart.

A gradebook table with averages and letter grades computed with IFS, green highlights on A grades and data bars, with the Formulas ribbon tab open showing the function library

Formulas. 500+ functions, dynamic arrays, LET and LAMBDA, structured table references.

The sales dashboard in dark mode with the Insert ribbon tab open, showing Pivot Table, Table, Pictures, Shapes, chart, sparkline, link, comment, text box and symbol buttons

Dark mode and the Insert tab: tables, charts, sparklines, pictures, shapes, links and comments.

A pivot table summarising revenue by region and product with a Channel report filter, grand totals and currency formatting, with the Pivot Table Fields pane open on the right showing ticked fields and the Filters, Columns, Rows and Values areas

Pivot tables. Field list, filters, layouts, date grouping and show-values-as, saved to and read from XLSX.

The sales dashboard with the column chart selected: resize handles on the chart, its source data outlined in the table, the Chart Design tab open and the Format Chart Area pane on the right

Charts. Seventeen chart types, Chart Design tab and a Format pane; selecting a chart outlines its source data.

The household budget in Page Break Preview: the print area outlined in blue with a dashed page break, Page 1 and Page 2 watermarks, and the area outside the print range greyed out

Page Break Preview, print settings, and PDF export and printing.

Why GridCraft

  • Familiar. Ribbon, formula bar, Name Box, sheet tabs, keyboard shortcuts, fill handle, point-mode formula entry with coloured references, Format Cells, Paste Special, conditional formatting, tables, charts and more. Everything is right where you expect it.
  • Compatible. XLSX is the native format: styles, themes, formulas (including dynamic arrays), tables, conditional formats, validation, comments, hyperlinks, charts, pictures, sparklines, pivot tables and print settings round-trip. CSV and TSV too. Legacy 97-2003 .xls workbooks open with their values, formulas and number formats (formatting and objects not yet). XLSB worksheet data can be imported, using saved values instead of formulas. ODS worksheet data can be imported, using saved values instead of formulas. Static pictures can live inside cells, fitting their size and moving with the data.
  • Capable. A dependency-graph calculation engine with dynamic arrays and spilling, 500+ worksheet functions plus LET, LAMBDA, MAP, REDUCE, SCAN, BYROW, BYCOL and MAKEARRAY, structured table references, a comprehensive number-format language, sort and AutoFilter, Flash Fill, data validation, outlining and subtotals.
  • Fast. Copy-on-write workbooks (undo snapshots are nearly free), incremental recalculation, and a virtualised grid that only lays out what's on screen.
  • Agent-native. Every ribbon button, menu item, dialog, keystroke and mouse gesture is a command that agents can run over a JSON control channel, an MCP server, or the CLI.
  • Everywhere. One Rust codebase for desktop and the web. No subscription, no licence server, no telemetry.

Quick start

cargo run --release -p gridcraft                              # desktop app
cargo run --release -p gridcraft -- --sample sales            # open a sample (sales, budget, grades)
cargo run --release -p gridcraft -- book.xlsx                 # open a workbook
cargo run --release -p gridcraft -- --sample sales --control 7979   # + JSON control channel
cargo xtask ci                                                 # fmt, clippy, tests, assets, layering, wasm

Choose Español under View → Interface language for Spanish formula names, decimal commas, and semicolon arguments. Workbooks, clipboard exchange, and programmatic APIs retain canonical English syntax.

Web

cd apps/gridcraft-web && trunk build --release     # → dist/web (serve it with any static server)
cd apps/gridcraft-web && trunk serve --release     # http://127.0.0.1:8771  (?sample=sales opens a sample)

You need trunk and the wasm32-unknown-unknown target. Open uses the browser's file picker (dropping files works too) and Save downloads the workbook.

Ribbon access keys

Press and release left Alt on Windows/Linux/BSD, or press F10 on any platform, then type a sequence below one key at a time. Keytips appear on the supported Home controls and menu items. On macOS, use F10 (Fn+F10 if required by your keyboard); Option remains available for entering accented characters.

Sequence after Alt/F10 Action
H A L / H A C / H A R Align left / center / right
H V S or legacy E S Open Paste Special
H O I or legacy O C A AutoFit selected columns

In Paste Special, T/V/F/C select formats/values/formulas/comments, and E toggles Transpose. Enter applies the selected options; Escape cancels the dialog. These letters also work with Alt held on Windows/Linux/BSD.

Escape backs out one keytip level; Alt/F10 again exits keytips. Clicking, scrolling, or switching windows cancels navigation. Access keys do not start while editing text or using another dialog. This is initial support for these Home and legacy paths; other ribbon commands and full mouse-free navigation remain to be implemented. Browsers or desktop environments may reserve Alt/F10 for their own menus.

Logs

The desktop app writes its log to standard error and to gridcraft.log in the logs folder next to its settings: ~/.config/gridcraft/logs on Linux and BSD ($XDG_CONFIG_HOME/gridcraft/logs when set), ~/Library/Application Support/GridCraft/logs on macOS and %APPDATA%\GridCraft\logs on Windows. Each start moves the previous log to gridcraft.1.log and that one to gridcraft.2.log, so the log of a run that crashed survives the next start; attach them to a bug report. A log file stops growing at 16 MiB. By default GridCraft's own crates log at info and everything else at warn; RUST_LOG replaces that with env_logger-style directives, e.g. RUST_LOG=debug or RUST_LOG=warn,gridcraft_engine=debug (a trailing * matches a prefix: gridcraft*=debug). Runs with GRIDCRAFT_NO_PREFS set (no saved settings) log to standard error only.

For agents: MCP, CLI and the control channel

claude mcp add gridcraft -- gridcraft-cli mcp                 # headless MCP server
gridcraft-cli mcp --connect 7979                               # MCP bridged to a running window
gridcraft-cli eval '=XLOOKUP("b",{"a","b"},{1,2})'             # → 2
gridcraft-cli run --sample budget --cmd 'home.bold={"range":"B4:F4"}' --out budget.xlsx
gridcraft-cli cat budget.xlsx --range B4:F15                   # aligned text table
gridcraft-cli commands --search chart                          # every command and its params

The MCP server offers tools such as read_range, write_range, set_cell, evaluate_formula, format_range, insert_chart, create_table, sort_range, filter and execute_command (which reaches every one of GridCraft's commands). The desktop app's control channel adds real pointer and keyboard input, dialogs and screenshots. See docs/mcp.md, docs/cli.md and docs/control-protocol.md.

Architecture

GridCraft is an engine-first Cargo workspace with enforced layering (cargo xtask layers). The egui frontend is a separate crate, so the UI can be swapped without touching the engine.

Layer Crates
L0 core (addresses, values, errors, dates, input parsing) · numfmt (number format codes)
L1 formula (lexer, parser, printer, reference adjustment) · functions (worksheet function library)
L2 model (workbook, sheets, copy-on-write cells, styles, tables, charts…) · calc (dependency graph, evaluator, dynamic arrays) · xlsx (XLSX, CSV, plus XLSB and ODS data import)
L3 chart (toolkit-free chart layout and rendering)
L5 engine (session, commands, history, clipboard, fill, sort, filter, file I/O)
L6 ui-egui (spreadsheet UI, control channel) · mcp (MCP server)
L7 apps/gridcraft, apps/gridcraft-cli, apps/gridcraft-web
  • Contributor and agent rules (clean-room, asset policy, never-crash, quality gates): AGENTS.md
  • Bundled assets: each one is listed with its licence in ATTRIBUTION.md
  • Releases: signed macOS, Windows, Linux, BSD and web builds; see docs/releasing.md

Roadmap

GridCraft is in alpha: the core workflow works end to end, but depth, fidelity and file compatibility are still rough. It covers 89% of standard ribbon commands in our catalog (docs/parity-checklist.md) and 513 standard worksheet functions (docs/function-parity.md); this is a parity inventory, not a count of working implementations. Weighted by what spreadsheet users rely on, we estimate it is about 50% ready for real work. Next up: XLSX fidelity on real-world files, very large workbooks and multi-threaded recalc, chart depth, text in every script, and .xls import. Stage, numbers and estimates are in ROADMAP.md; the ranked work list is docs/gaps.md.

Downloads

Download GridCraft from GitHub: the latest release has every build listed below, and all releases has earlier versions and their notes. <ver> in the file names is the version number, and SHA256SUMS.txt lists a checksum for every file.

Windows

Build Installer Portable
x64 (64-bit Intel/AMD) gridcraft-<ver>-windows-x64.msi gridcraft-<ver>-windows-x64-portable.zip
arm64 (Snapdragon and other ARM PCs) gridcraft-<ver>-windows-arm64.msi gridcraft-<ver>-windows-arm64-portable.zip
x86 (32-bit) gridcraft-<ver>-windows-x86.msi gridcraft-<ver>-windows-x86-portable.zip

Installers and executables are code-signed. The MSI installer offers an optional desktop shortcut (unchecked on a new installation).

If the app doesn't open on Windows: the desktop app initializes only DirectX 12 by default. Letting wgpu also create an OpenGL instance can crash some graphics drivers (AMD's atio6axx.dll) before the window appears, so the app would flash in Task Manager and quit. WGPU_BACKEND overrides the default for troubleshooting (for example dx12 or vulkan). In PowerShell, from the folder containing the executable:

$env:WGPU_BACKEND = "vulkan"
& .\gridcraft.exe
Remove-Item Env:WGPU_BACKEND                     # restore the default for later launches

An explicit gl override can bring the driver crash back on affected systems, and vulkan can bring back a crash in some Intel UHD Graphics drivers (igvk64.dll, #120). The macOS, Linux and web backend defaults are unchanged.

If the app is slow on an older Windows PC, or DirectX 12 can't start: when DirectX 12 finds no graphics card with a driver for it, it only offers the Windows-provided software renderer, which draws every frame on the processor. In that case GridCraft uses OpenGL instead, without setting anything; if the first renderer fails to start, it tries the other one once. The log file records the choice (lines starting with graphics:). WGPU_BACKEND still overrides it (and then nothing else is tried), e.g. $env:WGPU_BACKEND = "gl" or "dx12".

DirectX 12 shaders are compiled with FXC, which is part of Windows, so no dxcompiler.dll or dxil.dll is needed, and a copy another program put on PATH can no longer stop the app at start ("Parent device is lost", #339). $env:WGPU_DX12_COMPILER = "dxc" (or "auto") selects wgpu's DXC handling again for troubleshooting.

macOS

Build File Notes
App, universal (Apple silicon + Intel) gridcraft-<ver>-macos-universal.dmg Signed and notarized
Command-line tool, universal gridcraft-cli-<ver>-macos-universal.zip Signed and notarized

Linux

Format x86_64 aarch64 (ARM64) Notes
AppImage gridcraft-<ver>-linux-x86_64.AppImage gridcraft-<ver>-linux-aarch64.AppImage Runs anywhere; updates itself with AppImageUpdate (.zsync files)
Flatpak gridcraft-<ver>-linux-x86_64.flatpak gridcraft-<ver>-linux-aarch64.flatpak Sandboxed; flatpak install --user <file>
Debian/Ubuntu gridcraft-<ver>-linux-x86_64.deb gridcraft-<ver>-linux-aarch64.deb
Fedora/RHEL/openSUSE gridcraft-<ver>-linux-x86_64.rpm gridcraft-<ver>-linux-aarch64.rpm
Tarball gridcraft-<ver>-linux-x86_64.tar.gz gridcraft-<ver>-linux-aarch64.tar.gz Unpack anywhere

FreeBSD

Build File
x86_64 gridcraft-<ver>-freebsd-x86_64.tar.gz

Web (WebAssembly)

Build File Notes
Static site gridcraft-web-<ver>.zip Runs in a modern browser; host it on any static server

The Crafting Apps

GridCraft is one of the Crafting Apps: free, open-source creative tools from the ArtCraft team, each written from scratch in Rust and each able to stand on its own.

App What it's for Code Learn more
PhotoCraft Image editing: layers, masks, type and real PSD files GitHub Website
VectorCraft Vector illustration GitHub Website
FilmCraft Video editing, color and sound GitHub Website
LightCraft Photo library and raw development GitHub Website
PdfCraft Reading, organizing and protecting PDFs GitHub Website
EffectCraft Motion graphics and visual effects GitHub Website
DesignCraft Page layout and publishing GitHub Website
FigCraft Interface design and prototyping GitHub Website
WordCraft Word processing and documents GitHub Website
SoundCraft Audio recording and editing GitHub Website
DeckCraft Presentations and slide decks GitHub Website
CADCraft Computer-aided design GitHub Website
GridCraft Spreadsheets and data GitHub Website
TextureCraft 3D texture painting GitHub Website
Craft Anything See how any software works, then build your own GitHub Website
SolidCraft Parametric 3D CAD by Tangency Labs (community app) GitHub

And ArtCraft itself, our AI image and video studio for artists who want real control.


Join the ArtCraft community on Discord

Come make things with us

Our Discord is where artists of every kind hang out: people who paint, shoot, draw, cut film, set type, and people still figuring out what they like to make. Share what you're working on, ask for help, tell us what's broken, or tell us what you wish these tools could do. Whatever your medium and however long you've been at it, you're welcome here.

discord.gg/artcraft · getartcraft.com · The Crafting Apps · GridCraft

License and credits

GridCraft is dual-licensed under MIT or Apache-2.0, at your option. Copyright (c) 2026 ArtCraft Team and the GridCraft contributors. Required notices are in NOTICE.

Bundled fonts, icons, images and other assets keep their own open licenses; each one is listed with its author, source and license in ATTRIBUTION.md.

All UI icons are drawn in code and are original; the sample workbooks and every screenshot in this README were made with GridCraft itself. GridCraft bundles no fonts: it uses the fonts installed on your system.

The ArtCraft name, wordmark and logos in docs/brand/ are trademarks of the ArtCraft Team and are not covered by this license. They may be used only unmodified, and only as part of this repository and GridCraft, under docs/brand/LICENSE-brand.txt. Forks and modified versions must remove them.


ArtCraft
Made by the ArtCraft team and community.

S
Description
No description provided
Readme
108 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 95.3%
RenderScript 3.8%
PowerShell 0.5%
Shell 0.3%