tiinyapp.farm

Getting started

Install the farm command, point it at your Tiiny, then install, start, update and remove apps.

An app from the catalog runs on your computer, not on the Tiiny. It reaches the device over the device's own local API, so the Tiiny has to be switched on and reachable from the machine you are working at. Nothing here changes TiinyOS.

Install the command

You need Python 3.9 or newer on macOS, Linux or Windows. The tool itself uses only the Python standard library, so there is nothing else to install.

pip install tiinyapp-farm
farm --version

If pip answers "externally managed environment", which Homebrew Python and recent Debian both do, use pipx instead.

pipx install tiinyapp-farm

To work from a checkout of the catalog repository rather than the published package:

git clone https://github.com/Titanium-Devops/tiinyapp-farm.git
cd tiinyapp-farm
python3 -m pip install .

Commands end by saying so, in one line, when a newer farm is out. farm self-update takes it, and nothing you have installed is touched by that. To never be told, pass --no-update-check or set FARM_NO_UPDATE_CHECK=1.

Find your Tiiny

The farm looks for it, so you do not have to know its address:

farm device --find
jason's Tiiny (TNYM00000000000000X) at 192.0.2.1, over the cable, base http://192.0.2.1/v1
Run farm device to save it. It offers this address and asks for the key.

It looks on every USB cable in this machine, then on this network, then at the TiinyOS client. It saves nothing and asks for no key. If nothing answers it says so, and the address can still be typed in by hand:

ValueWhat to use
API base URL, on a Mac with the TiinyOS client installedhttp://openai.api.tiiny/v1
API base URL, from any other computer on the same networkhttp://<your-tiiny-ip>/v1
API keyTiinyOS, then Settings, then API Key

The base URL has to be an HTTP or HTTPS URL with no username, password, query or fragment in it. Anything else is refused.

Connect it

farm device

It looks for your Tiiny first and offers what it found, so pressing enter at the first question takes it:

Found jason's Tiiny (TNYM00000000000000X) at 192.0.2.1, over the cable, base http://192.0.2.1/v1
Device base URL [http://192.0.2.1/v1] (hidden):
Device API key (hidden):

Several Tiinys are numbered and it asks which one. Nothing found is the question on its own. It asks for both values without echoing them and writes ~/.tiinyapps/device.json with mode 0600 on macOS and Linux. On Windows the file follows the user folder's ACL, which is best effort. If there is no terminal that can hide what you type, it refuses rather than falling back to echoed input. Run it again at any time to change either value.

For a script, pass the base URL and pipe the key in, or put both in the environment:

farm device --base http://openai.api.tiiny/v1 --key-stdin < private-key-file
TIINY_BASE=http://openai.api.tiiny/v1 TIINY_KEY=... farm device

Explicit options win over the environment, and incomplete scripted input fails instead of falling back to a prompt. The key never has to appear in a command argument, and the command never prints it back.

After saving, it asks your Tiiny for its device page once. If macOS is blocking that Python from the local network, which it does silently for a Python you installed yourself, the farm finds another Python on this machine that can reach your Tiiny and runs your apps with that one instead. When anything at all looks wrong, farm doctor checks the lot in one go and prints a fix for each thing it finds. See The app cannot see my Tiiny.

Install an app

Browse the catalog, then install by the app's id.

farm install titanium-tiiny-bot

Before anything is downloaded it tells you what it is about to install, then asks once. Answer y to go ahead. In a script, -y prints the same lines and answers the prompt for you.

Looking up titanium-tiiny-bot in the catalog.
Titanium Tiiny Bot 0.1.15
A local assistant with chat, files, memories and voice.
Made by Titanium Computing. The farm has reviewed it.
Needs: Python 3.11 or newer, port 7788, your Tiiny, for chat, tts, 57 NPU units
It can reach your microphone, your files, the network and your Tiiny.
Install this release? [y/N] y
Downloading 456 KB from github.com.
The download matches the checksum the catalog lists.
Unpacking it into /Users/you/tiinyapps/titanium-tiiny-bot/0.1.15.
Ready. Run: farm start titanium-tiiny-bot

A library has nothing to start, so its last line names the file to copy or import instead.

What happens next, in order: the release archive is downloaded, its SHA-256 and its exact byte size are compared with the manifest, it is unpacked into a staging directory with the archive guards described in Permissions and archive rules, and only then does anything land in your app directory. A checksum that does not match means the archive is never unpacked and never run.

An app that is already installed is refused with a pointer to farm update. An app whose manifest still carries a pending checksum cannot be installed at all, and an app that needs a newer Python than the one running the command says so and stops.

Start it and stop it

farm start titanium-tiiny-bot
farm status
farm stop titanium-tiiny-bot

A start that worked ends with the link to open, what the app is for, and how to stop it:

Titanium Tiiny Bot is running.
Open http://localhost:7788
A local assistant with chat, files, memories and voice.
Stop it with: farm stop titanium-tiiny-bot
Log: /Users/you/tiinyapps/titanium-tiiny-bot/farm.log

The link is the app's root unless its catalog entry names a first page, and a health path is a probe rather than a page, so it is never used as the link. farm status shows the same link for every running app beside its process ID, port and uptime.

farm start runs the app's entry directly, without a shell, from its own version directory. It waits up to ten seconds for the app to be ready, checking that the process is still alive the whole time. If the manifest declares a health path, readiness on the first port means an HTTP GET of that path returning a JSON object; every other port just has to accept a TCP connection. A start that fails exits 1, prints the last ten lines of the app's log, and removes its process records so nothing is left half running.

farm stop sends SIGINT to the process group, waits five seconds, then sends SIGKILL. On Windows it terminates the process through a held handle so that a recycled process ID cannot be hit by mistake.

Neither command needs the app's name. Run farm stop on its own and it asks which of the running apps to stop, and farm start on its own asks which of the installed ones to start:

2 apps are running.
1. AINode Pocket 0.1.0 on port 7863
2. TiinyBench 0.1.1 on port 7864
Stop which? A number, "all", or Enter to leave them.

When only one app fits it asks about that one instead, Stop AINode Pocket? [Y/n], and yes is the default. Nothing running says so in one line.

The port rule

Every app declares the port it listens on. Before starting anything, farm start checks that the port is free. If it is taken and the app can be moved (its manifest says how it takes a port), the farm steps up to the next free port on its own and tells you:

Titanium Tiiny Bot is running.
Port 7788 was busy, so it started on 7789.
Open http://localhost:7789

An app with a fixed port is refused instead, because nothing can move it. To choose the port yourself, name it, and the farm never moves a port you chose:

farm start titanium-tiiny-bot --port 7799

The override replaces the first declared port only. The chosen port is exported to the app as TIINYAPP_PORT, and an app whose manifest says it takes a port some other way gets it that way too, so --port works whatever the app expects. A few apps cannot move at all, and those say so in one line instead of starting somewhere you did not ask for. farm status shows the port an app was actually started on.

Keep it current

To ask what is newer than everything you have, run the command with no app name. farm check is the same command under the word most people reach for:

farm update
Looking up all 2 apps you have installed in the catalog.
1. AINode Pocket 0.0.9, 0.1.0 is out, dated 2026-09-14.
2. Tiiny Brain 0.1.0, 0.1.1 is out, dated 2026-09-14.
Update which? A number, "all", or Enter to leave them.

Press a number to take that one, type all to take them in order, or press Enter to leave them where they are. When nothing is newer it says so in one line. In a script, where there is nobody to ask, it prints the list and changes nothing, and farm update --all takes every one of them.

Name one app and it asks about that one, with yes as the default:

farm update titanium-tiiny-bot
Titanium Tiiny Bot 0.1.14 is installed and 0.1.15 is out. Update it? [Y/n]

farm update installs only a strictly newer version. It never downgrades, and it stops the running old version only after the new archive has been downloaded, verified and unpacked. Your data directory is kept and it says so, and the previous version's code is left on disk. An app that was running is stopped for the update and started again on the port it was really on, and it tells you both.

farm list and farm status carry update available on any app the catalog has moved past, and farm start adds one line under the link when the app you just started has a newer version out. farm status also compares the version the running app reports through its health path with the version you have installed, and tells you to restart when they differ.

Take it away

farm remove titanium-tiiny-bot
farm remove titanium-tiiny-bot --purge

The first stops the app and deletes its code while keeping its data directory. The second deletes that data too. Your device settings and the shared lock directory survive either one, because they do not belong to any single app.

Where everything lives

PathWhat it is
~/tiinyapps/<id>/<version>/The unpacked app, one directory per installed version
~/tiinyapps/<id>/currentA one-line pointer to the version in use
~/tiinyapps/<id>/data/The app's own data, kept across updates and removal
~/tiinyapps/<id>/farm.logEverything the app writes to its output, appended
~/tiinyapps/<id>/farm.pidThe process ID of a running app
~/tiinyapps/<id>/process.jsonPorts, start time, version and health path of the running app
~/tiinyapps/<id>/launcher.jsonThe entry farm start uses
~/tiinyapps/.onelane/The shared device lock directory apps use to take turns
~/tiinyapps/.locks/One lock file per app, so two farm commands cannot collide
~/.tiinyapps/device.jsonYour device base URL and key, mode 0600
~/.tiinyapps/tokenYour farm API token, if you have created one

Settings written by a pre-0.1 installation at ~/tiinyapps/device.json are still read if the newer path does not exist. Running farm device again writes the newer path.

What the farm does not do

The permissions on an app page are what the author declared, and the command shows them to you before you install. It does not sandbox an installed app, and it cannot enforce those declarations once the app is running. Every app in the catalog ships its source, so read it if that matters to you.

All documentation Install an app Submit an app