# Forest: a Rust implementation of a Filecoin node

> Forest is ChainSafe's Rust implementation of a Filecoin node, positioned as a faster and easier to use alternative to the canonical Lotus node rather than a more complete one. It ships a daemon, a CLI, a wallet and a tool binary, installs with mise, and runs on Docker across Linux, macOS and Windows.

**ChainSafe/forest** — 🌲 Rust Filecoin Node Implementation

- Repository: https://github.com/ChainSafe/forest
- Website: https://forest.chainsafe.io
- Stars: 699 · Forks: 200
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/chainsafe-forest

## What the node actually does

Forest is described in one line as a Filecoin node written in Rust, and the README then lists four things you can do with it. You can transfer FIL, host a high-performance RPC API, validate the Filecoin blockchain, and generate blockchain snapshots.

Those four capabilities are the scope of the project, and the honest framing comes in the same paragraph. Forest is less feature-complete than the canonical Filecoin node, Lotus, and it aims to be the faster and easier-to-use alternative. That sentence is the most important thing in the README for an evaluator, because it defines the trade explicitly: you give up feature parity and you get startup speed and a smaller operational surface.

The last item on the list is the one that matters most for a node operator. Snapshot generation is what makes a node useful on a chain with a long history, since syncing from genesis is not practical for most deployments. The join-testnet instructions point at the same mechanism from the other direction, with an --auto-download-snapshot flag that fetches a snapshot if one is needed, without prompting.

The repository is a Cargo workspace with a large dependency surface, and the package is published under a dual MIT OR Apache-2.0 licence with edition 2024. The libp2p stack, tokio and axum are the load-bearing entries in the workspace dependency list, which tells you this is a libp2p peer with an HTTP API rather than a custom network stack.

## Four binaries come out of one build

A build produces more than a single program, and the Docker image verifies each of them before shipping. The verification step in the Dockerfile runs the version flag across all four: the daemon, the CLI, the tool binary and the wallet. The production image then copies the built forest* binaries out of the build stage into a slim ubuntu:24.04 layer with only ca-certificates installed.

That staging is deliberate. The build stage starts from a golang image and installs build-essential, clang-14, curl, git and ca-certificates, then installs Rust through rustup with a minimal profile, and finally runs the project's own install script before calling mise. The slim stage exists so the runtime image carries none of that. The daemon and CLI are what you actually interact with.

The Go toolchain in the build stage is not incidental. The repository has a separate f3-sidecar/ directory and a go.work file at the root, and the dependency list in the README states that Go is required for building the F3 sidecar module, with the toolchain version pinned in go.work. So a Forest build is a mixed Rust and Go build, and a contributor who skips the Go part will not get a working artefact.

There is a Go to Rust bridge in the dependency list, rust2go, which is what lets a Go-implemented module be called from the Rust side. That detail explains why the build environment needs both languages rather than one being optional.

## Docker is the shortest path to a running node

The README recommends Docker for people who do not want to install a Rust toolchain, and it is emphatic that Docker is all you need, working on Linux, macOS and Windows. The images are published to the GitHub container registry under the chainsafe scope.

The container has two entry points, one for the daemon and one for the CLI, and they are invoked through the same image with different flags. The daemon container runs with an init process, an interactive terminal and automatic removal, and the CLI container overrides the entry point to run forest-cli instead. Both take --help as their first argument, which is the fastest way to see the full flag surface without installing anything.

The pattern for working with a containerised node is that the daemon keeps running in one terminal and you open a shell inside the same container from another terminal:

```bash
docker exec -it forest /bin/bash
```

From there you run the forest-cli binary directly against the daemon that is already running in the container. The naming matters: the container is called forest, and that is the name you pass to docker exec.

One tag detail is stated explicitly. The latest tag is the latest stable release, and if you want the current development build you use the edge tag. That distinction is worth respecting in production, because edge is where breakage lands first.

## Installing from source leans on mise, not on cargo alone

The source install is three commands, and the middle one is the interesting part:

```bash
git clone --recursive https://github.com/chainsafe/forest
cd forest
```

The clone is recursive because the project carries submodules. After that, the build is driven by mise, the tool the README calls mise-en-place, which is used to handle builds and installations across the project. The install task puts the binary in your cargo bin directory:

```bash
mise run install
```

and the node is then started with no arguments at all:

```bash
forest
```

The build dependencies are listed as Rust with the toolchain version pinned in rust-toolchain.toml, Go for the F3 sidecar with the version pinned in go.work, a base development or build-essential package, and a Clang compiler. Two distributions get explicit install lines, Ubuntu 20.04 with build-essential and clang, and Fedora 36 with clang-devel. The project also expects rustup to be installed.

Using mise rather than a plain cargo build is a design choice with a visible consequence. The same tool drives the test suite and the linters, so a contributor who installs mise has one entry point for everything, and a contributor who does not will find that the documented commands do not work.

## Configuration is TOML, and the search order can surprise you

Forest reads a TOML configuration file, and the shape of it is short enough to be worth reproducing. The client section holds the data directory and the genesis file, and the network section holds the listening multiaddrs and the bootstrap peers:

```toml
[client]
data_dir = "<directory for all chain and networking data>"
genesis_file = "<relative file path of genesis car file>"

[network]
listening_multiaddrs = ["<multiaddress>"]
bootstrap_peers = ["<multiaddress>"]
```

A custom file is passed on the command line:

```bash
forest --config /path/to/your_config.toml
```

The part that deserves attention is the search order, because it determines which file actually wins. Forest looks first at paths passed with the --config flag, then at the environment variable FOREST_CONFIG_PATH if no config was given on the command line, then at the system default configuration directory, which is $XDG_CONFIG_HOME on Linux systems, and only if all of those are exhausted does it fall back to a default configuration.

The practical consequence is that a config file you did not write can still be in effect. If you set a flag on the command line it wins; if you do not, a file in your XDG config directory can win over your intentions, and nothing tells you which one was loaded. The failure mode is a node pointing at the wrong directory or the wrong peers with no obvious symptom.

## Keystore encryption, log filtering and CLI authentication

Three operational controls are documented in enough detail to be useful.

The first is keystore encryption. To encrypt the keystore in headless mode you set the FOREST_KEYSTORE_PHRASE environment variable. If you do not, you skip the encryption with the --encrypt-keystore false flag, and the documentation marks that option as not recommended in production environments. So there is a passphrase path and an explicitly discouraged path, and the default behaviour depends on whether the variable is set.

The second is logging, which uses Rust's own log filtering through the RUST_LOG environment variable:

```bash
RUST_LOG="debug,forest_libp2p::service=info" forest
```

That example turns on debug output everywhere and then limits the libp2p service module to info, which is the pattern to copy when the p2p layer is drowning the signal. Forest can also forward telemetry to a Loki instance or Loki agent, with --loki to enable it and --loki-endpoint to set the interface and port.

The third is how the CLI authenticates to the daemon. When the daemon starts it displays an admin token and saves it to the data directory by default, with a --save-token option to write it to disk explicitly. Commands that need a higher authorisation level, such as a password operation, require that token. Because Forest uses multiaddresses for networking, the CLI uses them too, and the FULLNODE_API_INFO environment variable holds the host, port and token:

```bash
FULLNODE_API_INFO="<token goes here>:/ip4/<host>/tcp/<port>/http
```

If no token is present in that value the colon is removed, and the token is set automatically for the CLI when it runs on the same host as the daemon.

## Testing, linters and the checks that run on every merge

The project makes a point of running its checks automatically, and it also documents how to reproduce them locally. Tests run through the nextest runner, which you install first:

```bash
cargo install cargo-nextest --locked
```

and then through mise, with a release profile variant:

```bash
mise test
mise test release
```

Chain synchronisation checks are run after every merge to main, and the README notes that this code is maintained in a separate repository, Forest IaC. That separation is worth noting: the most expensive correctness check for a chain node lives outside this tree, so the state of chain sync is not fully visible from the repository alone.

Linters follow the same pattern. The project uses a set of linters to keep the codebase clean and secure in an automated fashion, and the CI has them installed. To run them yourself, install the tools with mise install-lint-tools and then run mise lint. The repository tree shows how broad the lint surface is, with .clippy.toml for Clippy, .golangci.yml for the Go sidecar, deny.toml for dependency policy checks, taplo.toml for TOML formatting, and .eslintrc.yml plus pnpm tooling for the JavaScript and TypeScript parts of the tooling.

The tree also shows a documentation build, a monitored-stack/ directory, an interop-tests/ directory, test-snapshots/ and benches/, so the repository carries the full apparatus of a production node rather than a proof of concept.

## Conclusion

Adopt Forest if you need a Filecoin node that starts quickly, configures from a plain TOML file, and gives you a single Rust binary set to operate, and if you can tolerate gaps against the canonical implementation. Do not adopt it as a drop-in replacement for Lotus, because the project itself describes itself as less feature-complete, and do not assume protocol parity across a network upgrade, since Forest and Lotus ship on their own schedules. Before you commit, pick a configuration file and check the config search order, because a stray file in your XDG config directory will silently override your defaults, decide whether the keystore is encrypted through FOREST_KEYSTORE_PHRASE rather than left in the clear, and confirm the chain you point the node at with the --chain option, since a wrong network choice is not something the node will warn you about.

## FAQ

### What is Forest and how does it compare to Lotus?

Forest is a Filecoin node written in Rust by ChainSafe. The README says it is less feature-complete than the canonical Filecoin node, Lotus, and aims to be the faster and easier to use alternative, offering FIL transfers, an RPC API, blockchain validation and snapshot generation.

### How do I install and run a Forest node?

With Docker, run the daemon image from ghcr.io/chainsafe/forest, or build from source with git clone --recursive, cd forest and mise run install, which places the binary in your cargo bin directory. The node is then started by running forest, and you can open a shell in the container with docker exec -it forest /bin/bash to use forest-cli.

### How does Forest decide which configuration file to use?

It looks for paths given with the --config flag first, then the FOREST_CONFIG_PATH environment variable, then the system default configuration directory, which is $XDG_CONFIG_HOME on Linux. If none are found, a default configuration is assumed.

### How do I encrypt the Forest keystore and control its logging?

Set the FOREST_KEYSTORE_PHRASE environment variable to encrypt the keystore in headless mode, or pass --encrypt-keystore false to skip it, which the docs mark as not recommended in production. Logging uses the RUST_LOG variable with Rust's log filtering, and telemetry can be sent to a Loki endpoint with --loki and --loki-endpoint.

### What licence is Forest released under?

The Cargo package declares the licence as MIT OR Apache-2.0, and the repository root carries both LICENSE-APACHE and LICENSE-MIT files. The package is forest-filecoin, currently at version 0.38.0, and the homepage is forest.chainsafe.io.

## Sources

- [ChainSafe/forest on GitHub](https://github.com/ChainSafe/forest)
- [License: Apache-2.0](https://github.com/ChainSafe/forest/blob/main/LICENSE)
- [Project website](https://forest.chainsafe.io)
- [README](https://github.com/ChainSafe/forest/blob/main/README.md)
- [Releases](https://github.com/ChainSafe/forest/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/chainsafe-forest
