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 defaultKeys 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#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
version | whole number | 1 | Config format version. | |
farm_name | text | "Super Farm" | up to 60 characters | Name shown in the panel. |
server#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
server.host | text | "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.port | whole number | 7700 | 1 to 65535 | Port of the panel and API. |
server.auth_required | true / false | true | Require 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_cert | text | "" | PEM certificate file. With tls_key, the server speaks HTTPS. | |
server.tls_key | text | "" | PEM private key file for tls_cert. | |
server.session_hours | whole number | 12 | 1 to 720 | How long a browser login stays valid. |
server.max_upload_mb | whole number | 4096 | at least 1 | Largest file (APK, video) the panel accepts. |
server.allowed_origins | list of text | [] | Extra browser origins allowed to call the API (CORS). Empty = same origin only. | |
server.audit_log | true / false | true | Record 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_minute | whole number | 10 | at least 1 | Wrong 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_proxies | list 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#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
docker.binary | text | "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_prefix | text | "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_timeout | whole number | 10 | 0 to 300 | Seconds an Android gets to shut down before it is killed. |
docker.subnet_pool | text | "10.213.0.0/16" | Private range that shared-VPN gateway networks are carved from (one /24 each). | |
docker.log_max_size | text | "10m" | Per-container log size cap (json-file driver). | |
docker.log_max_files | whole number | 2 | at least 1 | Rotated log files kept per container. |
android#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
android.default_version | text | "13" | Android version given to new instances. | |
android.images | map of name to settings | see android.images.<name> | Android version -> image. Add an entry to support another version or a custom build. | |
android.width | whole number | 480 | 240 to 3840 | Screen width in pixels. |
android.height | whole number | 854 | 320 to 3840 | Screen height in pixels. |
android.dpi | whole number | 160 | 80 to 640 | Screen density. |
android.fps | whole number | 20 | 5 to 120 | Frames per second Android draws at. The single biggest CPU lever after the GPU mode. |
android.extra_boot_args | list of text | [] | Extra androidboot.* / ro.* arguments appended to every Android's command line. | |
android.extra_props | map 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.
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
android.images.<name>.base | text | required | Upstream image this Android version starts from. | |
android.images.<name>.tag | text | "" | Use this exact local image instead of building one from base (for an image you built or imported yourself). | |
android.images.<name>.arm_translation | true / false | true | Whether the image is expected to run ARM-only apps (native bridge present). | |
android.images.<name>.fps_property | text | "" | 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#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
resources.cpus | number | 2.0 | more than 0, at most 256 | CPU cores one Android may use (Docker --cpus). |
resources.memory_mb | whole number | 1536 | at least 512 | RAM limit per Android in MiB. |
resources.memory_swap_mb | whole number | 2560 | at least 0 | RAM + swap limit per Android in MiB. 0 = same as memory (no swap). |
resources.memory_reservation_mb | whole number | 0 | at least 0 | Soft limit the kernel reclaims toward under pressure. 0 = none. |
resources.cpu_pinning | choice | "none" | none, spread, performance, efficiency | none: 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_shares | whole number | 0 | at least 0 | Relative CPU weight under contention. 0 = Docker default (1024). |
resources.pids_limit | whole number | 4096 | at least 256 | Maximum processes and threads inside one Android. |
resources.shm_mb | whole number | 64 | at least 16 | /dev/shm size in MiB. |
resources.oom_score_adj | whole number | 0 | -1000 to 1000 | Bias for the kernel's out-of-memory killer; raise it to sacrifice Androids before the desktop. |
gpu#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
gpu.mode | choice | "auto" | auto, software, igpu, dgpu, intel, amd, nvidia | How 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.prefer | choice | "dgpu" | dgpu, igpu | Which 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_node | text | "" | Force one /dev/dri/renderD* node for intel/amd/nvidia. Empty = detect by driver. | |
gpu.assignment | choice | "round-robin" | first, round-robin, least-instances | With several usable GPUs of the chosen kind: which one a new Android gets. |
gpu.clear_shader_cache_on_change | true / false | true | Delete apps' compiled-shader caches when an Android's renderer changes. Stale caches crash apps at start. |
gpu.nvidia#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
gpu.nvidia.runtime_dir | text | "" | 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_dir | text | "" | 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_dir | text | "/run/superfarm/venus" | Where relay sockets live. One socket per NVIDIA card. | |
gpu.nvidia.icd | text | "/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.debug | true / false | false | Verbose driver logging inside Android (mesa.log.level=debug). Costs CPU; leave off. |
network#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
network.mode | choice | "direct" | direct, vpn, gateway, proxy, none | direct: 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.gateway | text | "" | Gateway name used when mode is gateway. | |
network.proxy | text | "" | host:port used when mode is proxy. | |
network.killswitch | true / false | true | Block all traffic that would leave outside the tunnel. On from the first packet, before the VPN connects. | |
network.allow_lan | true / false | false | Let Androids reach private (RFC 1918) addresses on your LAN. | |
network.dns | list of text | ["1.1.1.1", "8.8.8.8"] | DNS servers Androids use. | |
network.dns_leak_protection | true / false | true | With a VPN: resolve the VPN server names once at start, then allow DNS only through the tunnel. | |
network.ipv6 | true / false | false | Leave IPv6 enabled inside Androids. Off by default: most VPN profiles do not carry IPv6, so it would bypass the tunnel. | |
network.adb_bind | text | "127.0.0.1" | Host address ADB ports are published on. ADB has no authentication; do not expose it. | |
network.adb_port_base | whole number | 5555 | 1024 to 60000 | Android N is reachable on adb_port_base + N. |
network.gateways | map of name to settings | see network.gateways.<name> | Named shared VPN gateways. |
network.vpn#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
network.vpn.provider | choice | "openvpn" | openvpn, wireguard | VPN software for per-instance tunnels. |
network.vpn.selection | choice | "round-robin" | fixed, random, round-robin, unique | How an Android without a pinned profile gets one. unique: never share a profile between two running Androids (fails when profiles run out). |
network.vpn.profile | text | "" | Profile file name used when selection is fixed. | |
network.vpn.credentials | text | "default" | Name of the credentials entry in secrets/ (username + password for OpenVPN auth-user-pass). Managed with sf vpn credentials. | |
network.vpn.ping_seconds | whole number | 10 | at least 1 | OpenVPN keepalive interval. |
network.vpn.ping_restart_seconds | whole number | 120 | at least 10 | Tunnel is restarted after this long without a reply. |
network.vpn.connect_retry_max | whole number | 5 | at least 1 | Connection attempts per server before giving up (the container then restarts). |
network.vpn.extra_args | list of text | [] | Extra arguments passed to openvpn. | |
network.vpn.public_ip_url | text | "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.
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
network.gateways.<name>.provider | choice | "openvpn" | openvpn, wireguard | VPN software this gateway runs. |
network.gateways.<name>.profile | text | required | VPN profile this gateway connects with. | |
network.gateways.<name>.credentials | text | "default" | Name of the credentials entry in secrets/ this gateway logs in with. | |
network.gateways.<name>.subnet | text | "" | Private /24 for Androids behind this gateway. Empty = taken from docker.subnet_pool. |
device#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
device.profile | text | "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_id | true / false | true | Give each new Android its own random android_id. | |
device.locale | text | "en-US" | System language. | |
device.timezone | text | "" | Time zone as an Olson name, e.g. Europe/Berlin. Empty = the host's zone. | |
device.geo | text | "" | Mock location as lat,lon, e.g. 52.52,13.405. Empty = none. | |
device.extra_props | map of text to text | {} | Extra build properties set at boot (ro.product.* and friends). |
performance#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
performance.trim_packages | true / false | true | Disable system apps the farm never needs after first boot (list per Android version in data/trim/). Saves about 170 MB per Android. | |
performance.trim_extra | list of text | [] | More package names to disable. | |
performance.trim_keep | list of text | [] | Package names never to disable, even if listed. | |
performance.disable_animations | true / false | true | Set Android's animation scales to 0. Fewer frames drawn, faster automation. | |
performance.start_concurrency | whole number | 4 | 1 to 64 | Androids booted at the same time during bulk start. Booting is the heaviest moment. |
performance.start_stagger_seconds | number | 1.0 | at least 0 | Pause between launches inside a bulk start. |
performance.boot_timeout | whole number | 180 | at least 30 | Seconds to wait for sys.boot_completed before a start is reported failed. |
performance.memory_guard_mb | whole number | 1500 | at least 0 | Free-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_capacity | true / false | false | What 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_mb | whole number | 3072 | at least 0 | RAM the capacity estimate leaves for the host itself (desktop, Docker, the panel). |
performance.ksm | true / false | false | Enable kernel same-page merging at host setup. Identical pages across Androids are stored once. Experimental. | |
performance.swappiness | whole number | -1 | -1 to 200 | vm.swappiness to set at host setup. -1 = leave alone. |
performance.restart_policy | choice | "no" | no, on-failure, unless-stopped | Docker restart policy for Androids. no means nothing starts by itself after a reboot. |
performance.autostart | text | "" | Selector of Androids to start when the daemon starts (e.g. tag:always, 1-10, all). Empty = none. |
performance.zram#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
performance.zram.enabled | true / false | false | Create a compressed RAM swap device at host setup. Raises how many Androids fit; costs some CPU under pressure. | |
performance.zram.size_percent | whole number | 50 | 5 to 200 | zram size as a percentage of RAM. |
performance.zram.algorithm | text | "zstd" | Compression algorithm. |
stream#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
stream.thumbnail_fps | number | 1.0 | more than 0, at most 10 | Refresh rate of the grid's thumbnails. |
stream.thumbnail_width | whole number | 240 | 80 to 1080 | Thumbnail width in pixels. |
stream.live_fps | number | 8.0 | more than 0, at most 30 | Refresh rate of the focused live view (screenshot transport). |
stream.jpeg_quality | whole number | 60 | 10 to 95 | JPEG quality of streamed frames. |
stream.max_viewers | whole number | 64 | at least 1 | Simultaneous live views served. |
stream.scrcpy_bitrate | text | "2M" | Bitrate of native scrcpy windows. | |
stream.scrcpy_max_size | whole number | 720 | at least 0 | Longest side of native scrcpy windows. 0 = full size. |
stream.scrcpy_max_fps | whole number | 20 | at least 1 | Frame rate cap of native scrcpy windows. |
stream.scrcpy_extra_args | list of text | [] | Extra arguments for native scrcpy windows. | |
stream.h264 | choice | "auto" | auto, on, off | Experimental 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#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
media.camera.enabled | true / false | false | Give 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.slots | whole number | 32 | 0 to 240 | Virtual camera devices created at host setup (one per Android that will use a camera). |
media.camera.device_base | whole number | 100 | 10 to 200 | Android 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.width | whole number | 640 | Sensor frame width (landscape). | |
media.camera.height | whole number | 360 | Sensor frame height. | |
media.camera.fps | whole number | 20 | 5 to 60 | Frames per second of the camera feed. |
media.camera.quality | whole number | 5 | 2 to 31 | MJPEG quantiser; lower is better and larger. |
media.camera.mirror | true / false | true | Flip 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_bind | text | "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_remote | true / false | false | Accept 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_port | whole number | 1935 | 1024 to 65535 | Port the RTMP ingest listens on. |
media.camera.hal_overlay | text | "" | Folder with the camera/microphone HAL files copied into /vendor. Empty = <home>/overlays/camera/<android version>. |
media.audio#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
media.audio.listen_buffer_ms | whole number | 200 | 20 to 2000 | Buffer of the listen to one device stream. Larger = fewer stutters, more delay. |
media.audio.listen_volume | whole number | 15 | 0 to 15 | Android media volume applied while listening (capture is scaled by it). |
automation#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
automation.max_parallel | whole number | 8 | 1 to 256 | Devices one run works on at the same time; the rest queue. |
automation.hierarchy_reader | choice | "auto" | auto, uiautomator, u2 | How 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_timeout | whole number | 30 | at least 5 | Hard limit for one UI-tree read. |
automation.step_timeout | whole number | 20 | at least 1 | Seconds a step may wait for its element before recovery starts. |
automation.run_timeout | whole number | 900 | at least 10 | Hard limit for one device's whole run. |
automation.match_threshold | number | 3.0 | at least 0.5 | Score 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_seconds | number | 0.6 | at least 0 | Pause after each action before the screen is read again. |
automation.human_jitter | true / false | true | Vary tap positions and pauses slightly, as a person does. | |
automation.learn_fixes | true / false | true | Remember recoveries (keyed by app and screen) and replay them without asking the model again. | |
automation.stop_on_unknown_screen | true / false | true | A device that meets a screen no rule covers stops with needs attention instead of guessing. The other devices carry on. | |
automation.forbidden_field_words | list 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#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
automation.llm.provider | choice | "none" | none, ollama, openai | Model used to describe trained steps and to recover a stuck run. none: automation is fully deterministic. openai: any OpenAI-compatible endpoint. |
automation.llm.base_url | text | "http://127.0.0.1:11434" | Model server address. | |
automation.llm.model | text | "" | Model name. Empty = first one the server lists. | |
automation.llm.api_key_secret | text | "llm" | Name of the secret holding the API key (openai provider). | |
automation.llm.timeout | whole number | 120 | at least 5 | Seconds to wait for one answer from the model server. |
automation.llm.temperature | number | 0.1 | 0 to 2 | Sampling temperature sent to the model. Low values keep answers repeatable. |
automation.llm.num_ctx | whole number | 8192 | at least 1024 | Context window in tokens requested from the model (ollama provider). |
backup#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
backup.dir | text | "" | Where backups go. Empty = <home>/backups. | |
backup.compression | choice | "zstd" | zstd, gzip, none | How the data archive of a backup is compressed. none: a plain tar. |
backup.level | whole number | 3 | 1 to 19 | Compression level: 1 is fastest, higher is smaller and slower. gzip stops at 9. |
backup.keep | whole number | 7 | at least 0 | Backups kept per Android by sf backup prune. 0 = keep all. |
backup.stop_for_backup | true / false | true | Stop a running Android while its data is copied, for a consistent snapshot, then start it again. | |
backup.auto_before_risky | true / false | true | Take 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.schedule | text | "" | 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_selector | text | "all" | Which Androids the scheduled backup covers. | |
backup.include_secrets | true / false | false | Include VPN credentials and tokens in farm backups. Off by default so a backup can be stored or shared without leaking them. |
control#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
control.concurrency | whole number | 8 | 1 to 256 | Androids worked on at the same time by app, file, shell and device-setting operations; the rest queue. |
control.install_timeout | whole number | 900 | at least 30 | Seconds one Android may take to install an app before that device is reported failed. |
control.shell_timeout | whole number | 60 | 1 to 3600 | Default time limit for one shell command on one Android. |
control.shell_output_kb | whole number | 256 | 1 to 16384 | Output kept per Android from one shell command; a command that prints more is stopped. |
control.shell_denylist | true / false | true | Refuse obviously destructive shell commands (wipes, power-off, firewall changes that would open the VPN kill switch) unless the caller forces them. | |
control.max_app_mb | whole number | 8192 | at least 16 | Largest app (all split APKs and OBB files together, unpacked) the app library accepts. |
control.max_transfer_mb | whole number | 2048 | at least 1 | Largest single file pushed to or pulled from an Android. |
logging#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
logging.level | choice | "info" | debug, info, warning, error | Least severe kind of message the daemon writes to its log. |
logging.max_mb | whole number | 20 | at least 1 | Size at which a log file rotates. |
logging.keep | whole number | 5 | at least 1 | Rotated files kept. |
ai#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
ai.policy | choice | "ask" | read-only, ask, allow | What 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_for | list 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_seconds | whole number | 300 | 5 to 3600 | How long an approval request waits for the operator. After that it expires and the assistant is told that nobody answered; nothing runs. |
ai.auto_approve | list of text | [] | Tool names that never need approval under policy ask (e.g. tap, type_text). sf mcp tools lists the names. | |
ai.deny | list of text | ["run_shell"] | Tool names AI assistants can never use, whatever the policy. sf mcp tools lists the names. | |
ai.max_screenshot_px | whole number | 768 | 128 to 2048 | Longest side, in pixels, of a screenshot handed to an assistant. Larger pictures cost the model more and rarely help. |
ai.models | map of name to settings | see 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_model | text | "" | up to 32 characters | Profile used when none is named. Empty = the only profile there is. |
ai.mcp#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
ai.mcp.enabled | true / false | true | Let 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_port | whole number | 7711 | 1024 to 65535 | Port sf mcp --http listens on (loopback only, bearer token required) when no --port is given. |
ai.mcp.elicitation | true / false | false | When 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.
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
ai.models.<name>.provider | choice | required | ollama, openai | Wire 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_url | text | "http://127.0.0.1:11434" | Address of the model server. For openai, with or without a trailing /v1. | |
ai.models.<name>.model | text | "" | up to 200 characters | Model name as the server knows it. Empty = the first one the server lists. |
ai.models.<name>.api_key_secret | text | "" | 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>.tools | true / false | true | The model can call tools (function calling). The Assistant needs it. Off: the Pilot asks for plain JSON instead. | |
ai.models.<name>.vision | true / false | false | The model can read pictures. On: the Pilot also sends a downscaled screenshot with every step. | |
ai.models.<name>.timeout | whole number | 120 | 5 to 1800 | Seconds to wait for one answer. |
ai.models.<name>.temperature | any | 0.1 | Sampling temperature. Low values keep answers repeatable. Leave it out (null) for a server that does not accept the setting. | |
ai.models.<name>.num_ctx | whole number | 16384 | at least 1024 | Context 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_tokens | whole number | 1024 | 0 to 65536 | Longest answer in tokens. 0 = do not send a limit. |
ai.models.<name>.headers | map of text to text | Extra 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>.extra | map | Extra fields added to every request body, for options a particular server needs (e.g. think: false, keep_alive: 10m). |
ai.assistant#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
ai.assistant.max_steps | whole number | 12 | 1 to 100 | Model turns the Assistant may take to answer one message. It stops and says so when the limit is reached. |
ai.assistant.max_tool_calls | whole number | 24 | 1 to 500 | Tool calls the Assistant may make to answer one message. |
ai.assistant.max_seconds | whole number | 600 | 10 to 7200 | Wall-clock limit for answering one message. Time spent waiting for the operator's Yes or No does not count. |
ai.assistant.max_result_chars | whole number | 6000 | 500 to 100000 | Longest tool result handed back to the model; longer ones are cut and marked as cut. |
ai.assistant.keep_conversations | whole number | 20 | 1 to 500 | Conversations kept (in memory and as transcripts under <home>/automation/assistant). The oldest finished ones are removed first. |
ai.pilot#
| Key | Type | Default | Allowed | Description |
|---|---|---|---|---|
ai.pilot.max_parallel | whole number | 2 | 1 to 64 | Androids 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_steps | whole number | 25 | 1 to 200 | Actions one Pilot run may take on one Android before it stops. |
ai.pilot.max_minutes | number | 10.0 | more than 0, at most 240 | Wall-clock limit for one Pilot run on one Android. |
ai.pilot.max_model_calls | whole number | 60 | 1 to 1000 | Model requests one Pilot run on one Android may make (rejected answers count too). |
ai.pilot.max_no_progress | whole number | 3 | 1 to 20 | Actions 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_elements | whole number | 60 | 10 to 200 | Screen elements described to the model per step. |
ai.pilot.reply | choice | "auto" | auto, tools, json | How 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_app | true / false | true | Force-stop the app before each Pilot run, so every recording starts from the app's first screen. | |
ai.pilot.allowed_packages | list 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.