{
"$type": "site.standard.document",
"canonicalUrl": "https://www.tiles.run/blog/move-along-python",
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreietcta2kvgjmiasufa3h2rfgrn3753ixf2q7rcrebxuwvdvietwqm"
},
"mimeType": "image/png",
"size": 31265
},
"description": "Deterministic, portable Python runtimes for Tiles using layered venvstacks.",
"path": "/move-along-python",
"publishedAt": "2026-02-17T00:00:00.000Z",
"site": "at://did:plc:mqmcsjuerbjhu65mpmvkcuw2/site.standard.publication/3mnmiw6ypye2s",
"tags": [
"Tiles",
"Python",
"packaging",
"venvstacks"
],
"textContent": "We have been working on Tiles. Tiles is a local-first private AI assistant that runs on-device models with encrypted P2P sync, keeps your data and identity yours, and supports sharing chats with ATProto. To ensure its Python model server starts predictably on any machine, the runtime and dependencies must be deterministic and portable. This post walks through how we achieve that with layered venvstacks.\n\nThe Python Problem\n\nRight now, we have a polyglot architecture where the control plane and CLI are written in Rust, while local model inference runs through a Python server as a daemon. Ideally, when we ship Tiles, we should also ship the required artifacts needed to run Python on the user's system.\n\nSince Python servers cannot be compiled into a single standalone binary, the user's system must have a Python runtime available. More importantly, it must be a deterministic Python runtime so that the server runs exactly on the version developers expect.\n\nIn earlier releases of Tiles (before 0.4.0), we packed the server files into the final release tarball. During installation, we extracted them to the user's system, downloaded uv (a Python package manager), installed Python 3.13 if it was not already present, and then ran the server as a daemon.\n\nThis approach had several issues:\n\n- Downloading development-related tools such as uv onto the user's system\n- Relying on uv at install time to manage dependencies and run the server\n- Increased chances of failures due to dependency or runtime non-determinism\n- Requiring internet access to download all of the above tools\n- Lack of a fully deterministic runtime across operating systems\n\nOne of the long-term goals of Tiles is complete portability. The previous approach did not align with that vision.\n\nPortable Runtimes\n\nTo address these issues, we decided to ship the runtime along with the release tarball. We are now using venvstacks by LM Studio to achieve this.\n\nVenvstacks allows us to build a layered Python environment with three layers:\n\n- Runtimes: Defines the exact Python runtime version we need.\n- Frameworks: Specifies shared Python frameworks such as NumPy, MLX, and others.\n- Applications: Defines the actual server application and its specific dependencies.\n\nSimilar to Docker, each layer depends on the layer beneath it. A change in any layer requires rebuilding that layer and the ones above it.\n\nAll components within a layer share the layers beneath them. For example, every framework uses the same Python runtime defined in the runtimes layer. Likewise, if we have multiple servers in the applications layer and both depend on MLX, they will share the exact deterministic MLX dependency defined in frameworks, as well as the same Python runtime defined in runtimes.\n\nHow venvstacks is used in Tiles\n\nWe define everything inside a venvstacks.toml file. Here is the venvstacks.toml used in Tiles.\n\nBecause we pin dependency versions in the TOML file, we eliminate non-determinism.\n\nInternally, venvstacks uses uv to manage dependencies. Once the TOML file is defined, we run:\n\nThese commands resolve dependencies, create the necessary folders, lock files, and metadata for each layer, build the Python runtime and environments based on the lock files, and produce reproducible tarballs that can be unpacked on external systems and run directly.\n\nWe bundle the venvstack runtime artifacts into the final installer using this bundler script. During installation, this installer script extracts the venvstack tarballs into a deterministic directory.\n\nOur Rust CLI can then predictably start the Python server using:\n\nWhat's Next\n\nWe tested version 0.4.0 on clean macOS virtual machines to verify portability, and the approach worked well.\n\nFor now, we are focusing only on macOS. When we expand support to other operating systems, we will revisit this setup and adapt it as needed.\n\nPackaging the runtime and dependencies increases the size of the final installer. We are exploring ways to reduce that footprint.\n\nWe also observed that changes in lock files can produce redundant application tarballs when running the publish command. More details are tracked in this issue.\n\nOverall, we are satisfied with this approach for now.",
"title": "Move Along, Python"
}