CHECKS OF THE OPENSHELL LAB krueng.ai/openshell.html The page shows commands and what they printed. None of that text was typed in by hand or copied from memory. Every dark box on the page is a file in the folder above this one, and every green box is what that file printed when it was run. The measured numbers in the sentences come from those same outputs. This folder holds what is needed to do the whole thing again and compare. WHAT WAS RUN, AND WHERE A laptop with Windows 11 (an Intel Core Ultra 7 155H, 24 GB of memory), on mains power. Inside it WSL 2, version 2.7.3, with the Linux kernel 6.6.114.1-microsoft-standard-WSL2. For the lab we made a WSL distro of its own, named openshell-lab, from the image Ubuntu-26.04, so that the first step starts on a Linux with nothing installed, and so that one command removes every trace: wsl --unregister openshell-lab All the steps were then run in order, once, on 2026-10-09 between 21:06 and 21:11. The times are in timings.txt, one folder up. The word "power unknown" in that file means that the Python inside the lab had no way to ask Windows: the laptop was on mains, and slept.py (python/video_gen) confirms that it was awake through every step. That was the second run from nothing on the same evening. The first, half an hour earlier, was thrown away with its distro, after a change to sh.py. The two runs printed the same things. What differed: the times, the ids that OpenShell makes up (sandbox, proposal, placeholder), the sentence GitHub's /zen sends back, the megabytes of memory, and the order of some download lines. The verdicts, the refusals and the three policy hashes were the same. RUN IT ALL AGAIN On Windows, from this folder, with any Python 3: python whole_lab.py --fresh It removes the lab distro if there is one, makes a new one, creates a user in it (lab_user.sh), and runs every step of the page through the site's own tool, python/video_gen/run_page_files.py. It stops the distro twice on the way, because two steps add the user to a group and a group only counts from the next login. On our home connection the steps took under four minutes from the first to the last (timings.txt). Making the distro comes before that, and was not timed. Then, from python/video_gen in the repo: python fill_code_blocks.py openshell.html --check the boxes are the files python page_claims.py openshell.html the sentences are the outputs python slept.py openshell.html the laptop did not sleep To run only some steps in a lab that already exists: wsl -d openshell-lab -- bash /run_steps.sh review keys The steps and their order are in page_config.py. THE FILES IN THIS FOLDER README.txt this file page_config.py the steps, the numbers and the quoted words of the page whole_lab.py builds the lab and runs every step, from Windows lab_user.sh gives the new distro an ordinary user (the lab's only shortcut: this user needs no password for sudo) run_steps.sh copies the page's files into the Linux home folder of the lab, runs the steps there, copies the outputs back sh.py runs one shell file and prints it as "$ command" and output; it is how a .sh file becomes a green box sizes.py disk and memory, measured while a sandbox was running windows_drive.sh the two warnings of Part 5, as a run you can repeat facts.py the project's dates and licence, asked from GitHub *_output.txt what facts.py printed, and check_kernel.py on Linux network_mode.py runs steps 1 to 4 in the network mode WSL is in now modes/ what those steps printed in each mode, three runs THE SECOND COMPUTER check_kernel.py was also run on an NVIDIA DGX Spark: Ubuntu, kernel 6.17.0-1018-nvidia, an Arm chip, no Windows. Nothing was installed there. The file was sent over SSH and run by the Python that was already on the machine: ssh 'python3 -' < ../check_kernel.py > check_kernel_on_linux_output.txt It is the "listener: yes" that the page shows beside our laptop's "no". FOUND ON THE WAY 1. WSL in mirrored networking mode holds the seccomp listener. Our laptop's .wslconfig has networkingMode=mirrored. In that mode WSL's init installs a seccomp filter with a listener, for calls such as bind and listen, and the programs it starts afterwards inherit the filter. The kernel gives a program one listener at most, so the sandbox's own request fails with EBUSY, and the Docker runtime cannot start a sandbox (first_try). The VM runtime can, because the sandbox then has a kernel of its own. Where to read it: check_kernel_output.txt (filters: 1, listener: no), first_try_output.txt, and the function RegisterSeccompHook in github.com/microsoft/WSL, src/linux/init/main.cpp, which is called only for the port tracker of the mirrored mode. The inherited filter was seen in our lab distro; the other distros on the laptop were not looked at. TESTED the day after, 2026-10-10: the same steps in NAT mode started the sandbox, and back in mirrored mode they failed again. See THE TWO NETWORK MODES, below. 2. "openshell doctor check" says "All checks passed" on that same laptop (install_openshell_output.txt). It checks Docker and does not ask for a listener. 3. "openshell sandbox upload" from the Windows drive sends zeros. A file under /mnt/c arrives in the sandbox with the right length and only zero bytes in it, and the command still prints "Upload complete" (windows_drive_output.txt). The same file from the Linux home folder arrives whole. On /mnt/c the file reports 0 disk blocks (stat), which is our guess at the cause, and only a guess. This is why run_steps.sh copies the page's files to the Linux home folder before it runs anything. 4. The image a sandbox gets when none is named (nvcr.io/nvidia/base/ubuntu:24.04) has no curl and no python3 (windows_drive_output.txt). The example in the manual's Python SDK page runs "python" in that image, which cannot work as written. The page builds an image of its own (Dockerfile) and its SDK step runs "id". 5. After a rule is approved with "openshell rule approve", the sandbox takes up to about ten seconds to use it, and the command has no --wait. The steps sleep twelve seconds. 6. "openshell rule approve-all" approves every pending proposal, and old proposals can come back into the list after "rule clear". In a trial run it approved two when we meant one. The page's review step is ordered so that one proposal is pending when it runs, and page_claims.py checks that. 7. A command such as "openshell rule get demo | head -3" makes openshell stop with a panic about a broken pipe, because head closes the pipe early. The steps use grep, which reads to the end. THE TWO NETWORK MODES Finding 1 says that the mode is the cause. On 2026-10-10 that was put to the test with network_mode.py, which runs steps 1 to 4 of the page (check_kernel, install_docker, check_docker, install_openshell, first_try) in the lab, in whatever mode WSL is in, and keeps what they printed in a folder named after the mode. It works on a temporary copy of the page's files, so the outputs beside the page are not touched. It was run three times in one lab distro, and only the mode was changed between the runs: run WSL said step 1 step 4: the sandbox 1_mirrored mirrored filters 1, EBUSY Error 2_nat nat filters 0, listener: yes Ready (Docker runtime) 3_mirrored mirrored filters 1, EBUSY Error Each folder in modes/ holds the five outputs of its run, the times (timings.txt) and mode.txt: the mode as WSL itself reported it (wslinfo --networking-mode) and WSL's version. In all three runs the kernel was 6.6.114.1-microsoft-standard-WSL2, Docker 29.1.3 and WSL 2.7.3.0. page_claims.py checks these things from the files (page_config.py, "modes:"). The first run made the distro and installed everything; in the other two the install steps found everything in place, so their outputs are short. To do it again, on Windows, with the laptop on mains power: python network_mode.py a run in the mode you have now then, in the file .wslconfig in your Windows user folder, under [wsl2], change networkingMode=mirrored to networkingMode=nat (or back) wsl --shutdown stops every WSL distro: save your work in them first python network_mode.py the next run network_mode.py does not edit .wslconfig. Keep a copy of the file before you change it, and put the copy back when you have finished. Our laptop was in NAT mode for about three minutes, and its .wslconfig was the same afterwards as before, byte for byte (compared by SHA-256). The OpenShell in these runs was 0.1.3. The installer takes the newest release, and 0.1.3 was published on the evening of the page's own run, after it: that run, and so the page, has 0.1.2. With 0.1.3 the error of step 4 is shorter. It ends at "became ready" and does not print the two seccomp lines that the page quotes from 0.1.2. The page has no note about that: it is a dated run of 0.1.2, and its introduction tells the reader to trust their own screen where a command prints something else. Because of the new version, the rest of the lesson (steps 5 to 14) was also run once more that morning with 0.1.3, in mirrored mode, from a temporary copy, and page_claims.py was pointed at those outputs: of the 75 things the page then said about its outputs, 73 still held, and the two that did not were those two seccomp lines. Those outputs were not kept. NOT TESTED in this test: Docker Desktop, the remaining steps of the lesson in NAT mode, and the virtual-machine runtime in NAT mode. NOT TESTED AT ALL A real AI agent in the sandbox. The lesson with the Docker runtime (it started one sandbox, in the test above, and nothing was run in it). A Mac. Docker Desktop (the lab uses Docker Engine from Ubuntu's own packages, inside WSL). Audit mode, deny rules, the standalone prover, GPUs, Kubernetes. How well the sandbox resists an attack. get_files.sh The block on the page that fetches the page's files uses the address krueng.ai/openshell_assets, which answers only once the page is published. Before that, it was tested with a copy of the site on this computer: sed 's#https://krueng.ai#http://127.0.0.1:#' get_files.sh | bash On the evening the page was published, 2026-10-09, it was run again as it stands, in the lab, against krueng.ai: 24 files came, and each was the same, byte for byte, as the file in the repo. THE VIDEO The video in Part 2 of the page is built from python/video_gen/ page_explainers/openshell.py. Its pictures are drawn by code. Each line of terminal text in them is read from the output files in this folder's parent when the picture is drawn, so the video cannot show a line that the run did not print. Three places leave lines out, and say so in grey. One line, the long refusal of the POST, is shown up to the word "policy_denied" and ends in three dots. What the narration says about OpenShell, Linux and WSL was put to NotebookLM before the narration was written, with the answer limited to 14 outside sources: 30 claims, 30 supported. Each quoted sentence was also read in its source by us. Afterwards every one of the 32 spoken lines was listened to by a speech recogniser (python/video_gen/explainer_listen.py) and came back as written. The voice is a computer's. On 2026-10-10 the video was built again, and it has 33 lines now. One line was added: the test of the two network modes, with a picture that reads its terminal text from modes/2_nat. The line about what was not tried no longer names WSL's usual mode. Neither line says anything about the world outside our own runs, so the 30 claims stand as they were. All 33 lines were listened to again and came back as written. The sound was also raised to the level the site's episodes are held to. The voice as it comes measured -20.5 LUFS; the finished file measures -14.9, with a true peak of -1.4 dBTP. That is not one plain gain: the voice's peaks are too high for it, so ffmpeg's loudness filter limits them. Measured in pieces of 20 ms, it held 18 % of the speech down by more than 3 dB. A small recogniser heard the same 548 words before and after. Nobody has listened to it with their own ears yet.