Skip to main content
InteractiveBox attaches your terminal directly to a shell running inside a VM — like SSH into a sandbox. Use it for interactive tools, REPLs, debugging sessions, or any workflow where you need a live terminal.

What you’ll build

A script that:
  1. Launches an InteractiveBox with a shell
  2. Runs interactive commands in a TTY session
  3. Connects to a Python REPL inside the VM

Prerequisites

pip install boxlite
Requires Python 3.10+.

Step 1: Start a shell session

Launch an InteractiveBox and drop into a shell. The session stays open until you type exit or the shell terminates.
shell.py
import asyncio
from boxlite import InteractiveBox


async def main():
    async with InteractiveBox(image="alpine:latest") as box:
        # You're now in an interactive shell inside the VM
        # Type commands, see output in real-time
        # Type "exit" to close the session
        await box.wait()

    print("Shell session ended")


if __name__ == "__main__":
    asyncio.run(main())
python shell.py
# You'll see an Alpine Linux shell prompt
# Try: ls, whoami, cat /etc/os-release
# Type "exit" to quit
What’s happening:
  • InteractiveBox creates a VM with Alpine Linux and attaches your terminal to /bin/sh
  • Your keystrokes go directly into the VM’s shell, and output streams back in real-time
  • wait() blocks until the shell process exits (when you type exit or press Ctrl+D)

Step 2: Choose your shell

You can run any shell or REPL available in the image. Specify it with the shell parameter.
bash_session.py
import asyncio
from boxlite import InteractiveBox


async def main():
    # Use bash (available in Ubuntu, not Alpine)
    async with InteractiveBox(
        image="ubuntu:22.04",
        shell="/bin/bash"
    ) as box:
        await box.wait()


if __name__ == "__main__":
    asyncio.run(main())
python_repl.py
import asyncio
from boxlite import InteractiveBox


async def main():
    # Drop straight into a Python REPL
    async with InteractiveBox(
        image="python:slim",
        shell="/usr/bin/python3"
    ) as box:
        await box.wait()


if __name__ == "__main__":
    asyncio.run(main())
Make sure the shell you specify actually exists in the image. Alpine uses /bin/sh (busybox), not /bin/bash. Ubuntu has both.

Step 3: Choose the right TTY mode

The tty parameter controls how InteractiveBox handles terminal I/O. The right choice depends on how you’re using the box.
ValuePythonNode.jsBehaviorUse when…
Auto-detecttty=None (default)tty: undefined (default)Checks sys.stdin.isatty() / process.stdin.isTTYYou’re not sure — this works in most cases
Force TTYtty=Truetty: trueAlways enables PTY with I/O forwardingYou’re running from a terminal and want interactive control
No I/Otty=Falsetty: falseDisables I/O forwarding entirelyYou’re controlling the box programmatically with exec()
tty_modes.py
import asyncio
from boxlite import InteractiveBox


async def main():
    # Force TTY mode — always interactive
    async with InteractiveBox(
        image="alpine:latest",
        tty=True
    ) as box:
        await box.wait()


if __name__ == "__main__":
    asyncio.run(main())
programmatic.py
import asyncio
from boxlite import InteractiveBox


async def main():
    # No I/O forwarding — use exec() to send commands
    async with InteractiveBox(
        image="alpine:latest",
        tty=False
    ) as box:
        # InteractiveBox extends SimpleBox, so exec() works
        result = await box.exec("cat", "/etc/os-release")
        print(result.stdout)


if __name__ == "__main__":
    asyncio.run(main())
InteractiveBox extends SimpleBox, so you always have access to exec() for running one-off commands. The tty parameter only controls whether the shell session itself gets I/O forwarding — exec() works regardless.

What’s next?