Skip to content
VediramVediramSupport

Container Runtime

How Levee downloads, verifies, and updates the container runtime image, and how to run Levee on offline and air-gapped machines

Every Levee project runs inside the same Windows container image, the container runtime image. Vediram builds and publishes it. Levee downloads it, checks that it has not been altered, and loads it into its private container engine. IT can control where the image comes from, which matters for machines without internet access.

For users, the only visible part is a short wait the first time: projects can't start until the image is ready.

What's in the image?

The image is a Windows Server Core container with the tools an AI agent commonly needs to work on files and data. AI tools themselves are not in the image.

ComponentPurpose
PowerShell 7The shell the agent and the Console tab use (pwsh.exe)
GitVersion history inside the project
Python 3Scripts and data processing
Node.js (LTS)Scripts, and the runtime several AI tools need
Levee ODBC driverGoverned database access. See Database access

Every component in the image is pinned to a specific version and verified by checksum when Vediram builds the image. To see the exact versions on a machine, open a project's Console and run, for example, git --version, python --version, and node --version.

AI tools are not in the image. Claude Code, Codex CLI, and GitHub Copilot CLI are delivered as separate tool packages and mounted read-only into the container at C:\tools\<name> when a user enables them. That keeps the image stable and lets you update tools independently. See AI tools.

Some things to know about the shell inside the container:

  • The Windows PowerShell line-editing module (PSReadLine) is not included, so there is no predictive autocomplete in the Console.
  • The container has no GPU access. Agents and their tools run on the CPU.

How does Levee get the image?

By default the Levee service downloads the image by itself, verifies it, and loads it. No one needs to act unless the machine is offline.

Each Levee version requires one specific image version. The policy setting container.baseImage names it, and when it is unset Levee uses its built-in default (levee-base:0.2.0 today). The service provisions exactly that version:

  1. When the service starts, it checks whether the required image is loaded.
  2. If not, it reads the release manifest pinned to that version, <ring>/v<version>/latest.json, from https://downloads.vediram.com/releases/levee-images. The ring is stable, or preview for a preview version.
  3. It checks free disk space. It needs the image size plus the configured headroom (20 GB by default) free on the system drive.
  4. It downloads the compressed image, verifies its SHA-256 checksum against the manifest, and only then decompresses and loads it. An image that fails verification is discarded and never loaded.
  5. It retries up to 3 times on failure.

While this runs, the Levee app shows "Preparing container runtime… N%" and projects cannot start. Check progress from the command line:

levee runtime status

To start provisioning on demand and wait for it (for example in a deployment script, or after a machine was offline):

levee runtime ensure --wait --timeout 1800

levee runtime ensure works even when automatic provisioning is turned off in policy. Without --wait, it starts provisioning in the background and returns. With --wait, it checks every 2 seconds and returns when the runtime is ready. If the container engine is still starting, for example right after the service started, --wait keeps trying until the engine is up instead of failing at once. If it times out, provisioning continues in the background.

Provisioning settings

Provisioning is controlled by the runtimeProvisioning section of the policy. Most organizations keep the defaults. Change them to use an internal mirror, or to stop automatic downloads on machines you supply by hand.

The section can be set in policy.json or in the Portal policy editor under Container & Runtime.

{
  "runtimeProvisioning": {
    "enabled": true,
    "ring": "stable",
    "manifestBaseUrl": "https://downloads.vediram.com/releases/levee-images",
    "defaultFlavor": "base",
    "diskHeadroomBytes": 21474836480,
    "downloadTimeoutSeconds": 1800,
    "maxRetries": 3
  }
}

Reference

KeyDefaultMeaning
enabledtrueDownload the image automatically when it is missing. Set to false on machines where you deliver the image yourself.
ring"stable"Release ring checked for newer images: "stable" or "preview". It does not change which image is provisioned. Use preview only on test machines.
manifestBaseUrlhttps://downloads.vediram.com/releases/levee-imagesWhere to read the manifests. Point it at an internal mirror for offline networks. Must be an https:// URL.
defaultFlavor"base"Image flavor used for the update check. base is the only flavor today.
diskHeadroomBytes21474836480 (20 GB)Free space that must remain on the system drive after the download. Provisioning refuses to start below it.
downloadTimeoutSeconds1800 (30 minutes)Maximum time for one download. Also the default --timeout of levee runtime ensure --wait.
maxRetries3Download attempts before provisioning is reported as failed.

Leave container.baseImage unset unless Vediram support asks you to set it. See Container settings.

Image updates

A newer image never replaces the one in use by itself. Levee tells you an update exists, and the image changes only with a Levee release, or when you set container.baseImage because Vediram support asked you to.

  • Levee checks the ring's latest release, and when it is newer than the image the machine requires, the app shows a "Container image update available" banner and levee doctor reports it.
  • Levee never downloads or switches images on its own because of that check. A running fleet never changes silently underneath users.
  • Updated images are rolled out with Levee releases: a Levee version that requires a newer image provisions that version. Follow the release notes for each Levee update. See Updates & uninstall.

Offline and air-gapped machines

Machines that can't reach downloads.vediram.com can use an internal mirror, or an image you copy onto the machine.

Use an internal mirror

Host the release manifest and the image on an internal HTTPS web server, and point devices at it.

  1. From a connected machine, download the pinned manifest and the image for the version your Levee release requires (for example 0.2.0):

    $version = '0.2.0'
    $base = "https://downloads.vediram.com/releases/levee-images/stable/v$version"
    Invoke-WebRequest "$base/latest.json" -OutFile latest.json
    $manifest = Get-Content latest.json -Raw | ConvertFrom-Json
    $image = $manifest.images | Where-Object flavor -eq 'base'
    Invoke-WebRequest $image.url -OutFile (Split-Path $image.url -Leaf)
    
  2. Verify the download against the manifest before you publish it internally:

    $file = Split-Path $image.url -Leaf
    (Get-FileHash $file -Algorithm SHA256).Hash -eq $image.sha256   # must be True
    

    If this returns False, stop: don't publish or load the image.

  3. Publish both files on your mirror under the same path, for example https://mirror.example.com/levee-images/stable/v0.2.0/. Edit the url in your copy of latest.json to point at the image on your mirror. Leave the other fields unchanged.

  4. Set the mirror in policy:

    {
      "runtimeProvisioning": {
        "manifestBaseUrl": "https://mirror.example.com/levee-images"
      }
    }
    

    The mirror must be served over HTTPS with a certificate the machines trust. Plain http:// URLs are refused.

Devices then provision from the mirror exactly as they would from Vediram, with the same checksum verification. When you move to a Levee release that requires a newer image, mirror that version the same way.

Pre-stage an image

For machines with no network access at all, or to bake the image into a VDI golden image, place the uncompressed image in the images folder:

  1. Decompress the verified .tar.zst into a .tar file (for example with zstd -d).

  2. Create a checksum sidecar next to it containing the SHA-256 of the .tar file:

    $tar = '.\levee-base.tar'
    (Get-FileHash $tar -Algorithm SHA256).Hash.ToLowerInvariant() | Set-Content "$tar.sha256" -Encoding ascii
    
  3. From an elevated PowerShell, copy both files to %ProgramData%\Vediram\Levee\images\ and restart the Levee service. Only SYSTEM and Administrators can write to that folder.

    Copy-Item .\levee-base.tar, .\levee-base.tar.sha256 "$env:ProgramData\Vediram\Levee\images\"
    Restart-Service LeveeService
    

At startup the service loads every .tar in that folder only if its .sha256 sidecar matches. A .tar without a valid sidecar is skipped with a warning in the service log. Set runtimeProvisioning.enabled to false on these machines so they don't try to reach the internet.

Troubleshooting

  • "Container image is missing" or "Could not download the container runtime.": check access to downloads.vediram.com (or your mirror) through the system proxy, and free disk space. See Runtime won't provision.
  • "Not enough free disk space": free space on the system drive, or lower diskHeadroomBytes if you understand the trade-off.
  • "Preparing the container runtime stalled (no progress).": check free disk space, then run levee runtime ensure --wait.