# llms-full.txt for Tiles Privacy Expanded full-text context for tiles.run, intended for larger context windows. Source site: https://www.tiles.run Generated: 2026-06-15T14:20:03.975Z Canonical index file: /llms.txt Also available at: /.well-known/llms-full.txt ================================================================================ ## Homepage (https://www.tiles.run/) Tiles Tiles is a local-first AI assistant that runs models on your device, syncs privately across devices, and lets you share chats through AT Protocol. Hero subtext: For sensitive knowledge work, from client strategy to research you cannot put in the cloud: run models locally, sync with P2P encrypted sync, and share via ATProto while your data and identity stay private. Current status: CLI alpha. Why Tiles: sensitive knowledge work stays private on your machines, with secure collaboration built in; out-of-the-box on first open without API keys, model or harness selection, sync sessions and work across devices without leaking data to a cloud vendor, share chats publicly or privately without copy-pasting threads elsewhere, social features built on ATProto, giving you sovereignty over your online identity and data, Offline Installer bundles the model for air-gapped use. Linux availability form: https://www.tiles.run/linux ================================================================================ ## Download (https://www.tiles.run/download) Network installer for macOS: https://download.tiles.run/tiles-0.4.11-signed.pkg Version: 0.4.11 Size: 95.87 MB SHA256: 9b68d642caf589fd9eafaae95929e5623eb361964b029971a89b66eb1eaed2d1 Offline installer for macOS (bundled model: gpt-oss-20b-MXFP4-Q4): https://download.tiles.run/tiles-0.4.9-full-signed.pkg Size: 10.31 GB SHA256: fc2bbaf0408a3355d3079bf3435a2eba145c63bea48c35d3d14bb4a518a9a748 SHA256 file: https://download.tiles.run/checksums/tiles-0.4.9-full-signed.pkg.sha256 ================================================================================ ## Mission (https://www.tiles.run/mission) Our mission is to bring privacy technology to everyone. Tiles Privacy was born from the User & Agents community with a simple idea: software should understand you without taking anything from you. Tiles focuses on privacy-focused engineering and practical consumer experience. ================================================================================ ## Support (https://www.tiles.run/support) Get help. Find the shortest path from stuck to moving again. Documentation: install Tiles, read the manual, check Tilekit docs, and get unstuck on setup. GitHub Issues: report reproducible bugs, request features, and track fixes in the open. Discord: fast async help from users and maintainers when you need a quick route forward. Support FAQ: include Tiles version, operating system version, exact output, screenshots if useful, and the shortest reproduction steps for bug reports. Feature requests should include workflow context, current workaround, and the outcome you want. Releases has all versions and download links. Download has the latest version. Status has service availability. ================================================================================ ## Blog Index (https://www.tiles.run/blog) The Tiles Blog Privacy technology for everyone. Posts here use the Standard.site lexicon for rich presentation in supported ATProto clients and are stored on a PDS hosted by Eurosky. Published posts: 3 ================================================================================ ## Blog Post: Controlling the Ctrl-C (https://www.tiles.run/blog/controlling-ctrl-c) Published: 2026-06-08 Author: Anandu Pavanan @madcla.ws Author URL: https://bsky.app/profile/madcla.ws Description: Observations while trying to properly exit from Tiles CLI Content: The REPL UI issue As a local AI assistant, Tiles embeds Pi agent for agent harness. Since the REPL is written in Rust and Pi is in TypeScript, we embed the Pi Bun binary and use it via Pi's RPC mode, spawn a headless Pi binary as a child process, and communicate with it via standard streams (stdin/stdout/stderr) in JSON format. The user input to the model and the streamed output from the model come and go through Pi. It sits between the REPL and the inference system. As with any LLM inference interface, Tiles REPL must stop the output streaming from the model as soon as the user presses Ctrl-C and the REPL should return to prompt state, ideally like this. But in the versions before v0.4.11, although the streaming ends and REPL returns to prompt state, on the next user input we were greeted with a broken pipe error like Err value: Os { code: 32, kind: BrokenPipe, message: "Broken pipe" } . Because of this, users had to restart the REPL to continue. More details are in this issue . The broken pipes As mentioned, we spawn the Pi process as a child process and communicate through its stdin and stdout. For this reason the streams are connected via pipes rather than the child inheriting the parent's streams (which is the default behavior). If the child inherited the parent's streams, communication would be messy and processes would have to filter what they need from the shared stream, which would introduce race conditions. When piped, the parent can write to the child's stdin; the child writes to its own stdout, which the parent can then read, giving a clear separation of concerns. This is how we explicitly set the streams to be piped in Rust. let pi_process = Command::new(pi_exec_path) .arg("--mode") .arg("rpc") // default is Stdio::inherit .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("failed to run Pi"); So when we press Ctrl-C, the SIGINT (signal interrupt) event is propagated to the child processes, and if they don't handle it then the default behavior is to exit. To monitor this, we can use the following commands on Unix systems. ps -o pid,ppid,pgid,sid,command | grep tiles 88068 65985 88608 target/debug/tiles Here the processId is 7011, the parent's processId is 65985 (system root is parent), and the process's groupId is 7011. To inspect the children, we can use: // where 88068 is pid for tiles pstree -p 88068 \-+= 88068 tiles target/debug/tiles |--= 88098 tiles target/debug/tiles daemon \--= 88099 tiles /Users/tiles/tiles/.tiles_dev/tiles/pi/pi --mode rpc We can see the Pi process is running as a child of Tiles with PID 88099. We can use lsof to further monitor their relationship as follows: ➜ lsof -p 88099 // Pi's PID (child) COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME pi 88099 tiles 0 PIPE 0xd5c96c81e3aec5c2 16384 ->0xa95f7f46dfb5efe6 pi 88099 tiles 1 PIPE 0x745d3d0e7a7709aa 16384 ->0x27aceab27161a82 ➜ lsof -p 88429 // Tile's PID (parent) COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME tiles 88068 tiles 16 PIPE 0xa95f7f46dfb5efe6 16384 ->0xd5c96c81e3aec5c2 tiles 88068 tiles 17 PIPE 0x27aceab27161a82 16384 ->0x745d3d0e7a7709aa Here we can see Pi's stdin (FD=0) is connected to Tiles stdout (FD=16) and vice versa. So when we press Ctrl-C to stop a streaming response, the signal propagates to the Pi process and Pi exits. When we try a new user prompt next time in the REPL, unbeknownst to the REPL that such a process does not exist, it still tries to write to Pi's non-existent stdin, which gives us a broken pipe, aka a broken connection. Letting them go Since by default a child process is in the same process group (pgid) as the parent, the SIGINT event is propagated to all the processes in the group. One way is to remove the child from the same group as the parent. This means they still have a parent-child relationship, but the parent is no longer in direct control of the children. On Unix we can use setsid for this to create a new session and set the current process as the leader of it. But to do that in Rust is an unsafe operation (where we lose safety assurance from the compiler), as setsid is only available in the nightly version as of now, so we need to call it via C FFI . So setsid is achieved via other libraries such as libc , nix , etc., of course colored by the unsafe keyword. let pi_process = unsafe { Command::new(pi_exec_path) .arg("--mode") .arg("rpc") .stdin(Stdio::piped()) .stdout(Stdio::piped()) // runs the below closure before executing the // command function, here we run setsid for the // spawned process. .pre_exec(|| { if libc::setsid() == -1 { return Err(std::io::Error::last_os_error()); } Ok(()) }) .spawn() .expect("failed to run Pi") }; This resolves our broken pipe issue, as the SIGINT is never reaching the Pi process. But now we have another issue on hand: the model streaming is non-stoppable via Ctrl-C, and the REPL is completely unresponsive during the entire time we are streaming the output from Pi's stdout. It's no longer respecting SIGINT. Controlling the Control-C Thanks to a serendipitous moment while surfing the rustyline repo, which we use for building a nice UX for our REPL, we found that the version we were using (v17) had a bug which masks the SIGINT event. So we upgraded to the latest version, and now we are receiving SIGINT while the model is streaming, but the SIGINT exits the program altogether instead of stopping the stream and returning to the user prompt. This is in fact expected, as normally programs should handle SIGINT themselves if they want to do cleanups, graceful shutdowns, etc. So we use the ctrlc library to handle SIGINT, which uses a dedicated thread for handling the event. let is_running = Arc::new(AtomicBool::new(true)); let is_running_ref = is_running.clone(); ctrlc::set_handler(move || { is_running_ref.store(false, std::sync::atomic::Ordering::SeqCst); }) .... .... while is_running.load(std::sync::atomic::Ordering::SeqCst) { // loop breaks when SIGINT is fired and above handler toggles // is_running to false } But again, for some reason we are back to square one where the REPL is non-responsive to Ctrl-C when it's streaming the output. Turns out the way we read from Pi's stdout is a synchronous, blocking operation. So we tried converting all the functions related to this to async using the corresponding async functions provided by tokio (an async runtime library for Rust). For example, the core operation here is using a buffered reader to read from Pi's stdout efficiently, so we replace the BufReader from the std library with the async BufReader provided by the Tokio runtime. Tokio uses co-operative scheduling to switch between its tasks, so when we use an async function, it will yield frequently instead of blocking throughout the process. Once we refactored the codebase to be async, we started getting SIGINT events in the handler we set using the ctrlc library before, and the program no longer exits either. Now all we have to do is abort the Pi session by sending an abort event to Pi and do the cleanup from our side. while let Some(line) = reader.next_line().await? { if !is_running.load(std::sync::atomic::Ordering::SeqCst) { info!("Ctrlc detected, aborting Pi ops"); let end_payload = json!({ "type": "abort", }); // sending abort event to Pi send_to_pi(pi_stdin, end_payload).await?; // toggling is_running back to true, once we handled // the ctrl-c is_running.store(true, std::sync::atomic::Ordering::SeqCst); continue; } For more details on the sync-async conversion, see the PR diff . Unexpected entry of SIGPIPE The interesting thing now is that when we exit the main REPL program, the Pi process also exits, which shouldn't be the case as both are now in different process groups, right? Could this be related to the pipes getting closed on one end? Although this is fine for us, as we don't want the Pi process to be a background daemon and go rogue, it's important to understand what's happening under the hood, as we also have a Tiles daemon process (which is a background headless Tiles HTTP server) that is still alive even after the main REPL program closes, as it's supposed to be (this was also spawned in a different process group). // where 88068 is pid for tiles pstree -p 88068 \-+= 88068 tiles target/debug/tiles |--= 88098 tiles target/debug/tiles daemon \--= 88099 tiles /Users/tiles/tiles/.tiles_dev/tiles/pi/pi --mode rpc PID=88098 is our daemon. Why the dual behavior for the same action? For that we can live-debug the Pi program using lldb (LLVM debugger) to see what happens when the parent exits. We will attach the Pi process to lldb, add a breakpoint for SIGPIPE, then step through to see if Pi is handling SIGPIPE or not. The actions we take are commented with numbered index. // (1) Starting lldb for the Pi process ➜ sudo lldb -p 88099 No entry for terminal type "xterm-ghostty"; using dumb terminal settings. // (2) Attaching Pi PID to the debugger (lldb) process attach --pid 88099 Process 88099 stopped Target 0: (pi) stopped. Executable binary set to "/Users/tiles/tiles/.tiles_dev/tiles/pi/pi". Architecture set to: arm64-apple-macosx-. No entry for terminal type "xterm-ghostty"; using dumb terminal settings. // (3) Adding a breakpoint for SIGPIPE and notify us (lldb) break set -n write Breakpoint 1: 19 locations. (lldb) process handle SIGPIPE --pass false --stop true --notify true NAME PASS STOP NOTIFY =========== ===== ===== ====== SIGPIPE false true true // (4) Resuming the debugger (lldb) process continue // At this point we exit the Tiles repl and debugger pauses // Pi Process 88099 resuming Process 88099 stopped // (5) At this point, Tiles repl already exit, and now we // remove the SIGPIPE breakpoint (lldb) process handle SIGPIPE --pass true --stop false --notify true NAME PASS STOP NOTIFY =========== ===== ===== ====== SIGPIPE true false true // (6) and continue the Pi program after removing the breakpoint (lldb) process continue // We can see that Pi program exits as soon as it // receives SIGPIPE Process 88099 resuming Process 88099 exited with status = 0 (0x00000000) As seen in the lldb logs, the program exits as soon as it receives SIGPIPE, so Pi doesn't have a handler for SIGPIPE, which causes it to exit. Conclusion Debugging a seemingly trivial terminal UI issue led us into a rabbit hole of standard streams, pipes, Unix processes, and their dynamic behavior on system signals with respect to their parent, and finally to the problems caused by blocking I/O in a UI and how async Rust can fix it. ================================================================================ ## Blog Post: Ship it up (https://www.tiles.run/blog/ship-it-up) Published: 2026-04-05 Author: Anandu Pavanan @madcla.ws Author URL: https://bsky.app/profile/madcla.ws Description: How we package and ship Tiles Content: We ship Tiles as a tar.gz tarball and as a native macOS .pkg . Either format follows the same broad steps. Assemble the deliverable: Package the Tiles binary and the Python venvstack artifacts into one installer payload. Run the installer: Copy the Tiles binary and Python artifacts into the correct locations on disk. Run postinstall scripts. In early releases, the tarball was the only install path. It served us well, but it had clear limits. Because the release artifact is a gzip tarball, we cannot codesign, notarize, and staple the archive itself the way we can a full installer. We run those steps on the Tiles binary only, not on the entire .gz file. Installation relies on a curl-based script. That is workable for developers and rougher for everyone else. The flow leaves little room to tailor copy, branding, or steps in the installer UI. We wanted a path that fits Apple's signing and notarization story and feels native on macOS. Installers ship as .dmg or .pkg bundles; we chose .pkg because it can run scripts, uses a familiar installer UI, and supports customization with simple HTML. What follows is how we build that .pkg for Tiles. Install file structure The layout matches the directory tree above. When we build the package, we point the tooling at this pkgroot directory as the install root. The installer copies that tree onto the destination volume and creates intermediate directories as needed. For each release, we build a fresh Tiles binary into pkgroot/usr/local/bin and place the remaining updated artifacts under pkgroot/usr/local/share/tiles/ . The build script in the repo has the full sequence. Code signing the Tiles binary Code signing gives us: A signed Tiles binary that cannot be altered without invalidating the signature. A signature from a developer certificate that Apple trusts. You need an Apple Developer account and two certificate types: DEVELOPER ID APPLICATION for the binary and DEVELOPER ID INSTALLER for the .pkg . The finished installer wraps the signed binary and the other artifacts in one compressed package. For creating and exporting those certificates, the CodeVamping macOS pkg installer article covers the details. # Signing the Tiles binary codesign --force \ --sign "$DEVELOPER_ID_APPLICATION"\ --options runtime \ --timestamp \ --strict \ "${CLI_BIN_PATH}/tiles" The snippet signs the Tiles CLI. $DEVELOPER_ID_APPLICATION is an environment variable that holds the common name of the Developer ID Application certificate. Scripts Bash scripts can run before and after the payload is laid down. We keep preinstall and postinstall scripts in a directory and pass that path into pkgbuild . The Tiles repo has those scripts ; they handle cleanup and internal setup. Building the Tiles package With the signed Tiles binary and the rest of the install tree under pkgroot , we run: pkgbuild --root pkgroot --scripts \ pkg/scripts --identifier com.tilesprivacy.tiles --version "$VERSION" \ pkg/tiles-unsigned.pkg --root is pkgroot ; --scripts points at the directory from the previous section. Customizing the installer Opening the unsigned package from the previous step shows Apple's default, minimal flow. We layer on welcome and conclusion screens, a logo, and other panels with an Apple distribution definition in XML. That file references HTML for the welcome, conclusion, and other supported steps. Our distribution_network.xml is the working example. Elements such as and point at resources we keep alongside the definition and pass into the final productbuild invocation. productbuild \ --distribution pkg/distribution_network.xml \ --resources pkg/resources \ --package-path pkg/ \ pkg/tiles-dist-unsigned.pkg productbuild reads the unsigned package and writes a distributable installer with those customizations baked in. Code signing the complete installer productsign \ --sign "$DEVELOPER_ID_INSTALLER" \ pkg/tiles-dist-unsigned.pkg \ pkg/tiles.pkg That signs the .pkg itself. The Tiles binary is already signed; signing a macOS binary and signing an installer package are different operations. The main difference is productsign instead of codesign , and the certificate we use is DEVELOPER ID INSTALLER instead of DEVELOPER ID APPLICATION . Notarizing the installer Notarization lets Apple scan the payload for malware. We upload the installer; when processing finishes, the service returns acceptance or rejection. Submission looks like this: xcrun notarytool submit pkg/tiles.pkg \ --keychain-profile "tiles-notary-profile" \ --wait tiles-notary-profile is the keychain entry name so we do not pass Apple ID details on every run. Store the profile once with: xcrun notarytool store-credentials \ "tiles-notary-profile" \ --apple-id "john.doe@gmail.com" \ --team-id "X********4" \ --password "****-****-****-****" Stapling the installer Stapling embeds the notarization ticket in the installer file so Gatekeeper needs fewer round trips to Apple when a user opens the package. xcrun stapler staple pkg/tiles.pkg What's next We also ship a fully offline installer that bundles OpenAI's gpt-oss 20B model, so Tiles can be installed without an internet connection. We are working toward making Tiles itself portable, allowing users to carry their model and data and run it from a flash drive on any compatible system. The installer requires Rosetta; on Apple Silicon Macs, it is not included by default and must be installed separately. Rosetta translates Intel binaries to run on Apple Silicon. There is a workaround for this, described in the linked GitHub issue . ================================================================================ ## Blog Post: Move Along, Python (https://www.tiles.run/blog/move-along-python) Published: 2026-02-17 Author: Anandu Pavanan @madcla.ws Author URL: https://bsky.app/profile/madcla.ws Description: Deterministic, portable Python runtimes for Tiles using layered venvstacks. Content: 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. The Python Problem Right now, we have a polyglot architecture where the control pane 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. Since 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. In 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. This approach had several issues: Downloading development-related tools such as uv onto the user’s system Relying on uv at install time to manage dependencies and run the server Increased chances of failures due to dependency or runtime non-determinism Requiring internet access to download all of the above tools Lack of a fully deterministic runtime across operating systems One of the long-term goals of Tiles is complete portability. The previous approach did not align with that vision. Portable Runtimes To 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. Venvstacks allows us to build a layered Python environment with three layers: Runtimes Defines the exact Python runtime version we need. Frameworks Specifies shared Python frameworks such as NumPy, MLX, and others. Applications Defines the actual server application and its specific dependencies. Similar to Docker, each layer depends on the layer beneath it. A change in any layer requires rebuilding that layer and the ones above it. All 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 . We define everything inside a venvstacks.toml file. Here is the venvstacks.toml used in Tiles. Because we pin dependency versions in the TOML file, we eliminate non-determinism. Internally, venvstacks uses uv to manage dependencies. Once the TOML file is defined, we run: venvstacks lock venvstacks.toml This resolves dependencies and creates the necessary folders, lock files, and metadata for each layer. Next: venvstacks build venvstacks.toml This builds the Python runtime and environments based on the lock files. Finally: venvstacks publish venvstacks.toml This produces reproducible tarballs for each layer. These tarballs can be unpacked on external systems and run directly. We 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. Our Rust CLI can then predictably start the Python server using: stack_export_prod/app-server/bin/python -m server.main What’s Next We tested version 0.4.0 on clean macOS virtual machines to verify portability, and the approach worked well. For 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. Packaging the runtime and dependencies increases the size of the final installer. We are exploring ways to reduce that footprint. We also observed that changes in lock files can produce redundant application tarballs when running the publish command. More details are tracked in this issue . Overall, we are satisfied with this approach for now. ================================================================================ ## Book: Acknowledgements (https://www.tiles.run/book/acknowledgements) # Acknowledgements Tiles utilizes the following open-source software. We are thankful to the developers and maintainers of these libraries! | Library/Resource | Description | License | Link | | --- | --- | --- | --- | | ml-explore/mlx-lm | Run large language models locally with MLX on Apple Silicon. | MIT | mlx-lm | | earendil-works/pi | AI agent toolkit with a unified LLM API, agent loop, TUI, and coding-agent CLI. | MIT | pi | | n0-computer/iroh | Modular peer-to-peer networking stack in Rust for distributed apps. | Apache-2.0 | iroh | | sqlcipher/sqlcipher | Standalone SQLite fork with AES-256 encryption and other database security features. | BSD-3-Clause | SQLCipher | | rusqlite/rusqlite | Ergonomic bindings to SQLite for Rust. | MIT | rusqlite | | ucan-wg/rs-ucan | Rust implementation of UCAN. | Apache-2.0 | rs-ucan | | atrium-rs/atrium (atrium-oauth) | OAuth client library for AT Protocol. | MIT | atrium-oauth | | huggingface/hf-hub | Rust client for the Hugging Face Hub. | Apache-2.0 | hf-hub | | openai/harmony | Renderer for the Harmony response format used with gpt-oss. | Apache-2.0 | harmony | | openai-harmony (PyPI) | Python package for OpenAI's Harmony response format for gpt-oss. | Apache-2.0 | openai-harmony | | mozilla-ai/openresponses-python | Python SDK for the OpenResponses specification. | Apache-2.0 | openresponses-python | | openresponses/openresponses | Open specification for multi-provider, interoperable LLM interfaces. | Apache-2.0 | OpenResponses | | openai/gpt-oss-20b | Open-weight 20B language model from OpenAI. | Apache-2.0 | gpt-oss-20b | | mlx-community/gpt-oss-20b-MXFP4-Q4 | MLX-quantized gpt-oss-20b model for local inference. | Apache-2.0 | gpt-oss-20b-MXFP4-Q4 | | openai/gpt-oss | Open-weight gpt-oss language models and reference tooling. | Apache-2.0 | gpt-oss | | lmstudio-ai/venvstacks | Virtual environment stacks for Python. | MIT | venvstacks | This list is still a work in progress and will be updated as we continue to add more features and dependencies to Tiles. ================================================================================ ## Book: Community (https://www.tiles.run/book/community) # Community ## Get Involved Whether you are already committed to privacy technology or just beginning to reclaim ownership of your computing experience, there are many ways to get involved in the Tiles community. To understand the project goals and architecture, start with: - Install Tiles CLI and try it out. - Explore the documentation: Tiles Book ## Communication and Support If you have questions or want to discuss ideas, you can reach the team and community via: - Discord: https://go.tiles.run/discord - Email: hello@tiles.run ## Roadmap Track our progress and see what's coming next on our Roadmap. The roadmap outlines our planned features, improvements, and research directions. ## RFCs Join or contribute to the conversation about Tiles design and architecture through Tiles RFC Discussions. RFCs (Request for Comments) are proposals that introduce major features, architectural changes, or significant new areas of functionality. We welcome community input on these proposals. ## Contributing Thanks for your interest in contributing to Tiles. We welcome contributions of all kinds, including code, documentation, design, and discussion. ### How to Contribute You can contribute in several ways: - Report bugs or request features by opening an issue - Improve documentation or examples - Submit pull requests for bug fixes, refactors, or new features - Open an RFC discussion for proposals that introduce major features, architectural changes, or significant new areas of functionality Before starting work, check the existing issues to avoid duplication and to align with current priorities. If you plan to work on a larger change, open an issue or discussion first to confirm scope and approach. ### Pull Request Guidelines When submitting a pull request: - Keep changes focused and scoped to a single concern - Follow existing code style and conventions - Include clear commit messages and a concise PR description - Reference relevant issues where applicable - Ensure all checks and tests pass New inference backends, memory models, and improvements aligned with the roadmap are especially welcome. ## Development Setup This guide will help you set up a reproducible development environment for Tiles. Tiles supports two environments: prod (production) and dev (development). These instructions assume you are setting up for local development. ### Prerequisites - Rust & Cargo - just (for task management) - Python 3.13 - uv (for fast Python dependency management) - Git ### Setup Steps 1. **Clone the repository:** [code block] 2. **Install Rust dependencies:** If you're new to Rust, see Rust Install Guide. [code block] 3. **Install project task runner:** just provides easy command shortcuts. [code block] 4. **Set up the Python server environment:** - Make sure uv is installed: [code block] - Sync Python dependencies: [code block] ### Running Tiles (Development) Open two terminal windows: 1. **Terminal 1: Start the server** From the project root: [code block] 2. **Terminal 2: Run the Rust CLI** From the root directory: [code block] > **Tip:** Refer to the justfile for additional common commands and automation. For troubleshooting, see CONTRIBUTING.md and open an issue if you need help. ### Building Tiles installer (Development) Install venvstacks for portable py runtime From the project root do: [code block] Set the ENV in install.sh to dev [code block] Now tiles should be available in PATH ## Code of Conduct By participating in this project, you agree to follow the project's Code of Conduct. Be respectful, constructive, and collaborative in all interactions. We appreciate your time and effort in helping build Tiles. ================================================================================ ## Book: Tiles Book (https://www.tiles.run/book) import { marketingPageTitleClass } from "@/lib/marketing-page-title-classes"

Book

Technical documentation covering the models, infrastructure, and cryptography behind Tiles, the consumer product, and Tilekit, the developer-facing SDK written in Rust. AI / LLM agents: For a machine-readable summary of this site, see /llms.txt or /llms-full.txt. ================================================================================ ## Book: Licenses (https://www.tiles.run/book/licenses) # Licenses Tiles is free to use without limits. Licenses are optional and help fund independent development. ## Overview - **Backer license**: for individual supporters with a one-time payment. - **Commercial license**: for teams and organizations, priced per user per year. - **Commercial use**: payment is encouraged, not required. ## Polar checkout details Tiles purchases are processed through Polar, which acts as merchant of record for payment processing. ### Customer portal After checkout, you can manage your license key, subscription renewals, billing, and related purchase details in Polar’s customer portal. Sign in and self-serve updates there: https://polar.sh/tilesprivacy/portal/ ## Backer The Backer license is designed for individual supporters who want to help sustain the project. ### Payment - Price: **$50 USD** - Cadence: **one-time payment** - Checkout: Buy Backer license ### What it includes - Managed public relays for sync - Valid for up to 5 devices - Community support channel ## Commercial The Commercial license is designed for teams and organizations using Tiles for work. ### Payment - Price: **$100 USD** - Cadence: **per user, per year** - Checkout: Buy Commercial license - Bulk purchases: support@tiles.run ### What it includes - Self-hosted relays for sync - Featured organization status - Valid for up to 5 devices - Priority support channel ## Notes - Backer and Commercial licenses are non-refundable. - Tiles remains free to use without payment. - License purchases support the long-term independence of Tiles. ================================================================================ ## Book: Manual (https://www.tiles.run/book/manual) import { ClickableImage as Image } from '@/components/clickable-image' # Manual ## Command Line Options Commands are grouped by subcommand family: the base tiles command, identity, data layout, models, updates, ATProto linking for sharing, then peer-to-peer linking and sync. ### Main command (tiles) Launches onboarding (first run) and the interactive REPL. [code block] ### Account (tiles account) Root identity and display nickname. [code block] [code block] ### Data folder (tiles data) Where Tiles stores local data files. [code block] [code block] ### Run models (tiles run) Start a chat session with a model; optional Modelfile path. [code block] [code block] Create a new Modelfile by adding a plain text file named Modelfile: [code block] Use any mlx-community/ model hosted on Hugging Face in the FROM field. Other model hosts and namespaces are not supported there right now. Learn more in the Tilekit Modelfile reference. ### App updates (tiles update) [code block] ### Peer-to-peer: linking (tiles link) Tiles can sync chats across multiple **linked** devices peer-to-peer, with or without the internet. Linking is a one-time step so both sides consent before any sync traffic is accepted; you do not need to re-link each time you sync. [code block] **tiles link enable**: Puts this device in **link listener** mode. It generates a **link ticket** and waits for a link request from another device. Share the ticket **out of band** with the peer you want to link. - If the device is **online**, the ticket is a **base64** string (easy to copy). - If **offline**, the ticket is an **eight-character alphanumeric code** you can type on another device on the same network. When the peer runs **tiles link enable** and supplies your ticket, this device is notified; after you **approve**, the two devices are linked. **tiles link list-peers**: Lists linked peers by **DID** (decentralized identifier) and **nickname**. **tiles link disable**: Unlinks a peer. Future sync requests from that peer are ignored. ### Peer-to-peer: chat sync (tiles sync) After devices are linked, use **tiles sync** to replicate chats. Listener and initiator roles mirror linking: one side waits, the other connects using the listener’s DID. [code block] **tiles sync** (listener): Starts this device in **sync listener** mode: it waits for incoming sync requests. On the **other** device, run **tiles sync** with the listener’s **DID** so the initiator targets the correct peer. If your CLI uses different syntax for the peer argument, check tiles sync --help. When a sync run finishes, you should get a notification on the devices involved. ### ATProto (tiles at) ATProto is currently used to share chat sessions. - tiles at login : log in with your ATProto handle (for example, alice.bsky.social) via browser OAuth. - tiles at logout: log out of the current ATProto account. [code block] Authentication currently uses a localhost callback (127.0.0.1:8988), and session state is stored locally in the Tiles database. ## Slash commands Slash commands give you fast, keyboard-first control over Tiles in interactive chat. Type /? or /help in the prompt to open available commands and quickly run actions like checking session state, resuming prior sessions, and sharing chats without leaving the terminal. Use /bye to exit the chat session. ### Session Commands - /status Show the current session state. - /sessions List available sessions in local storage. - /resume Resume a specific session by ID. ### Sharing Commands These commands require an active ATProto login. See #ATProto (tiles at). - /share (via ATProto) Create a shareable link for the current session. - /share (via ATProto) Create a shareable link for a specific session. Example shared session link: tiles.run/share/YXQ6Ly9k...xeTI3. ## Uninstall Tiles (macOS) By default, uninstalling Tiles removes only binaries and shared payloads under /usr/local. User data files under ~/.local/share/tiles (or a custom [data] path in config.toml) are not deleted, so chats, memory, databases, and logs remain for a later reinstall. Wipe that data only when you want a clean slate. Read the full step-by-step procedure in the Tiles uninstaller skill, including optional receipt and keychain cleanup. With coding agents (for example Claude Code or Codex), share that URL in your prompt and ask the agent to follow it, adapting paths that differ on your machine. We are actively working on an official Tiles uninstaller. ================================================================================ ## Book: MIR Extension (https://www.tiles.run/book/mir) # MIR Extension
MIR MIR
Based on the specifications defined by Darkshapes' MIR (Machine Intelligence Resource), a naming schema for AIGC and ML work, and Ollama's Modelfile, this blueprint aims to extend Modelfile's capabilities to support intent-based model chaining driven by the prompt, with an implementation currently in progress. [code block] ## MIR Reference Copied verbatim from: github.com/darkshapes/MIR: > MIR (Machine Intelligence Resource)

A naming schema for AIGC/ML work. The MIR classification format seeks to standardize and complete a hyperlinked network of model information, improving accessibility and reproducibility across the AI community.
Example: > mir : model . transformer . clip-l : stable-diffusion-xl [code block] Code for this project can be found at darkshapes/MIR on GitHub. ### Definitions Like other URI schema, the order of the identifiers roughly indicates their specificity from left (broad) to right (narrow). #### DOMAINS > ↑Most Specific/Decentralized ###### Dev **Pre-release or under evaluation items without an identifier in an expected format** Anything in in-training, pre-public release, and items under evaluation Meant to be created by anyone, derived from code and file analysis - Contextual - Layers of neural networks - Dynamic ###### Model **Publicly released machine learning models with an identifier in the database** Model weight tensors with arbitrary locations and quantitative dimensions Meant to be created by file hosts, derived from research pre-prints - Contextual - Layers of neural networks - Fixed ###### Ops **References to specific optimization or manipulation techniques** Algorithms, optimizations and procedures for models Meant to be created by code libraries, derived from research pre-prints - Universal - Attributes of neural networks - Dynamic ###### Info **Metadata of layer names or settings with an identifier in the database** Information about the model and tensor specifications Meant to be created by standards community, derived from code and file analysis - Universal - Attributes of neural networks - Fixed > ↓Most General/Centralized #### ARCHITECTURE Broad and general terms for system architectures: | Abbreviation | Description | | --------------- | ----------------------------------------- | | GRU | Gated recurrent unit | | RBM | Restricted Boltzmann machine | | TAE | Tiny Autoencoder | | VAE | Variable Autoencoder | | LSTM | Long Short-Term Memory | | RESNET | Residual Network | | CNN | Convolutional Neural Network | | RCNN | Region-based Convolutional Neural Network | | RNN | Recurrent Neural Network | | BRNN | Bi-directional Recurrent Neural Network | | GAN | Generative Adversarial Model | | SSM | State-Space Model | | DETR | Detection Transformer | | VIT | Vision Transformer | | MOE | Mixture of Experts | | AET | Autoencoding Transformer | | STST | Sequence-to-Sequence Transformer | | ART | Autoregressive Transformer | | LORA | Low-Rank Adaptation | | CONTROLNET | Controlnet | | UNCLASSIFIED | Unknown | --- #### SERIES Foundational network and technique types. ##### Rules - Lowercase, hyphen only - Remove parameter size, non-breaking semantic versioning, library names Example: tencent-hunyuan/hunyuandiT-v1.2-diffusers
SERIES : hunyuandit-v1 Example: black-forest-labs/FLUX.1-dev
SERIES : flux1dev In regex (roughly): [code block] #### COMPATIBILITY Implementation details based on version-breaking changes, configuration inconsistencies, or other conflicting indicators that have practical application. ##### Rules An additional SERIES label for identifying cross-compatibility ### Notes If you would like to regenerate or update the example file here, use nnll: MIR is inspired by: - AIR-URN project by CivitAI - Spandrel super-resolution registry by chaiNNer - SDWebUI Model Toolkit by silveroxides ================================================================================ ## Book: Models (https://www.tiles.run/book/models) # Models The local inference landscape moves quickly, and excellent new projects appear all the time. Our approach is intentionally narrower. Rather than chasing every new release, we focus deeply on integrating one model at a time within the constraints of the hardware we support. Today, Tiles is designed around everyday productivity tasks, with plans to expand into more domain-specific workflows over time. We provide a carefully tested combination of model, inference parameters, and agent harness, so users do not have to manage model selection, tuning, or scaffolding themselves. We currently use the OpenAI OpenAI logogpt-oss-20b model as the primary model for everyday tasks, alongside the Harmony renderer and support for the Open Responses API to improve inference quality. Its Mixture of Experts architecture activates roughly 4B parameters during inference out of 21B total parameters, offering a strong balance between quality and efficiency. That narrow focus gives us room to validate more deeply over time. We aim to compare against official outputs, run long-context evaluations, and integrate models directly into the agent harness to see how they actually hold up in real workflows. The exact model may change as the landscape evolves, but the constraint remains the same: local inference should be practical and credible on everyday personal machines, starting at 16 GB of system memory for real productivity tasks. ================================================================================ ## Book: Overview (https://www.tiles.run/book/overview) import Link from "next/link" import { Check, CircleDashed } from "lucide-react" import { BookFaq, BookFaqItem } from "@/components/book-faq" import { ClickableImage as Image } from "@/components/clickable-image" import { ProductFeatureWidgets } from "@/components/product-feature-widgets" # Overview We make two things: Tiles, our consumer product, a local-first AI assistant (currently a CLI in alpha); and Tilekit, the SDK for developers to build on the infrastructure behind Tiles. **Status: Alpha** Tiles is currently alpha-quality software. It is usable for everyday tasks, though you may encounter bugs and performance issues. Tilekit, the developer SDK, is experimental, not a current priority, and intended for exploratory use, not production. ## Features
## What makes Tiles different Tiles is built around a narrow bet: one well-integrated model at a time, starting with OpenAI logo gpt-oss-20b, optimized for credible local inference on Apple Silicon systems with 16GB+ memory while balancing privacy, capability, and everyday usability.
Capability Tiles Ollama LM Studio Jan Osaurus
Decentralized Identity Supported - Not supported - Not supported - Not supported Supported
Encryption Supported - Not supported Partially supported - Not supported Supported
Sync Supported - Not supported - Not supported - Not supported - Not supported
On-device models Supported Supported Supported Supported Supported
Cloud models - Not supported Supported - Not supported Supported Supported
In-house models - Not supported - Not supported - Not supported Supported Supported
Agent Harness Supported Supported Partially supported - Not supported Supported
Memory - Not supported - Not supported - Not supported - Not supported Supported
Connectors - Not supported Supported Supported Supported Supported
Sandbox - Not supported - Not supported - Not supported - Not supported Supported
Remote Link - Not supported - Not supported Supported - Not supported Supported
Shared Links Supported - Not supported - Not supported - Not supported - Not supported
Developer SDK Supported Supported Supported - Not supported Supported
Offline Installer Supported - Not supported - Not supported - Not supported - Not supported
Cross platform - Not supported Supported Supported Supported - Not supported
Open source Supported Partially supported Partially supported Supported Supported
Supported Partially supported - Not supported
## Product walkthrough ### Talk video Watch our talk about Tiles at the MLX India Community Meetup: