diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..59a603c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,69 @@
+.PHONY: serve clean info
+
+SRC_DIR := src
+BUILD_DIR := build
+ENTRIES_LIST := .entries.csv
+
+# Pandoc basic configuration
+PANDOC := pandoc -s
+PANDOC_FILTERS :=
+
+PANDOC_METADATA = \
+ --template=templates/default.html \
+ --metadata-file metadata.yaml \
+ --metadata=path="$(shell echo $@ | sed -e 's/build//g' )" \
+ --metadata=git_initial_date="$(shell git log --reverse -n 1 --pretty=format:%aI -- $< || echo 'draft')" \
+ --metadata=git_date="$(shell git log -n 1 --pretty=format:%aI -- $< || echo 'draft')" \
+
+PANDOC_COMMAND = $(PANDOC) $(PANDOC_FILTERS) $(PANDOC_METADATA)
+
+# Blog Configuration
+COMMENT_SECTION := templates/comment-section.html
+PANDOC_BLOG_FILTER := filters/blog_feed.py
+
+# Public Pages configuration
+#
+# All markdown files
+PAGES = \
+ build/index.html \
+ build/contact.html
+FEEDS = build/blog/index.html build/blog/rss.xml
+ENTRIES = $(shell find src/blog -mindepth 2 -type f -name '*.md' | sed -e 's/\.md/.html/;s/src/build/g')
+# PAGES = # Define here which pages to publish.
+# PAGES = build/index.html
+
+info:
+ @echo $(ENTRIES)
+
+
+all: $(PAGES) $(FEEDS)
+
+$(ENTRIES_LIST): $(ENTRIES)
+
+
+$(BUILD_DIR)/blog/%.html: $(SRC_DIR)/blog/%.md $(COMMENT_SECTION)
+ mkdir -p $(shell dirname $@)
+ [[ -d "$(shell dirname $<)/assets" ]] \
+ && cp -r $(shell dirname $<)/assets/. $(shell dirname $@)/assets \
+ || echo "Entry with no assets"
+ $(PANDOC_COMMAND) \
+ --filter $(PANDOC_BLOG_FILTER) \
+ --include-after-body=$(COMMENT_SECTION) \
+ -i $< -o $@
+
+$(BUILD_DIR)/blog/index.html: $(SRC_DIR)/blog/index.md $(ENTRIES_LIST)
+ cat $(ENTRIES_LIST) | sort | ./scripts/entries2rss.py html | \
+ $(PANDOC_COMMAND) -i $< -i - -o $@
+
+$(BUILD_DIR)/blog/rss.xml: $(ENTRIES_LIST)
+ cat $(ENTRIES_LIST) | sort | ./scripts/entries2rss.py > $@
+
+$(BUILD_DIR)/%.html: $(SRC_DIR)/%.md
+ mkdir -p $(shell dirname $@)
+ $(PANDOC_COMMAND) -i $< -o $@
+
+serve:
+ python3 -m http.server --directory $(BUILD_DIR)
+
+clean:
+ rm -rf $(BUILD_DIR) $(ENTRIES_LIST)
diff --git a/filters/blog_feed.py b/filters/blog_feed.py
new file mode 100755
index 0000000..cde3a1a
--- /dev/null
+++ b/filters/blog_feed.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+import csv
+import panflute as pf
+
+def action(elem, doc):
+ pass
+
+def finalize(doc):
+ keys = [
+ "git_date",
+ "git_initial_date",
+ "title",
+ "subject",
+ "path",
+ "base_url",
+ "feed_list_path",
+ ]
+ rss_data = {}
+ for k in keys:
+ rss_data[k] = doc.get_metadata(k, "")
+ feed_list_path = rss_data["feed_list_path"]
+ path = rss_data["base_url"] + rss_data["path"]
+ rss_data["path"] = path
+ del rss_data["feed_list_path"]
+ del rss_data["base_url"]
+ with open(feed_list_path, "a") as file:
+ writer = csv.DictWriter(file, fieldnames=keys[:-2])
+ writer.writerow(rss_data)
+
+def main(doc=None):
+ return pf.run_filter(action, finalize=finalize, doc=doc)
+
+if __name__ == "__main__":
+ main()
diff --git a/metadata.yaml b/metadata.yaml
new file mode 100644
index 0000000..d3f5400
--- /dev/null
+++ b/metadata.yaml
@@ -0,0 +1,11 @@
+title: Personal Site
+subtitle: Foo Bar
+global_links:
+ - name: Index
+ uri: /
+ - name: Contact
+ uri: /contact.html
+ - name: Blog
+ uri: /blog
+feed_list_path: .entries.csv
+base_url: http://localhost:8000
diff --git a/scripts/entries2rss.py b/scripts/entries2rss.py
new file mode 100755
index 0000000..e3abfa5
--- /dev/null
+++ b/scripts/entries2rss.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+from sys import stdin, argv
+from csv import DictReader
+import panflute as pf
+import yaml
+from yaml.loader import SafeLoader
+
+fieldnames = [
+ "git_date",
+ "git_initial_date",
+ "title",
+ "subject",
+ "path",
+]
+
+def entry_to_rss(entry):
+ return (
+ f'
$subtitle$
+$endif$ +$for(author)$ + +$endfor$ +$if(date)$ +$date$
+$endif$ +$if(abstract)$ +