Forrest runs tasks using a GUI. This weirdware keeps track of tasks, such as the ssh tunnels you have running and any scripts that may be running locally or on a remote computer over ssh.
Each task is run on its own thread and can be started and stopped using the interface. CPU usage should be minimal when running, adhoc tests put CPU usage at around 0.1%. RAM usage sits around 160 MB.
Every task has exactly one of three behaviors, which decides what its status means:
- One-Shot - runs to completion, then finishes; you care about its output and whether it succeeded or failed. Any saved runnable script (log into AWS, rebuild the DB, grab a cookie).
- Long-Running - starts and stays up until you stop it by hand; status is alive vs dead. Can opt in to Keep-Alive, which relaunches the job if it dies on its own, backing off and giving up if it keeps flapping.
- Monitor - watches an external thing on a timer and reports up vs down. Report-only: no notifications, no restarts. You start and stop the watching, not the watched thing.
Two of these ship as fill-in-the-blanks presets, so you supply a few fields instead of a whole script:
- Port Forward - a Long-Running preset: an SSH tunnel binding a local port to a remote port, with a companion "kill port" action that frees the local port if something is holding it.
- Docker Monitor - a Monitor preset that watches a Docker container.
Remote tasks are reached over SSH by destination - normally an ~/.ssh/config Host alias
(e.g. dev), with an optional explicit key path for hosts not in the config. ~/.ssh/config owns
hostnames, users, and keys; Forrest is a task board, not an SSH connection manager (see
docs/adr/0001-ssh-config-destinations.md).
Configuration is stored as JSON at $HOME/forrest_config.json, pre-populated with one example task
on first launch. Add, edit, and remove tasks from the GUI - changes persist immediately, no restart
needed. If the file ever gets corrupted, Forrest backs it up and starts empty so you can recover it;
deleting the file re-creates the default on next launch.
- One-Shot script tasks
- Long-Running job tasks (with opt-in Keep-Alive / flap guard)
- Monitor tasks (process PID)
- Docker Monitor tasks
- Port Forward tasks (with local port kill)
- run tasks on the local computer
- run tasks on a remote host over SSH (via
~/.ssh/configdestination) - start and stop tasks from the GUI
- view task logs from the GUI
- live log view that updates automatically while a task runs
- add tasks from the GUI
- remove tasks from the GUI
- edit tasks from the GUI
- behavior-specific status (running / succeeded / failed, alive / dead, up / down)
- set a per-task remote SSH destination from the GUI (an
~/.ssh/configalias oruser@host; not a full ssh-config manager, by design) - task config stored as a portable JSON file at
$HOME/forrest_config.json(copy it to back up or move) - import/export task configurations from the GUI (currently: copy the JSON file by hand)
This project is a Compose Multiplatform desktop (JVM) app.
Toolchain:
- Kotlin 2.0.21, Compose Multiplatform 1.7.3
- Gradle 8.10.2 with a daemon JVM toolchain pinned to JDK 21 (
gradle/gradle-daemon-jvm.properties) - App/compile target JDK 17 (
jvmToolchain(17))
Open the project in Android Studio or IntelliJ and use the bundled run configurations:
- Run JVM Desktop — launches the app (
gradlew run) - Package Dmg — builds the native macOS
.dmg(gradlew packageDmg)
Gradle 8.10 does not auto-download the daemon JDK, so it must find a JDK 21 already installed. Android Studio's
bundled JBR 21 satisfies this in-IDE. For command-line builds, install a JDK 21 (e.g. sdk install java 21-tem) or
point Gradle at an existing one in ~/.gradle/gradle.properties:
org.gradle.java.installations.paths=/path/to/jdk-21