Super Farm
On this page

Configuration reference

Every setting of a farm, with its default. Settings live in config.yaml in the farm home (sf config path prints where); the file holds only what differs from the defaults below.

sf config show                    the settings in effect
sf config get android.fps         one value
sf config set android.fps 25      change one value (validated before it is saved)
sf config unset android.fps       back to the default

Keys are written with dots: network.vpn.selection is selection under vpn under network. Farm-wide values are defaults; most can be overridden for a single Android with sf set <selector> key=value.

General#

KeyTypeDefaultAllowedDescription
versionwhole number1Config format version.
farm_nametext"Super Farm"up to 60 charactersName shown in the panel.

server#

KeyTypeDefaultAllowedDescription
server.hosttext"127.0.0.1"Address the panel and API listen on. Keep 127.0.0.1 unless you put TLS in front; anything else exposes farm control to the network. The API token is equivalent to root on this machine (the daemon controls Docker, and Androids are privileged containers): treat it like a root password.
server.portwhole number77001 to 65535Port of the panel and API.
server.auth_requiredtrue / falsetrueRequire the API token for every request. Turning this off is refused when host is not a loopback address; a daemon that is listening on another address keeps asking for the token until it is restarted on loopback.
server.tls_certtext""PEM certificate file. With tls_key, the server speaks HTTPS.
server.tls_keytext""PEM private key file for tls_cert.
server.session_hourswhole number121 to 720How long a browser login stays valid.
server.max_upload_mbwhole number4096at least 1Largest file (APK, video) the panel accepts.
server.allowed_originslist of text[]Extra browser origins allowed to call the API (CORS). Empty = same origin only.
server.audit_logtrue / falsetrueRecord every state-changing request (who, what, when) in logs/audit.log. Requests nobody authenticated (refused logins, wrong tokens) go to logs/audit-anonymous.log, so they cannot push the real history out.
server.login_attempts_per_minutewhole number10at least 1Wrong tokens allowed per client address per minute; further wrong ones from that address are refused for a minute. The right token is always accepted.
server.trusted_proxieslist of text[]Addresses or networks (e.g. 127.0.0.1, 10.0.0.0/8) of reverse proxies whose X-Forwarded-For and X-Forwarded-Proto headers are believed. Empty = none: the recorded client is always the peer that connected. A proxy listed here must set both headers itself.

docker#

KeyTypeDefaultAllowedDescription
docker.binarytext"docker"Docker CLI to use. The daemon runs this program: it is set in config.yaml or with sf config set, and cannot be changed through the API.
docker.name_prefixtext"sf"matches ^[a-z][a-z0-9]{0,11}$Prefix of every container, volume and network this farm creates. Two farms on one machine need different prefixes.
docker.stop_timeoutwhole number100 to 300Seconds an Android gets to shut down before it is killed.
docker.subnet_pooltext"10.213.0.0/16"Private range that shared-VPN gateway networks are carved from (one /24 each).
docker.log_max_sizetext"10m"Per-container log size cap (json-file driver).
docker.log_max_fileswhole number2at least 1Rotated log files kept per container.

android#

KeyTypeDefaultAllowedDescription
android.default_versiontext"13"Android version given to new instances.
android.imagesmap of name to settingssee android.images.<name>Android version -> image. Add an entry to support another version or a custom build.
android.widthwhole number480240 to 3840Screen width in pixels.
android.heightwhole number854320 to 3840Screen height in pixels.
android.dpiwhole number16080 to 640Screen density.
android.fpswhole number205 to 120Frames per second Android draws at. The single biggest CPU lever after the GPU mode.
android.extra_boot_argslist of text[]Extra androidboot.* / ro.* arguments appended to every Android's command line.
android.extra_propsmap of text to text{}System properties set on every Android after boot (setprop).

android.images.<name>#

One group per entry of android.images; <name> is the entry's name. Entries present by default: 9, 10, 11, 12, 13, 14, 15.

KeyTypeDefaultAllowedDescription
android.images.<name>.basetextrequiredUpstream image this Android version starts from.
android.images.<name>.tagtext""Use this exact local image instead of building one from base (for an image you built or imported yourself).
android.images.<name>.arm_translationtrue / falsetrueWhether the image is expected to run ARM-only apps (native bridge present).
android.images.<name>.fps_propertytext""matches ^([A-Za-z0-9_.\-]{1,90})?$Extra boot property that carries the frame rate, for an image whose compositor does not read the standard androidboot.redroid_fps (a patched build that takes its rate from a property of its own). The frame rate of every Android on this image is then passed under this name as well. Empty = the standard argument only.

resources#

KeyTypeDefaultAllowedDescription
resources.cpusnumber2.0more than 0, at most 256CPU cores one Android may use (Docker --cpus).
resources.memory_mbwhole number1536at least 512RAM limit per Android in MiB.
resources.memory_swap_mbwhole number2560at least 0RAM + swap limit per Android in MiB. 0 = same as memory (no swap).
resources.memory_reservation_mbwhole number0at least 0Soft limit the kernel reclaims toward under pressure. 0 = none.
resources.cpu_pinningchoice"none"none, spread, performance, efficiencynone: let the scheduler decide. spread: pin each Android to its own cores, round-robin. performance / efficiency: pin to P-cores or E-cores on hybrid CPUs.
resources.cpu_shareswhole number0at least 0Relative CPU weight under contention. 0 = Docker default (1024).
resources.pids_limitwhole number4096at least 256Maximum processes and threads inside one Android.
resources.shm_mbwhole number64at least 16/dev/shm size in MiB.
resources.oom_score_adjwhole number0-1000 to 1000Bias for the kernel's out-of-memory killer; raise it to sacrifice Androids before the desktop.

gpu#

KeyTypeDefaultAllowedDescription
gpu.modechoice"auto"auto, software, igpu, dgpu, intel, amd, nvidiaHow Androids draw (default for the farm; each Android can override it). auto: best available. software: CPU only. igpu: the integrated GPU. dgpu: a discrete card of any vendor. intel / amd: that vendor's GPU through Mesa. nvidia: Vulkan relayed to the card (needs the Venus runtime).
gpu.preferchoice"dgpu"dgpu, igpuWhich kind auto tries first when the machine has both. igpu keeps the discrete card free for other work and draws the least power.
gpu.render_nodetext""Force one /dev/dri/renderD* node for intel/amd/nvidia. Empty = detect by driver.
gpu.assignmentchoice"round-robin"first, round-robin, least-instancesWith several usable GPUs of the chosen kind: which one a new Android gets.
gpu.clear_shader_cache_on_changetrue / falsetrueDelete apps' compiled-shader caches when an Android's renderer changes. Stale caches crash apps at start.

gpu.nvidia#

KeyTypeDefaultAllowedDescription
gpu.nvidia.runtime_dirtext""Folder holding the Venus runtime for Android (vulkan.virtio.so, gralloc.venus.so, gralloc.venus.x86.so, gpu_config.sh). Empty = <home>/overlays/venus.
gpu.nvidia.relay_dirtext""Install prefix of the patched virgl_test_server relay (contains bin/ and lib/). Empty = <home>/overlays/venus-relay. The daemon runs the relay from here: set in config.yaml or with sf config set, not through the API.
gpu.nvidia.socket_dirtext"/run/superfarm/venus"Where relay sockets live. One socket per NVIDIA card.
gpu.nvidia.icdtext"/usr/share/vulkan/icd.d/nvidia_icd.json"Vulkan ICD manifest of the NVIDIA driver on the host. It names the driver library the relay loads: set in config.yaml or with sf config set, not through the API.
gpu.nvidia.debugtrue / falsefalseVerbose driver logging inside Android (mesa.log.level=debug). Costs CPU; leave off.

network#

KeyTypeDefaultAllowedDescription
network.modechoice"direct"direct, vpn, gateway, proxy, nonedirect: Androids use the host's connection. vpn: one tunnel per Android. gateway: Androids share a named VPN gateway. proxy: Android's global HTTP proxy. none: no network at all.
network.gatewaytext""Gateway name used when mode is gateway.
network.proxytext""host:port used when mode is proxy.
network.killswitchtrue / falsetrueBlock all traffic that would leave outside the tunnel. On from the first packet, before the VPN connects.
network.allow_lantrue / falsefalseLet Androids reach private (RFC 1918) addresses on your LAN.
network.dnslist of text["1.1.1.1", "8.8.8.8"]DNS servers Androids use.
network.dns_leak_protectiontrue / falsetrueWith a VPN: resolve the VPN server names once at start, then allow DNS only through the tunnel.
network.ipv6true / falsefalseLeave IPv6 enabled inside Androids. Off by default: most VPN profiles do not carry IPv6, so it would bypass the tunnel.
network.adb_bindtext"127.0.0.1"Host address ADB ports are published on. ADB has no authentication; do not expose it.
network.adb_port_basewhole number55551024 to 60000Android N is reachable on adb_port_base + N.
network.gatewaysmap of name to settingssee network.gateways.<name>Named shared VPN gateways.

network.vpn#

KeyTypeDefaultAllowedDescription
network.vpn.providerchoice"openvpn"openvpn, wireguardVPN software for per-instance tunnels.
network.vpn.selectionchoice"round-robin"fixed, random, round-robin, uniqueHow an Android without a pinned profile gets one. unique: never share a profile between two running Androids (fails when profiles run out).
network.vpn.profiletext""Profile file name used when selection is fixed.
network.vpn.credentialstext"default"Name of the credentials entry in secrets/ (username + password for OpenVPN auth-user-pass). Managed with sf vpn credentials.
network.vpn.ping_secondswhole number10at least 1OpenVPN keepalive interval.
network.vpn.ping_restart_secondswhole number120at least 10Tunnel is restarted after this long without a reply.
network.vpn.connect_retry_maxwhole number5at least 1Connection attempts per server before giving up (the container then restarts).
network.vpn.extra_argslist of text[]Extra arguments passed to openvpn.
network.vpn.public_ip_urltext"https://api.ipify.org"Service used to read an Android's public address for display and leak checks.

network.gateways.<name>#

One group per entry of network.gateways; <name> is the entry's name. No entries by default.

KeyTypeDefaultAllowedDescription
network.gateways.<name>.providerchoice"openvpn"openvpn, wireguardVPN software this gateway runs.
network.gateways.<name>.profiletextrequiredVPN profile this gateway connects with.
network.gateways.<name>.credentialstext"default"Name of the credentials entry in secrets/ this gateway logs in with.
network.gateways.<name>.subnettext""Private /24 for Androids behind this gateway. Empty = taken from docker.subnet_pool.

device#

KeyTypeDefaultAllowedDescription
device.profiletext"random"Device model an Android reports: a preset name (the Create form in the panel lists them), random, or none to keep the image's own identity.
device.randomize_android_idtrue / falsetrueGive each new Android its own random android_id.
device.localetext"en-US"System language.
device.timezonetext""Time zone as an Olson name, e.g. Europe/Berlin. Empty = the host's zone.
device.geotext""Mock location as lat,lon, e.g. 52.52,13.405. Empty = none.
device.extra_propsmap of text to text{}Extra build properties set at boot (ro.product.* and friends).

performance#

KeyTypeDefaultAllowedDescription
performance.trim_packagestrue / falsetrueDisable system apps the farm never needs after first boot (list per Android version in data/trim/). Saves about 170 MB per Android.
performance.trim_extralist of text[]More package names to disable.
performance.trim_keeplist of text[]Package names never to disable, even if listed.
performance.disable_animationstrue / falsetrueSet Android's animation scales to 0. Fewer frames drawn, faster automation.
performance.start_concurrencywhole number41 to 64Androids booted at the same time during bulk start. Booting is the heaviest moment.
performance.start_stagger_secondsnumber1.0at least 0Pause between launches inside a bulk start.
performance.boot_timeoutwhole number180at least 30Seconds to wait for sys.boot_completed before a start is reported failed.
performance.memory_guard_mbwhole number1500at least 0Free-RAM threshold of the capacity guard: starting another Android when less than this much RAM is available counts as over capacity. 0 = no guard.
performance.enforce_capacitytrue / falsefalseWhat happens when a start goes over the capacity estimate shown in System Info. false: warn and let the operator run more anyway. true: refuse unless the start is explicitly forced.
performance.reserve_host_mbwhole number3072at least 0RAM the capacity estimate leaves for the host itself (desktop, Docker, the panel).
performance.ksmtrue / falsefalseEnable kernel same-page merging at host setup. Identical pages across Androids are stored once. Experimental.
performance.swappinesswhole number-1-1 to 200vm.swappiness to set at host setup. -1 = leave alone.
performance.restart_policychoice"no"no, on-failure, unless-stoppedDocker restart policy for Androids. no means nothing starts by itself after a reboot.
performance.autostarttext""Selector of Androids to start when the daemon starts (e.g. tag:always, 1-10, all). Empty = none.

performance.zram#

KeyTypeDefaultAllowedDescription
performance.zram.enabledtrue / falsefalseCreate a compressed RAM swap device at host setup. Raises how many Androids fit; costs some CPU under pressure.
performance.zram.size_percentwhole number505 to 200zram size as a percentage of RAM.
performance.zram.algorithmtext"zstd"Compression algorithm.

stream#

KeyTypeDefaultAllowedDescription
stream.thumbnail_fpsnumber1.0more than 0, at most 10Refresh rate of the grid's thumbnails.
stream.thumbnail_widthwhole number24080 to 1080Thumbnail width in pixels.
stream.live_fpsnumber8.0more than 0, at most 30Refresh rate of the focused live view (screenshot transport).
stream.jpeg_qualitywhole number6010 to 95JPEG quality of streamed frames.
stream.max_viewerswhole number64at least 1Simultaneous live views served.
stream.scrcpy_bitratetext"2M"Bitrate of native scrcpy windows.
stream.scrcpy_max_sizewhole number720at least 0Longest side of native scrcpy windows. 0 = full size.
stream.scrcpy_max_fpswhole number20at least 1Frame rate cap of native scrcpy windows.
stream.scrcpy_extra_argslist of text[]Extra arguments for native scrcpy windows.
stream.h264choice"auto"auto, on, offExperimental H.264 live view in the browser (the device's own video stream, relayed from scrcpy-server; uses scrcpy_bitrate / scrcpy_max_size / scrcpy_max_fps). auto: offered when the installed scrcpy version is one this release was tested with. on: also with untested versions. off: never.

media#

media.camera#

KeyTypeDefaultAllowedDescription
media.camera.enabledtrue / falsefalseGive Androids a virtual front camera and microphone fed from a video file or an RTMP stream. Needs the v4l2loopback module and the camera overlay.
media.camera.slotswhole number320 to 240Virtual camera devices created at host setup (one per Android that will use a camera).
media.camera.device_basewhole number10010 to 200Android N's camera is /dev/video(device_base + N) on the host, for N from 1 to slots (the devices host setup creates). An Android with any other id is given a free device from that pool.
media.camera.widthwhole number640Sensor frame width (landscape).
media.camera.heightwhole number360Sensor frame height.
media.camera.fpswhole number205 to 60Frames per second of the camera feed.
media.camera.qualitywhole number52 to 31MJPEG quantiser; lower is better and larger.
media.camera.mirrortrue / falsetrueFlip the picture horizontally. On by default: apps mirror a front camera, and in a live broadcast test viewers (and the broadcaster's preview) saw the source the right way round only with this on. Turn it off if text in your source appears reversed.
media.camera.rtmp_bindtext"127.0.0.1"Address the RTMP ingest listens on for live sources (OBS). The ingest has no authentication (the stream key is not checked): whatever can reach this address becomes the camera picture and microphone of the Androids. Keep 127.0.0.1; for OBS on another PC use an SSH tunnel. Another address is refused unless rtmp_allow_remote is on.
media.camera.rtmp_allow_remotetrue / falsefalseAccept an rtmp_bind that is not a loopback address. Only on a network where every machine is trusted: the ingest cannot tell your OBS from anyone else's.
media.camera.rtmp_portwhole number19351024 to 65535Port the RTMP ingest listens on.
media.camera.hal_overlaytext""Folder with the camera/microphone HAL files copied into /vendor. Empty = <home>/overlays/camera/<android version>.

media.audio#

KeyTypeDefaultAllowedDescription
media.audio.listen_buffer_mswhole number20020 to 2000Buffer of the listen to one device stream. Larger = fewer stutters, more delay.
media.audio.listen_volumewhole number150 to 15Android media volume applied while listening (capture is scaled by it).

automation#

KeyTypeDefaultAllowedDescription
automation.max_parallelwhole number81 to 256Devices one run works on at the same time; the rest queue.
automation.hierarchy_readerchoice"auto"auto, uiautomator, u2How the UI tree is read. uiautomator: built in, waits for the screen to go idle. u2: persistent reader that never waits (needs the u2 extra). auto: u2 when installed.
automation.read_timeoutwhole number30at least 5Hard limit for one UI-tree read.
automation.step_timeoutwhole number20at least 1Seconds a step may wait for its element before recovery starts.
automation.run_timeoutwhole number900at least 10Hard limit for one device's whole run.
automation.match_thresholdnumber3.0at least 0.5Score an on-screen element needs to be accepted as a step's target (id 5, text 4, description 3, path 3, class 0.5, position up to 1).
automation.settle_secondsnumber0.6at least 0Pause after each action before the screen is read again.
automation.human_jittertrue / falsetrueVary tap positions and pauses slightly, as a person does.
automation.learn_fixestrue / falsetrueRemember recoveries (keyed by app and screen) and replay them without asking the model again.
automation.stop_on_unknown_screentrue / falsetrueA device that meets a screen no rule covers stops with needs attention instead of guessing. The other devices carry on.
automation.forbidden_field_wordslist of text["password", "passwd", "pin", "otp", "2fa", "seed", "card", "cvv", "ssn"]Values whose name contains one of these are never written to disk or sent to a model.

automation.llm#

KeyTypeDefaultAllowedDescription
automation.llm.providerchoice"none"none, ollama, openaiModel used to describe trained steps and to recover a stuck run. none: automation is fully deterministic. openai: any OpenAI-compatible endpoint.
automation.llm.base_urltext"http://127.0.0.1:11434"Model server address.
automation.llm.modeltext""Model name. Empty = first one the server lists.
automation.llm.api_key_secrettext"llm"Name of the secret holding the API key (openai provider).
automation.llm.timeoutwhole number120at least 5Seconds to wait for one answer from the model server.
automation.llm.temperaturenumber0.10 to 2Sampling temperature sent to the model. Low values keep answers repeatable.
automation.llm.num_ctxwhole number8192at least 1024Context window in tokens requested from the model (ollama provider).

backup#

KeyTypeDefaultAllowedDescription
backup.dirtext""Where backups go. Empty = <home>/backups.
backup.compressionchoice"zstd"zstd, gzip, noneHow the data archive of a backup is compressed. none: a plain tar.
backup.levelwhole number31 to 19Compression level: 1 is fastest, higher is smaller and slower. gzip stops at 9.
backup.keepwhole number7at least 0Backups kept per Android by sf backup prune. 0 = keep all.
backup.stop_for_backuptrue / falsetrueStop a running Android while its data is copied, for a consistent snapshot, then start it again.
backup.auto_before_riskytrue / falsetrueTake a backup automatically before an operation that can lose data (delete, forced Android version change, restore over an existing Android). If that backup fails, the operation is refused unless forced.
backup.scheduletext""Recurring backups as a cron line minute hour day-of-month month day-of-week (e.g. 0 4 * * * = every day at 04:00). Empty = off.
backup.schedule_selectortext"all"Which Androids the scheduled backup covers.
backup.include_secretstrue / falsefalseInclude VPN credentials and tokens in farm backups. Off by default so a backup can be stored or shared without leaking them.

control#

KeyTypeDefaultAllowedDescription
control.concurrencywhole number81 to 256Androids worked on at the same time by app, file, shell and device-setting operations; the rest queue.
control.install_timeoutwhole number900at least 30Seconds one Android may take to install an app before that device is reported failed.
control.shell_timeoutwhole number601 to 3600Default time limit for one shell command on one Android.
control.shell_output_kbwhole number2561 to 16384Output kept per Android from one shell command; a command that prints more is stopped.
control.shell_denylisttrue / falsetrueRefuse obviously destructive shell commands (wipes, power-off, firewall changes that would open the VPN kill switch) unless the caller forces them.
control.max_app_mbwhole number8192at least 16Largest app (all split APKs and OBB files together, unpacked) the app library accepts.
control.max_transfer_mbwhole number2048at least 1Largest single file pushed to or pulled from an Android.

logging#

KeyTypeDefaultAllowedDescription
logging.levelchoice"info"debug, info, warning, errorLeast severe kind of message the daemon writes to its log.
logging.max_mbwhole number20at least 1Size at which a log file rotates.
logging.keepwhole number5at least 1Rotated files kept.

ai#

KeyTypeDefaultAllowedDescription
ai.policychoice"ask"read-only, ask, allowWhat AI assistants may do through the farm's tools. read-only: look, never change anything. ask: actions that change the farm wait for the operator's Yes or No (see ask_for). allow: every tool that is not denied runs without asking.
ai.ask_forlist of choice["change", "destructive"]Under policy ask: which kinds of action need the operator's approval. change: start, stop, install, tap, type and the like. destructive: delete, uninstall, clear data, restore, shell. Remove change to be asked only before destructive actions.
ai.approval_timeout_secondswhole number3005 to 3600How long an approval request waits for the operator. After that it expires and the assistant is told that nobody answered; nothing runs.
ai.auto_approvelist of text[]Tool names that never need approval under policy ask (e.g. tap, type_text). sf mcp tools lists the names.
ai.denylist of text["run_shell"]Tool names AI assistants can never use, whatever the policy. sf mcp tools lists the names.
ai.max_screenshot_pxwhole number768128 to 2048Longest side, in pixels, of a screenshot handed to an assistant. Larger pictures cost the model more and rarely help.
ai.modelsmap of name to settingssee ai.models.<name>Named model profiles the Assistant and the Pilot can use: profile name -> where the model lives and what it can do. automation is reserved for the model set under automation.llm.
ai.default_modeltext""up to 32 charactersProfile used when none is named. Empty = the only profile there is.

ai.mcp#

KeyTypeDefaultAllowedDescription
ai.mcp.enabledtrue / falsetrueLet MCP clients (desktop assistants, IDE agents) connect through sf mcp. Off: sf mcp refuses to start. It needs the farm's API token either way, so this is a switch for the operator, not a security boundary.
ai.mcp.http_portwhole number77111024 to 65535Port sf mcp --http listens on (loopback only, bearer token required) when no --port is given.
ai.mcp.elicitationtrue / falsefalseWhen an approval is needed and the MCP client can ask its user questions (MCP elicitation), also show the Yes/No there. Off by default: a client may answer such a question by itself without showing it to anyone, and the decision is meant to be a person's. The request is in the farm's own queue either way and the first answer counts.

ai.models.<name>#

One group per entry of ai.models; <name> is the entry's name. No entries by default.

KeyTypeDefaultAllowedDescription
ai.models.<name>.providerchoicerequiredollama, openaiWire format of the model server. ollama: an Ollama server's own API. openai: any server that speaks the OpenAI chat-completions API (most hosted services and local servers do).
ai.models.<name>.base_urltext"http://127.0.0.1:11434"Address of the model server. For openai, with or without a trailing /v1.
ai.models.<name>.modeltext""up to 200 charactersModel name as the server knows it. Empty = the first one the server lists.
ai.models.<name>.api_key_secrettext""matches ^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$|^$Name of the secret that holds the API key (a file in <home>/secrets, written with sf ai models key <profile>). Never the key itself. Empty = the server needs no key.
ai.models.<name>.toolstrue / falsetrueThe model can call tools (function calling). The Assistant needs it. Off: the Pilot asks for plain JSON instead.
ai.models.<name>.visiontrue / falsefalseThe model can read pictures. On: the Pilot also sends a downscaled screenshot with every step.
ai.models.<name>.timeoutwhole number1205 to 1800Seconds to wait for one answer.
ai.models.<name>.temperatureany0.1Sampling temperature. Low values keep answers repeatable. Leave it out (null) for a server that does not accept the setting.
ai.models.<name>.num_ctxwhole number16384at least 1024Context window in tokens requested from the model (ollama provider). The Assistant's tool list alone is about 6,500 tokens, so less than 12288 leaves it little room; the Pilot needs about 2,000 per step.
ai.models.<name>.max_tokenswhole number10240 to 65536Longest answer in tokens. 0 = do not send a limit.
ai.models.<name>.headersmap of text to textExtra HTTP headers sent with every request (a gateway's routing header, for example). Not for keys: those go in api_key_secret.
ai.models.<name>.extramapExtra fields added to every request body, for options a particular server needs (e.g. think: false, keep_alive: 10m).

ai.assistant#

KeyTypeDefaultAllowedDescription
ai.assistant.max_stepswhole number121 to 100Model turns the Assistant may take to answer one message. It stops and says so when the limit is reached.
ai.assistant.max_tool_callswhole number241 to 500Tool calls the Assistant may make to answer one message.
ai.assistant.max_secondswhole number60010 to 7200Wall-clock limit for answering one message. Time spent waiting for the operator's Yes or No does not count.
ai.assistant.max_result_charswhole number6000500 to 100000Longest tool result handed back to the model; longer ones are cut and marked as cut.
ai.assistant.keep_conversationswhole number201 to 500Conversations kept (in memory and as transcripts under <home>/automation/assistant). The oldest finished ones are removed first.

ai.pilot#

KeyTypeDefaultAllowedDescription
ai.pilot.max_parallelwhole number21 to 64Androids one Pilot session drives at the same time; the rest queue. Each one asks the model once per step, so this is also how many requests the model server sees at once.
ai.pilot.max_stepswhole number251 to 200Actions one Pilot run may take on one Android before it stops.
ai.pilot.max_minutesnumber10.0more than 0, at most 240Wall-clock limit for one Pilot run on one Android.
ai.pilot.max_model_callswhole number601 to 1000Model requests one Pilot run on one Android may make (rejected answers count too).
ai.pilot.max_no_progresswhole number31 to 20Actions in a row that lead to no new screen (nothing changes, or the run is back on a screen it has already seen) before the run stops.
ai.pilot.max_elementswhole number6010 to 200Screen elements described to the model per step.
ai.pilot.replychoice"auto"auto, tools, jsonHow the Pilot gets its one action per step from the model. json: one JSON object held to a schema by the server. tools: a tool call. auto: json on an ollama server (it enforces the schema, so even a small model cannot answer in the wrong form), tools elsewhere when the profile has tools, otherwise json.
ai.pilot.restart_apptrue / falsetrueForce-stop the app before each Pilot run, so every recording starts from the app's first screen.
ai.pilot.allowed_packageslist of text["com.android.permissioncontroller", "com.google.android.permissioncontroller"]Packages a Pilot run may pass through besides the app it was started for (permission dialogs by default). Landing anywhere else ends the run.

This page is docs/CONFIGURATION.md in the repository.