Code
Sylve is built with Go (backend) and SvelteKit in SPA mode (frontend), we’re API first so if you’re planning to work on the go side you don’t have to touch the web/ directory at all. For the best development experience, we recommend a FreeBSD -STABLE machine with pkgbase.
Install Go and Node.js On FreeBSD
Section titled “Install Go and Node.js On FreeBSD”Install required tooling with pkg:
pkg updatepkg install -y git-lite go node npmVerify your installation:
go versionnode -vnpm -vIf your local machine is not FreeBSD, use a remote FreeBSD host for development.
VS Code Remote - SSH works great for this workflow:
- Extension: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh
- Example setup article: https://hayzam.com/blog/02-linuxulator-is-awesome/
Clone The Repository
Section titled “Clone The Repository”git clone https://github.com/AlchemillaHQ/Sylvecd SylveInstall Live-Reload Tooling
Section titled “Install Live-Reload Tooling”Install air:
go install github.com/air-verse/air@latestUse A Single Development Entry Point
Section titled “Use A Single Development Entry Point”In config.json, set (along with other properties):
{ "proxyToVite": true}With proxyToVite enabled, the Go backend proxies frontend requests to Vite, so you do not need to work with two separate ports directly.
Run Backend And Frontend Together
Section titled “Run Backend And Frontend Together”Use tmux, zellij, or screen and split into two panes:
- In the repository root:
air .- In the
web/directory:
npm run devCode Style
Section titled “Code Style”Prefer simple, direct code over clever abstractions. A solution should be easy to read, trace, and change without requiring the reader to decode tricks or reconstruct hidden control flow.
Code should explain itself through precise names, small focused functions, straightforward data flow, and clear boundaries. We strongly dislike comments that merely restate what the code is doing; if a block needs that kind of explanation, simplify or rename it instead.
Use a comment only when the code cannot communicate the reason on its own; for example, to record an external constraint, a specific edge case, or the rationale behind a non-obvious compatibility decision. Comments should explain why, not narrate what.
If you use an LLM or other AI-assisted tooling, read and follow Sylve’s LLM / AI Contribution Policy before submitting your work. You remain responsible for understanding and testing everything you contribute, and contribution discussions must follow the policy as well.
Commit Messages
Section titled “Commit Messages”Use a short, lowercase scope followed by a concise description:
area: summaryarea: subsystem: summaryChoose the part of the repository affected by the change as the first scope, then add a narrower scope when it makes the commit easier to scan. Write the summary in the imperative mood and omit a trailing period.
system: route jailed restarts through graceful shutdownweb: vm: make request outcomes explicitdocs: app-docs: improve the getting-started pageci/cd: add pull-request validationKeep each commit focused on one coherent change. If more context is useful, leave a blank line after the subject and explain the motivation and important trade-offs in the commit body.
Formatting, Testing, And Builds
Section titled “Formatting, Testing, And Builds”Run the checks relevant to your change before opening a pull request. The commands below mirror the repository’s automated pull-request workflows.
Repository-wide quality checks
Section titled “Repository-wide quality checks”From the repository root, run the same code-quality gate used by CI:
make qualityThis command does not intentionally modify source files. It:
- Checks every tracked or unignored Go file with
gofmtand fails with a list of files that need formatting. - Installs the exact frontend dependencies from
web/package-lock.jsonusingnpm ci. - Runs the frontend lint task, which checks Prettier formatting and ESLint rules.
- Runs
svelte-check, treating warnings as failures.
If the formatting or lint steps fail, apply the available automatic fixes with:
make quality-fixmake quality-fix rewrites tracked and unignored Go files with gofmt, then runs Prettier and ESLint’s automatic fixes in web/. Review the resulting changes before committing. It does not install frontend dependencies or run svelte-check, so follow it with make quality to verify the complete quality gate:
make quality-fixmake qualityThe fix target cannot resolve every lint or type-checking problem. If make quality still fails, address the reported issues manually and run it again.
Go formatting and unit tests
Section titled “Go formatting and unit tests”From the repository root, format Go code and run the short unit-test suite:
go fmt ./...make testmake test runs go test -short across the repository, including the ZFS test utilities. Run it on FreeBSD so packages that depend on FreeBSD interfaces are compiled and exercised in the supported environment.
Integration tests
Section titled “Integration tests”Backend changes that interact with Jails, ZFS, networking, libvirt, migration, clustering, or replication should also run the integration suite:
doas make test-integrationIntegration tests must run as root on FreeBSD with ZFS available. The target checks for leaked ZFS test resources before and after the suite and runs integration packages without the Go test cache.
The repository also provides more specialized suites:
| Command | Use it when |
|---|---|
doas make test-acceptance |
A change affects end-to-end console workflows. |
doas make test-acceptance-full |
You need the longer full acceptance run. |
doas make test-smart-integration SMART_DEVICE=/dev/… |
A disk or SMART change must be tested against explicitly selected hardware. This target can interact with the named device, so review its options first. |
These specialized targets are not part of the standard pull-request workflow, but maintainers may request them for relevant changes.
Frontend checks
Section titled “Frontend checks”From the repository root, run:
npm run format --prefix webnpm run lint --prefix webmake frontendnpm run build:demo --prefix webnpm run format writes Prettier changes. npm run lint verifies formatting and ESLint rules. make frontend performs a clean dependency install, runs svelte-check with warnings treated as failures, builds the production frontend, and copies the generated assets into the Go embed directory. The final command verifies the separate demo build used by the project website.
If a frontend change adds, changes, or removes user-facing text, follow the translation contribution guide to extract, clean, and validate the locale catalogs before submitting it.
Full local build
Section titled “Full local build”On a FreeBSD development host, build the complete application with:
make buildThis builds the frontend assets first and then produces the FreeBSD sylve binary in bin/.
What runs on a pull request
Section titled “What runs on a pull request”For pull requests that change application code, GitHub runs both the Test and Build workflows before merge:
| Workflow | Automated checks |
|---|---|
| Test | Runs the short Go unit suite on FreeBSD 15. If it succeeds, prepares ZFS, PF, VMM, libvirt, and local SSH before running the integration suite. Test summaries and raw reports are uploaded as workflow artifacts. |
| Build | Uses Node.js 24 to check and build the production frontend and demo frontend, then cross-builds FreeBSD 15 binaries for amd64 and arm64 and verifies each binary’s OS and architecture. |
Both workflows cancel superseded runs when a pull request is updated. Their test and build checks are expected to pass before a pull request is merged. Because the workflows ignore changes confined to docs/**, documentation-only pull requests do not run the application test/build matrix.