Getting started
Platforms: nautilus is one static binary with no runtime dependencies.
Every release ships builds for ### macOS (Apple Silicon and Intel),
### Linux (x86-64 and arm64), and ### Windows (x86-64 and arm64); the
controller it builds runs anywhere the same binary does, from a laptop to a
Raspberry Pi to a Kubernetes pod. VS Code is the editor experience on all
three. The
release page
has the archives and a checksums.txt.
1. Install the CLI
Section titled “1. Install the CLI”macOS — pick arm64 for Apple Silicon, amd64 for Intel:
Section titled “macOS — pick arm64 for Apple Silicon, amd64 for Intel:”v=$(curl -fsSL https://api.github.com/repos/joyautomation/nautilus/releases/latest | grep -m1 '"tag_name"' | cut -d'"' -f4)curl -fsSL "https://github.com/joyautomation/nautilus/releases/download/$v/nautilus_${v#v}_darwin_arm64.tar.gz" | tar xz nautilussudo mv nautilus /usr/local/bin/Downloading with curl skips Gatekeeper’s quarantine. If you fetched the
archive in a browser instead and macOS refuses to open the binary, clear the
flag once: xattr -d com.apple.quarantine /usr/local/bin/nautilus.
Linux — amd64 or arm64:
Section titled “Linux — amd64 or arm64:”v=$(curl -fsSL https://api.github.com/repos/joyautomation/nautilus/releases/latest | grep -m1 '"tag_name"' | cut -d'"' -f4)curl -fsSL "https://github.com/joyautomation/nautilus/releases/download/$v/nautilus_${v#v}_linux_amd64.tar.gz" | tar xz nautilussudo install nautilus /usr/local/bin/Windows — PowerShell, amd64 or arm64:
Section titled “Windows — PowerShell, amd64 or arm64:”$v = (Invoke-RestMethod https://api.github.com/repos/joyautomation/nautilus/releases/latest).tag_nameInvoke-WebRequest "https://github.com/joyautomation/nautilus/releases/download/$v/nautilus_$($v.TrimStart('v'))_windows_amd64.zip" -OutFile nautilus.zipExpand-Archive nautilus.zip -DestinationPath "$env:LOCALAPPDATA\nautilus" -Force[Environment]::SetEnvironmentVariable("Path", "$env:LOCALAPPDATA\nautilus;" + [Environment]::GetEnvironmentVariable("Path", "User"), "User")Open a new terminal afterwards so the Path change is picked up.
Any OS, with Go 1.24+ installed
Section titled “Any OS, with Go 1.24+ installed”go install github.com/joyautomation/nautilus/cmd/nautilus@latestThis puts the binary in $(go env GOPATH)/bin, which needs to be on your
PATH. Whichever route you took, nautilus version should now answer.
The one binary is the whole toolchain: nautilus new (scaffold a
project), run, test, check (the CI gate: compiles every program and
cross-checks it against the manifest), build, pull (bring a controller’s
running program back into the repo), lsp (the language server the VS Code
extension uses), and the eip, modbus, sparkplug, and historian tools.
2. Scaffold a project
Section titled “2. Scaffold a project”nautilus new my-plant # the tour: 3 tasks, 3 IEC languages, simulated plantnautilus new my-plant --template minimal # one task, one program, one testnautilus new my-plant --template sdk # Go project, for a custom field busnautilus new my-plant --template sdk-demo # Go project with plant physics in GoRun it bare for the interactive form — it asks for the template, the program language, and the features you want.
A nautilus project is your logic and a manifest. Run, test, and ship it with the CLI alone, no toolchain:
cd my-plantnautilus run # scan loop + dashboard + tag API on http://localhost:8080nautilus test # acceptance tests, in virtual timenautilus check # compile (the CI gate)nautilus build # emit ./my-plant — a self-contained controller binarynautilus.yaml declares the tasks (one program file each, any language,
own scan rates), the tags by role, the server, and the field driver.
nautilus build emits one deployable binary, with no Go toolchain anywhere.
*_test.yaml holds the acceptance tests, and they run against a virtual
clock, so a ten-second on-delay or a loop’s settling time is asserted
exactly, deterministically, in milliseconds:
- name: low-temp alarm waits its full 10 s suspend: [sim] # freeze the plant; drive the value directly given: { TempC: 45.0 } steps: - advance: 9.5s expect: { TempLowAlm: false } # the TON has not elapsed - advance: 1s expect: { TempLowAlm: true } # ... and now it hasSee Testing for the whole format.
Go is the SDK. Reach for --template sdk when you need a custom field
bus or richer simulation physics. That form is the same
runtime with the manifest written as code, and it’s an ordinary Go
program:
cd my-plantgo mod tidy # resolves github.com/joyautomation/nautilus from the proxygo run . # scan loop + tag API on http://localhost:8080go test ./... # the program's acceptance tests, on the same virtual clockOpen http://localhost:8080 for the built-in live dashboard, or
GET /api/state for the raw tag snapshot. Setpoints are click-to-set right in
the tag table — click a value and type, or flip a BOOL with its toggle — so you
can drive the loop before there is an HMI. Inputs and outputs stay read-only:
the driver rewrites an input before every scan and the logic rewrites an output
after, so an edit there would be discarded within a scan.
3. Develop in VS Code
Section titled “3. Develop in VS Code”Install nautilus IEC 61131-3 from the VS Code Marketplace or Open VSX — currently on the pre-release channel, so use Install Pre-Release Version. With your project open and the controller running you get compile diagnostics as you type, go-to-definition, hover, completion, and live tag values next to identifiers in your program.
On macOS, VS Code launched from the Dock or Spotlight gets the login PATH,
not your shell’s, so it may not find nautilus even though your terminal
does. If the extension reports it could not start the language server, set
nautilus.cliPath to the full path (which nautilus), or launch VS Code
from a terminal with code ..
4. Make it yours
Section titled “4. Make it yours”- Write control logic in
program.st, or in.ld,.fbd, or.sfc; the graphical languages open in full diagram editors in VS Code. - Assert on it in
*_test.yaml, and keep asserting as you tune. The fixture comes fromnautilus.yaml, so a retuned gain can’t drift away from what the tests verify. - Swap the simulated plant for real field I/O when you have hardware:
delete the
simtask and pointdriver:at the bus. The control logic doesn’t change — it reads the same tags either way. - Add an HMI with the SvelteKit component kit: faceplates, trends, and an SSE realtime client.
- Ship it as one binary. The scaffolded CI gates on
nautilus check,nautilus test, andnautilus build. Add--deploytonautilus newfor a Dockerfile, a redundant-pair Kubernetes manifest, and the workflow that ships a merged commit to the controller.
- Online edits — change logic on a running controller from VS Code, and pull field edits back into git.
- Testing — virtual time, and the
*_test.yamlformat for asserting on timers and loop responses. - The tag model — how tags come to exist, which role fits which job, and the one rule that bites.
- Language reference — evaluation semantics and every built-in operator, function, and function block.