Contributing #
Lichen is a CMS for static site generators, with realtime collaboration and ATProto integration. Code, documentation, bug reports, and usability improvements are welcome.
Before committing #
Keep changes focused. Describe the problem, the resulting behavior, and how you verified it in the pull request.
Production error handling #
Propagate or explicitly handle recoverable failures, normally with Result and ?.
Do not use expect, unwrap, panic! or an assertion as error handling. At task and
HTTP boundaries, report the error and complete or close waiting requests. Never carry
a lock guard or guard-bearing poison error through an async/public error boundary.
Tests may assert fixture outcomes or deliberately panic in a joined helper thread.
The rebuild module denies Clippy's unwrap, expect and panic lints outside tests;
legacy sites elsewhere remain follow-up work, not evidence that the repo is panic-free.
Development #
The Nix development shell provides the Rust toolchain and tools used by the gate:
nix develop
# Build the unified client/server executable
cargo build -p lichen-cli --bin lichen
# Run the local site manager, then visit http://127.0.0.1:9000/
cargo run -- server run
# Or serve a single site directory
cargo run -- server run --site ./site
# Format Rust code, then run the PR gate
just fmt
just gate
just gate checks formatting; it does not fix it. just crates shows which
crates it selects for linting and why, and just --list lists the available
commands. The gate shares its feature selection with the test harness in
e2e/lib/features.env; use it to check feature-gated code as well as the defaults.
The lint comparison defaults to review/main. If your checkout uses another
remote or target branch, set GATE_BASE to the branch you are contributing to,
for example GATE_BASE=origin/main just gate.
For a quick targeted Rust test while iterating, use
cargo test -p lichen-cms <test_name>, adding the relevant features when needed.
Run just gate before submitting the change.
Testing #
just gate is the normal check for small changes. Full end-to-end coverage is
reserved for release preparation or a planned, focused test pass:
# Full workspace gate (excluding the desktop crate)
just gate-all
# Behavioral end-to-end suites
just e2e standard
# Separate feature matrix; not included in the behavioral suites
just e2e feature-matrix
just e2e feature-matrix --full
Start with e2e/TESTING.md for current test selection and setup.
The normal gate excludes lichen-tauri, which needs additional desktop system
libraries. Use nix develop .#tauri when working on the desktop app and verify
those changes separately.
Nix and dependencies #
Without Nix, start with the toolchain in rust-toolchain.toml, including
rustfmt and clippy. The gate also needs tools such as just, Git, Node.js,
jq, and Hugo; the default shell in flake.nix is the maintained
list of development dependencies.
Cargo features select optional functionality, including Hugo, mdBook, and iroh. See README-extended.md and the CLI manifest for the feature definitions.
When changing dependencies, update Cargo.lock alongside the manifests. Git
dependency updates may also require changing cargoLock.outputHashes in
flake.nix. For dependency or packaging changes that affect the Nix package,
verify with:
nix build .#lichen
If Nix reports a fixed-output hash mismatch after an intentional dependency
update, update the corresponding hash and rerun the build. The package disables
its test phase, so a successful Nix build does not replace just gate.
Documentation #
README.md covers the quickstart and basic usage;
README-extended.md covers the command reference and hosting.
Keep that split when documenting new behavior. Configuration examples live in
settings.toml.example, and architecture and protocol details live in docs/.
Update CLI help when commands or flags change. For bugs, include reproduction steps, expected and actual behavior, and the relevant platform, version, and enabled features.
Architecture #
crates/lichen-cli/— thelichenexecutable, command dispatch, and remote clientcrates/lichen-server/— server modes, site management, authentication, and Git integrationcrates/lichen-cms/— editor routes, templates, static assets, saves, and static site generatorscrates/lichen-flat-file/— confined site-tree access and transactionscrates/lichen-yrs/— collaborative document and synchronization supportcrates/lichen-iroh/— optional peer-to-peer transportcrates/lichen-shell/— optional terminal supportcrates/lichen-tauri/— desktop applicatione2e/— shared test harness and end-to-end suites
Code style and concurrency #
Prefer APIs that enforce correctness over rules every caller must remember. Keep concurrency and lifecycle bookkeeping behind safe accessors rather than adding caller-managed locks.
Read docs/SITE-CONCURRENCY-MODEL.md before changing saves, Git operations, rooms, or anything that writes into a site tree. It defines ownership, lock order, and how the rules are enforced. Use the existing transaction and lifecycle APIs, and update the model when its contract changes.