<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: rahulb0802</title>
    <description>The latest articles on DEV Community by rahulb0802 (@rahulb0802).</description>
    <link>https://dev.to/rahulb0802</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4020021%2F8edabac5-8df7-4d55-ab56-8fb4406c7479.png</url>
      <title>DEV Community: rahulb0802</title>
      <link>https://dev.to/rahulb0802</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rahulb0802"/>
    <language>en</language>
    <item>
      <title>Giving LLMs access to a bash terminal is terrifying</title>
      <dc:creator>rahulb0802</dc:creator>
      <pubDate>Tue, 07 Jul 2026 20:16:22 +0000</pubDate>
      <link>https://dev.to/rahulb0802/giving-llms-access-to-a-bash-terminal-is-terrifying-1h08</link>
      <guid>https://dev.to/rahulb0802/giving-llms-access-to-a-bash-terminal-is-terrifying-1h08</guid>
      <description>&lt;p&gt;Recently, I was testing an autonomous AI agent in a local directory. I gave it a multi-step coding task and a terminal tool so it could run its own tests. &lt;/p&gt;

&lt;p&gt;Everything looked fine for the first three steps. I thought to myself, &lt;em&gt;this is way easier than I expected&lt;/em&gt;. But I was wrong. The agent hit an error, hallucinated a strange fix, and then tried to fix that but executing a command that nuked my workspace filesystem. Everything was corrupted, without warning.&lt;/p&gt;

&lt;p&gt;If you are building with LLMs right now, you already know that giving an agent raw shell access feels like giving the summer intern prod credentials on their first day. But the real problem isn't just that agents break things. It's that when they do, we don't have a standard way to hit undo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Missing Primitive
&lt;/h2&gt;

&lt;p&gt;Right now, the standard answer to this problem would be: "Why don't you just use a sandbox?"&lt;/p&gt;

&lt;p&gt;But spinning up a temporary Docker container or microVM only solves one half of the problem: blast radius. It stops the agent from destroying your machine, but it does absolutely nothing to protect the internal state of the task itself.&lt;/p&gt;

&lt;p&gt;Let's take a 10-step agent workflow, for example. If the agent does a flawless job on steps 1 through 5, but completely breaks the workspace on step 6, the entire run is ruined. Because the sandbox doesn't have instant state recovery built into the process, your only real option is to start the entire loop from scratch.&lt;/p&gt;

&lt;p&gt;In standard software development, we have &lt;code&gt;git checkout&lt;/code&gt; or VM snapshots to save our state. But when it comes to autonomous agents running terminal commands, we are completely missing a basic safety primitive: instant checkpointing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lightbulb Moment
&lt;/h2&gt;

&lt;p&gt;I realized we don't need heavier sandboxes; we just need a way to travel back in time. &lt;/p&gt;

&lt;p&gt;If you look at how agents run right now, it's a fragile linear setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Step 1: OK] -&amp;gt; [Step 2: OK] -&amp;gt; [Step 3: rm -rf /] -&amp;gt; 💥 TOTAL CRASH (Start Over)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, I feel the workflow should look more like a Git tree. Before the agent executes &lt;em&gt;any&lt;/em&gt; tool call or bash command, the system should automatically drop a silent checkpoint. If the command succeeds, the checkpoint merges. If the agent hallucinates a mistake, you instantly roll the entire filesystem back to the exact second before the command ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Step 1: OK] 
   ↓
[Step 2: OK] -&amp;gt; (Checkpoint Created)
   ↓
[Step 3: rm -rf /] -&amp;gt; 💥 FAILED -&amp;gt; [🔄 ROLLBACK TO STEP 2] -&amp;gt; [Step 3 (Retry): OK]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But to make this practical, it can't be slow, because no one wants to wait 5 seconds to take a VM snapshot between every single tool call. It had to be a lightweight, near-instant filesystem trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;To make checkpointing near-instantaneous, I avoided heavy VM snapshots and went straight to the Linux kernel level using OverlayFS. &lt;/p&gt;

&lt;p&gt;OverlayFS lets you combine two directories—a read-only base layer and a blank writeable layer—into a single merged view. When the AI agent runs a command, it thinks it is editing your real project files, but the OS is actually writing those changes to an isolated scratch space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.rewind/
├── base/       &amp;lt;-- Your original, untouched workspace files (Read-Only)
├── scratch/    &amp;lt;-- Where the agent's changes actually go (Write-Only)
└── merged/     &amp;lt;-- What the agent actually sees and interacts with
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because of this copy-on-write mechanism, creating a checkpoint is a sub-20 ms operation.&lt;/p&gt;

&lt;p&gt;If the agent runs a successful command, you just commit the changes by syncing the &lt;code&gt;scratch&lt;/code&gt; layer into your main workspace. But if the agent does something destructive, you just unmount the directory and spin up a blank one.&lt;/p&gt;

&lt;p&gt;Here is the logic handling the cycle written out in TypeScript for brevity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// abstraction of runtime loop&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runAgentStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Checkpoint filesystem&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;filesystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCheckpoint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Let the agent execute the terminal tool&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// if success, keep changes&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;checkpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// wipe scratch layer otherwise&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Agent hallucinated. Rolling back filesystem...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;checkpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Packaging it into &lt;code&gt;rewind-sdk&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I got tired of manually piecing together custom shell scripts and OverlayFS mount commands every time I wanted to test a new agent script. So, I packaged all of this underlying Linux filesystem logic into a lean, open-source infra layer called &lt;code&gt;rewind-sdk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The goal here was to make checkpoint and rollbacks completely framework-agnostic. It currently only supports LangGraph, raw scripts, and standard tool calls, but ideally the system built is designed to be extended to other agent frameworks. The killer feature, however, is atomic state management: when you set a checkpoint or rollback, the SDK syncs the filesystem state &lt;em&gt;and&lt;/em&gt; your conversation history together in a single call.&lt;/p&gt;

&lt;p&gt;Here is how simple it is to initialize a protected runtime session and use it in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rewind_sdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Verifier&lt;/span&gt;

&lt;span class="c1"&gt;# initialize the protected workspace environment
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_sandbox&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workspace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./my_codebase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;# set known-good checkpoint before risky actions
&lt;/span&gt;    &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;known_good&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# configure automated rollback rules/triggers
&lt;/span&gt;    &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auto_rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exception&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_failure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;known_good&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Verifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Simulating an agent step: modifying files inside the sandbox
&lt;/span&gt;    &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;change_from_llm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Run tests. If the verifier fails, auto_rollback fires instantly.
&lt;/span&gt;        &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_tests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent broke the build. Filesystem and memory rolled back!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just the tip of the iceberg. With the &lt;code&gt;@session.tool&lt;/code&gt; decorator, you can turn your tool functions into rollback-compatible actions. And with further verification features, you can hook your external, dedicated testing suite into &lt;code&gt;rewind-sdk&lt;/code&gt; for more control over failure management.&lt;/p&gt;

&lt;p&gt;This way, you get full, deterministic control over your workspace state. If the agent hallucinates a destructive command, the container discards the broken layer, wipes the dangling messages from the history so LLM providers don't reject the schema, and lets the agent try again from a clean slate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to from here?
&lt;/h2&gt;

&lt;p&gt;Building autonomous agents that actually &lt;em&gt;do things&lt;/em&gt; is super exciting. However, we need better runtime guardrails if we want to move past simple chat interfaces.&lt;/p&gt;

&lt;p&gt;I'm currently treating this as an early prototype and building it entirely in public. If you want to check out the full source code, try running the demo script, or even integrate it into your own agent workflows, everything is completely open-source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rahulb0802/rewind-sdk/tree/master" rel="noopener noreferrer"&gt;Github Repo Link&lt;/a&gt;&lt;br&gt;
&lt;a href="https://pypi.org/project/rewind-sdk/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt; &lt;br&gt;
To get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;rewind-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: You'll need Docker running locally for the containerized isolation)&lt;/p&gt;

&lt;p&gt;How are you currently handling agent safety and runtime errors in your own workflows? Let me know in the comments; I'd love to hear how others are tackling this problem.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
