Category: AI Development

  • Safe AI Using Docker

    Safe AI Development Using Docker

    One of the issues with using tools like Claude CLI or Gemini CLI is that you have to closely monitor each command they run. This can sometimes lead to mindlessly pressing “ok” or letting the AI run commands that later prove problematic. These CLI AI tools have full access to everything you have access to. If you can drop a database, they can drop a database. If you can delete a file, they can delete a file.

    The problem is you either monitor every command or you run the risk of the AI damaging something.

    There are several ways to “sandbox” AI from your environment. I use Docker as it accomplishes two tasks: it helps protect resources from inadvertent AI changes, and it helps set up a way to deploy services and web apps to production.

    In this series of posts and videos, I will be reviewing how I use Docker for TypeScript and PHP development with AI tools like Claude CLI and Gemini CLI.

    Creating a Git Project to Hold Our “Dev Environment as Code”

    For this task we start where we always start as developers: with Git.

    Creating the docker-compose.yml Config File

    The next thing to do is create our docker-compose.yml. This is the file that configures Docker.

    The first line needs to be services:. This is where we will start to list the services (aka servers) we want to run.

    Below that will be the name we want to give our service. In this case we will name the first service ai-dev.

    For now we will use a Docker image for our service. The next line needs to be image: debian. Below that add command: bash -c 'sleep infinity'.

    Note that each line is indented one tab from the previous line.

    This is enough to test our first service. Make sure that file is saved, and from the command line in the same directory as the docker-compose.yml file, run:

    docker compose up -d
    

    NOTE: You may see several additional lines as Docker downloads all the resources needed to build the service, but eventually you should see the command prompt again.

    Run:

    docker ps
    

    Note the line highlighted in blue. This is our running container.

    To get a command line into the container, run:

    docker exec -it ai-dev-ai-dev-1 bash
    

    This command will open a command line inside the running container.

    If we run ls inside the container, we can see the root files of the container. We can type exit to leave the container, and run ls again to see the files in our project.

    Running docker compose down from the directory with the docker-compose.yml file will shut down the container(s).

    We now have two worlds that are separated from each other: the “inside the container” world and the “outside the container” world. We can use this to set up our safe AI development environment.

    Mapping Directories into the Container

    This container world is all well and good, but we need to get our “code” inside it.

    To do that we can map a volume from outside the container to inside the container by adding the lines volumes: followed by the path we want to map: - ./:/project.

    We can start our container again with docker compose up -d and go “inside” with docker exec -it ai-dev-ai-dev-1 bash.

    If we run ls again we will see our project directory inside our container.

    If we cd project we can then see our files from outside the container.

    Let’s run exit again and docker compose down to leave and clean up our container(s).

    We can set the default working directory to project by adding the line working_dir: /project/ to our docker-compose.yml.

    We now can map files into our container.

    Installing Claude Inside Our Container

    Now we need to start customizing our container by installing Claude CLI in it.

    To do that we will start by adding a directory called .docker to our project. After that, create a file called TypeScript.Dockerfile inside it.

    This file will be our custom setup for our ai-dev container.

    In that file add the line FROM debian AS ai-dev. This tells Docker to use the same Debian-based image that we are using in the docker-compose.yml file.

    Next we need to add curl so we can download Claude:

    RUN apt-get -y update \
        && apt-get install -y curl
    

    Then we can add Claude:

    RUN curl -fsSL https://claude.ai/install.sh | bash
    ENV PATH /root/.local/bin:$PATH
    

    Finally we need to tell our docker-compose.yml to use our new file:

    services:
      ai-dev:
    #    image: debian:trixie-backports
        build:
          context: .
          dockerfile: .docker/TypeScript.Dockerfile
        command: bash -c 'sleep infinity'
        volumes:
          - ./:/project
        working_dir: /project/
    

    Now we run docker compose build to build our new image, followed by docker compose up -d and docker exec -it ai-dev-ai-dev-1 bash. We can now run claude inside our container.

    Press Ctrl+C to exit Claude, then type exit followed by docker compose down to clean up.

    This is a super minimal Docker-protected AI development setup.

    Finally we will commit and push.

    In the next post we will make it more useful by passing our Claude login and adding tools for Claude to use.

  • UI on the Fly

    There’s an emerging concept in AI interfaces called “UI on the fly.” Basically, it’s where the AI creates a pleasing and effective user interface in the moment. You can get a taste of this yourself. Open Gemini, Claude, or any other leading-edge AI and give it a prompt like “Write me an HTML file with Tetris written in JavaScript.” Save the results to a file—say, tetris.html—then right-click on that file and open it in your browser. I bet you’ll get a working Tetris game. In a multimodal interface like Claude Desktop, it will even open it for you. This is UI on the fly.

    Prompt given to Claude: “Can you create a Tetris game for me”

    As software developers, let’s imagine where this is going. Say you’re writing an interface to an accounting package. Do you even need to write a user interface, or do you just need to write a detailed set of prompts and a good data tool for AI to access the data?

    Let’s take another example. You’re writing a pizza ordering web app for a mom-and-pop pizza store. You write a basic static landing page with the phone number, address, map links, and so on. But for the ordering, do you write a static webpage that only works on a browser, or do you write a prompt for the AI to generate the ordering interface—with details about what data to collect and how to pass the order to the restaurant? Your prompt could include things like “when on a phone, make the interface phone-friendly” and “when on a tablet, support landscape and portrait layouts.” You can include links to pictures of the pizza types.

    Now let’s say sometime in the future, the user of your app isn’t a person directly but an AI acting on the user’s behalf. You can include in your prompt: “When talking to another AI, use Markdown,” letting the user’s AI decide the interface. What if that interface is voice? What if it’s Braille? What if it’s not in English? This will be our world before we know it.

    If AI can create a good Tetris game on the fly, it will soon be creating user interfaces on the fly—whether you want it to or not.