Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# shellcheck shell=bash

if command -v nix >/dev/null 2>&1; then
if ! has nix_direnv_version; then
log_status "nix-direnv not found; installing to add nix support..."
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.1.1/direnvrc" "sha256-p+fzQdrms/hDa7g+soShAybJNo4bN4SIAeSfqNKgD5I="
fi

log_status "Loading nix shell from flake..."
log_status "To suppress direnv log output: export DIRENV_LOG_FORMAT=\"\""

watch_file $(find . -name "*.nix")
use flake
fi
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@ ui.zip
# local embedded frontend output for fast E2E runs
internal/http/dist/*
!internal/http/dist/.gitkeep

result
.direnv
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"bbenoist.nix",
"mkhl.direnv"
]
}
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ If you've looked at Wiki.js or Outline and thought "this is too much to operate
→ If it fits, [a star](https://github.com/perber/leafwiki) helps others find it.

```bash
# Docker
docker run -p 8080:8080 -v ~/leafwiki-data:/app/data \
ghcr.io/perber/leafwiki:latest \
--jwt-secret=yoursecret --admin-password=yourpassword --allow-insecure=true

# Nix
nix run github:perber/leafwiki -- --help
```

→ [All install options](#install) (Docker Compose, Linux installer, binary)
Expand Down Expand Up @@ -165,6 +169,81 @@ sudo ./install.sh --non-interactive --env-file ./.env
- [Install with nginx on Ubuntu](docs/install/nginx.md)
- [Install on a Raspberry Pi](docs/install/raspberry.md)

### Nix

#### Try it without installing

```bash
nix run github:perber/leafwiki -- --jwt-secret=yoursecret --admin-password=yourpassword --allow-insecure=true
```

#### Add to a NixOS configuration

In your `flake.nix`:

```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
leafwiki.url = "github:perber/leafwiki";
};

outputs = { nixpkgs, leafwiki, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
leafwiki.nixosModules.default
{
services.leafwiki = {
enable = true;
jwtSecretFile = "/run/secrets/leafwiki-jwt";
adminPasswordFile = "/run/secrets/leafwiki-admin-password";
# Optional overrides:
# host = "0.0.0.0";
# port = 8080;
# dataDir = "/var/lib/leafwiki";
# disableAuth = true; # insecure - only for local dev instances!
};
}
];
};
};
}
```

Secrets are loaded from files at service start using systemd `LoadCredential`, so they never appear in the Nix store or the unit file.

#### Add to a Home Manager configuration

```nix
{
inputs = {
home-manager.url = "github:nix-community/home-manager";
leafwiki.url = "github:perber/leafwiki";
};

outputs = { home-manager, leafwiki, ... }: {
homeConfigurations.myuser = home-manager.lib.homeManagerConfiguration {
modules = [
leafwiki.homeManagerModules.default
{
services.leafwiki = {
enable = true;
jwtSecretFile = "/run/user/1000/secrets/leafwiki-jwt";
adminPasswordFile = "/run/user/1000/secrets/leafwiki-admin-password";
};
}
];
};
};
}
```

The Home Manager module runs leafwiki as a `systemd --user` service. Enable lingering so it starts at boot without a login session:

```bash
loginctl enable-linger $USER
```

### Binary

```bash
Expand Down
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
description = "LeafWiki - a fast wiki for people who think in folders, not feeds";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};

outputs =
{
self,
nixpkgs,
flake-utils,
}:
let
version = "0.0.0";

perSystemOutputs = flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
pkgSet = import ./nix/packages.nix { inherit pkgs version; };
packages = {
default = pkgSet.leafwiki;
inherit (pkgSet) leafwiki ui;
};
apps = rec {
default = leafwiki-app;
leafwiki-app = {
type = "app";
program = "${pkgSet.leafwiki}/bin/leafwiki";
};
};
shell = import ./nix/shell.nix { inherit pkgs; };
in
{
inherit packages apps;
inherit (shell) devShells;
formatter = pkgs.nixfmt;
}
);
in
perSystemOutputs
// {
nixosModules = {
default = import ./nix/nixos-module.nix self;
leafwiki = import ./nix/nixos-module.nix self;
};

homeManagerModules = {
default = import ./nix/home-manager-module.nix self;
leafwiki = import ./nix/home-manager-module.nix self;
};
};
}
82 changes: 82 additions & 0 deletions nix/common.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Shared option declarations and helpers used by both the NixOS and the
# Home Manager module. Callers must pass:
# - lib – the nixpkgs lib
# - defaultPkg – the resolved leafwiki package for the current system
# - dataDirType – the mkOption type for dataDir (path vs. str differs)
# - dataDirDefault – the default value for dataDir
{
lib,
defaultPkg,
dataDirType,
dataDirDefault,
}:
{
# ---- shared option declarations -----------------------------------------
options = {
package = lib.mkOption {
type = lib.types.package;
default = defaultPkg;
description = "The leafwiki package to use.";
};

host = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "Address to bind to.";
};

port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "Port to listen on.";
};

dataDir = lib.mkOption {
type = dataDirType;
default = dataDirDefault;
description = "Directory to store wiki data.";
};

jwtSecretFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Path to a file containing the JWT secret. Required unless disableAuth is true.";
};

adminPasswordFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Path to a file containing the admin password. Required unless disableAuth is true.";
};

disableAuth = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Disable authentication (everyone can read and edit).";
};

extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Additional CLI arguments passed to leafwiki.";
};
};

# ---- shared helpers ------------------------------------------------------

# Build the ExecStart string. The authArgs differ slightly between the NixOS
# module (reads from $CREDENTIALS_DIRECTORY) and Home Manager (reads the file
# path directly), so the caller supplies them.
mkExecStart =
cfg: authArgs:
lib.escapeShellArgs (
[
"${cfg.package}/bin/leafwiki"
"--host=${cfg.host}"
"--port=${toString cfg.port}"
"--data-dir=${cfg.dataDir}"
]
++ authArgs
++ cfg.extraArgs
);
}
55 changes: 55 additions & 0 deletions nix/home-manager-module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Home Manager module – runs leafwiki as a systemd --user service.
# Secrets are read from plain files at service start time.
self:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.leafwiki;
# Fall back gracefully when the flake's system package is unavailable
# (e.g. when home-manager is used standalone with a different nixpkgs).
leafwikiPkg = self.packages.${pkgs.stdenv.hostPlatform.system}.leafwiki or pkgs.leafwiki;
common = import ./common.nix {
inherit lib;
defaultPkg = leafwikiPkg;
# str instead of path: supports systemd specifiers like %h
dataDirType = lib.types.str;
dataDirDefault = "%h/.local/share/leafwiki";
};
in
{
options.services.leafwiki = {
enable = lib.mkEnableOption "LeafWiki (user service)";
}
// common.options;

config = lib.mkIf cfg.enable {
systemd.user.services.leafwiki = {
Unit = {
Description = "LeafWiki";
After = [ "network.target" ];
};

Service = {
ExecStart =
let
authArgs =
if cfg.disableAuth then
[ "--disable-auth" ]
else
[
''--jwt-secret=$(cat "${cfg.jwtSecretFile}")''
''--admin-password=$(cat "${cfg.adminPasswordFile}")''
];
in
common.mkExecStart cfg authArgs;
Restart = "on-failure";
};

Install.WantedBy = [ "default.target" ];
};
};
}
Loading
Loading