diff --git a/Cargo.lock b/Cargo.lock
index 2beb1e5..c2f3267 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -35,6 +35,34 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "argh"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e"
+dependencies = [
+ "argh_derive",
+ "argh_shared",
+]
+
+[[package]]
+name = "argh_derive"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6"
+dependencies = [
+ "argh_shared",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "argh_shared"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -470,6 +498,15 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "krile"
+version = "0.5.6"
+dependencies = [
+ "argh",
+ "minifemme",
+ "shtola 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -886,6 +923,24 @@ dependencies = [
  "ware 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "shtola"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0807abbba44d0d5aaae56cba07cb4787e3a31d2a7a0cc5cfc809c8405d5b2a11"
+dependencies = [
+ "comrak",
+ "globset",
+ "id_tree",
+ "log",
+ "pathdiff",
+ "serde_json",
+ "serde_yaml",
+ "tera",
+ "walkdir",
+ "ware 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "slug"
 version = "0.1.4"
diff --git a/Cargo.toml b/Cargo.toml
index d6bee75..250aa0f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,6 @@
 [workspace]
 members = [
   "shtola",
-  "ware"
+  "ware",
+  "krile"
 ]
diff --git a/krile/CHANGELOG.md b/krile/CHANGELOG.md
new file mode 100644
index 0000000..0f55c09
--- /dev/null
+++ b/krile/CHANGELOG.md
@@ -0,0 +1,85 @@
+# Changelog
+
+## [0.5.6] - 2021-11-10
+
+### Changed
+
+- Upgraded shtola to `0.4.2`.
+
+## [0.5.5] - 2021-11-10
+
+### Changed
+
+- Upgraded shtola to `0.4.1`.
+
+## [0.5.4] - 2021-11-10
+
+### Added
+
+- Upgraded shtola to `0.4.0`.
+- Added pretty links support.
+
+## [0.5.3] - 2021-11-09
+
+### Changed
+
+- Upgraded shtola to `0.3.0`.
+
+## [0.5.2] - 2021-11-09
+
+### Changed
+
+- The Markdown processing plugin now runs before the Layout processing
+  plugin. This is because rendering half-Markdown, half-HTML pages will
+  break something at some point, so it's best to render layouts when they're
+  entirely HTML.
+
+## [0.5.1] - 2021-11-03
+
+### Added
+
+- Upgraded shtola to `0.3.0-alpha5`.
+
+## [0.5.0] - 2021-09-21
+
+### Changed
+
+- The CLI interface now takes source and destination in the form of option
+  flags instead of positional arguments.
+- Added a `--version` switch.
+- Upgraded shtola to `0.3.0-alpha5`.
+
+## [0.4.1] - 2021-07-23
+
+### Added
+
+- Upgraded shtola to `0.3.0-alpha4`.
+
+## [0.4.0] - 2021-07-23
+
+### Added
+
+- Read other ignores from a `.krileignore` file.
+
+## [0.3.1] - 2021-07-23
+
+### Added
+
+- Upgraded shtola to `0.3.0-alpha3`.
+
+## [0.3.0] - 2021-07-23
+
+### Added
+
+- Upgraded shtola to `0.3.0-alpha2`.
+- Krile now reads your `.gitignore` (at top level) and ignores all of the stuff in it.
+
+## [0.2.0] - 2021-07-23
+
+### Added
+
+- Added a flag to toggle verbose log output.
+
+## [0.1.0] - 2021-07-21
+
+Initial release.
diff --git a/krile/Cargo.toml b/krile/Cargo.toml
new file mode 100644
index 0000000..6220108
--- /dev/null
+++ b/krile/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "krile"
+description = "A static site generator"
+version = "0.5.6"
+license = "AGPL-3.0-or-later"
+edition = "2021"
+repository = "https://codeberg.org/shadows_withal/shtola/src/branch/main/krile"
+readme = "README.md"
+
+[dependencies]
+shtola = { version = "0.4.2", features = ["full"] }
+argh = "~0.1"
+minifemme = "~1.0"
diff --git a/krile/README.md b/krile/README.md
new file mode 100644
index 0000000..d2b0edf
--- /dev/null
+++ b/krile/README.md
@@ -0,0 +1,27 @@
+# krile
+
+Krile is a reference implementation of a CLI frontend for [shtola](https://crates.io/crates/shtola).
+
+It's pretty unstable, since it usually tracks prerelease versions of shtola, I mostly use it
+for myself. You're welcome to use it as the basis for your own frontend though.
+
+## Installation
+
+```sh
+cargo install krile
+```
+
+## Usage
+
+```sh
+# Minimal configuration
+krile page-source/ # outputs to ./dest/
+# Clean destination directory (my-dest/) before building
+krile page-source/ my-dest/ -c
+# Don't run the markdown plugin
+krile page-source/ -i markdown
+```
+
+## License
+
+AGPL 3.0.
diff --git a/krile/src/main.rs b/krile/src/main.rs
new file mode 100644
index 0000000..4aa3c61
--- /dev/null
+++ b/krile/src/main.rs
@@ -0,0 +1,87 @@
+use std::path::PathBuf;
+use std::time::Instant;
+
+use argh::FromArgs;
+use shtola::log::*;
+use shtola::plugins;
+use shtola::Shtola;
+
+const VERSION: &'static str = "0.5.6";
+
+#[derive(FromArgs)]
+/// A static site generator.
+struct Args {
+    /// output the current version
+    #[argh(switch)]
+    version: bool,
+
+    /// the source directory
+    #[argh(option, short = 's', default = "PathBuf::from(\"./\")")]
+    source: PathBuf,
+
+    /// the destination directory
+    #[argh(option, short = 'd', default = "PathBuf::from(\"./dest\")")]
+    destination: PathBuf,
+
+    /// whether to wipe the destination directory before building
+    #[argh(switch, short = 'c')]
+    clean: bool,
+
+    /// what plugins to not run
+    #[argh(option, short = 'i')]
+    ignored_plugins: Vec<String>,
+
+    /// whether to skip parsing frontmatter (will probably break plugins)
+    #[argh(switch)]
+    no_frontmatter: bool,
+
+    /// whether to output debug stuff
+    #[argh(switch, short = 'v')]
+    verbose: bool,
+}
+
+fn main() {
+    let args: Args = argh::from_env();
+
+    if args.version {
+        println!("{}", VERSION);
+        std::process::exit(0);
+    }
+
+    if args.verbose {
+        minifemme::start(minifemme::LevelFilter::Trace, minifemme::LogMode::Pretty);
+    } else {
+        minifemme::start(minifemme::LevelFilter::Info, minifemme::LogMode::Pretty);
+    }
+
+    info!("Starting build");
+    let start = Instant::now();
+    let mut sh = Shtola::new();
+    sh.source(&args.source);
+    sh.destination(args.destination);
+    sh.clean(args.clean);
+    sh.frontmatter(!args.no_frontmatter);
+    let potential_gitignore = args.source.join(".gitignore");
+    if potential_gitignore.exists() {
+        sh.source_ignores(&potential_gitignore).unwrap();
+    }
+    let potential_krileignore = args.source.join(".krileignore");
+    if potential_krileignore.exists() {
+        sh.source_ignores(&potential_krileignore).unwrap();
+    }
+    if !args.ignored_plugins.contains(&"markdown".to_string()) {
+        sh.register(plugins::markdown::plugin());
+    }
+    if !args.ignored_plugins.contains(&"tera_layouts".to_string()) {
+        sh.register(plugins::tera_layouts::plugin());
+    }
+    if !args.ignored_plugins.contains(&"pretty_links".to_string()) {
+        sh.register(plugins::pretty_links::plugin());
+    }
+
+    sh.build().unwrap();
+    info!(
+        "Finished build in {:?}",
+        Instant::now().duration_since(start)
+    );
+}
diff --git a/ware/Cargo.toml b/ware/Cargo.toml
index 090bc61..ba923cb 100644
--- a/ware/Cargo.toml
+++ b/ware/Cargo.toml
@@ -10,6 +10,6 @@ homepage = "https://codeberg.org/shadows_withal/shtola/src/branch/main/ware"
 readme = "README.md"
 categories = ["data-structures"]
 keywords = ["middleware"]
-license-file = "AGPL-3.0-or-later"
+license = "AGPL-3.0-or-later"
 
 [dependencies]