diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/ansible-playbooks.iml b/.idea/ansible-playbooks.iml
new file mode 100644
index 0000000..4a24893
--- /dev/null
+++ b/.idea/ansible-playbooks.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..23231ce
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..ba01819
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sabre-dav/.gitignore b/sabre-dav/.gitignore
new file mode 100644
index 0000000..35f3568
--- /dev/null
+++ b/sabre-dav/.gitignore
@@ -0,0 +1,164 @@
+
+# Created by https://www.gitignore.io/api/macos,python,ansible
+# Edit at https://www.gitignore.io/?templates=macos,python,ansible
+
+### Ansible ###
+*.retry
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don’t work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# Downloaded files
+root@*
+
+# End of https://www.gitignore.io/api/macos,python,ansible
\ No newline at end of file
diff --git a/sabre-dav/.idea/.gitignore b/sabre-dav/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/sabre-dav/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/sabre-dav/.idea/inspectionProfiles/profiles_settings.xml b/sabre-dav/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/sabre-dav/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sabre-dav/.idea/misc.xml b/sabre-dav/.idea/misc.xml
new file mode 100644
index 0000000..f1ed6f5
--- /dev/null
+++ b/sabre-dav/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sabre-dav/.idea/modules.xml b/sabre-dav/.idea/modules.xml
new file mode 100644
index 0000000..56cff7e
--- /dev/null
+++ b/sabre-dav/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sabre-dav/.idea/sabre-dav.iml b/sabre-dav/.idea/sabre-dav.iml
new file mode 100644
index 0000000..e43d4d2
--- /dev/null
+++ b/sabre-dav/.idea/sabre-dav.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sabre-dav/.idea/vcs.xml b/sabre-dav/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/sabre-dav/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sabre-dav/Makefile b/sabre-dav/Makefile
new file mode 100644
index 0000000..0789436
--- /dev/null
+++ b/sabre-dav/Makefile
@@ -0,0 +1,9 @@
+install:
+ sudo dnf install ansible
+ ansible-galaxy collection install community.general
+ ansible-galaxy collection install containers.podman
+ ansible-galaxy collection install ansible.posix
+list-hosts:
+ ansible-inventory -i inventory.ini --list
+run:
+ ansible-playbook -i inventory.ini playbook.yaml --ask-become-pass
diff --git a/sabre-dav/img.png b/sabre-dav/img.png
new file mode 100644
index 0000000..ee8e53b
Binary files /dev/null and b/sabre-dav/img.png differ
diff --git a/sabre-dav/inventory.ini b/sabre-dav/inventory.ini
new file mode 100644
index 0000000..b55c7de
--- /dev/null
+++ b/sabre-dav/inventory.ini
@@ -0,0 +1,5 @@
+[nuculabs]
+my-linux.box ansible_user=ansible
+
+[local]
+localhost
\ No newline at end of file
diff --git a/sabre-dav/playbook.yaml b/sabre-dav/playbook.yaml
new file mode 100644
index 0000000..49b6c58
--- /dev/null
+++ b/sabre-dav/playbook.yaml
@@ -0,0 +1,100 @@
+- name: Install Baïkal server
+ hosts: nuculabs
+ become: true
+ become_method: sudo
+ vars_files:
+ - variables.yaml
+ tasks:
+ # Create necessary directories.
+ - name: "Create directories"
+ block:
+ - name: Create base directory
+ ansible.builtin.file:
+ path: "{{ baikal.base_directory }}"
+ state: directory
+ mode: "0755"
+ ignore_errors: true
+ - name: Create data directory
+ ansible.builtin.file:
+ path: "{{ baikal.base_directory }}/{{ baikal.data_directory }}"
+ state: directory
+ mode: "0755"
+ ignore_errors: true
+ - name: Create extensions directory
+ ansible.builtin.file:
+ path: "{{ baikal.base_directory }}/{{ baikal.config_directory }}"
+ state: directory
+ mode: "0755"
+ ignore_errors: true
+# - name: Setup SELinux
+# block:
+# - name: Ensure Udica is installed
+# ansible.builtin.package:
+# name: udica
+# state: present
+# - name: Ensure container-selinux is installed
+# ansible.builtin.package:
+# name: container-selinux
+# state: present
+# - name: Copy baikal cil
+# ansible.builtin.copy:
+# src: ./templates/selinux/baikal.cil
+# dest: /tmp/baikal.cil
+# mode: "0644"
+# - name: Load baikal CIL policy
+# ansible.builtin.command:
+# argv:
+# - semodule
+# - -i
+# - /tmp/baikal.cil
+# - /usr/share/udica/templates/base_container.cil
+# - /usr/share/udica/templates/net_container.cil
+# when: setup_selinux
+ - name: Setup Container
+ block:
+ - name: Ensure Podman is installed
+ ansible.builtin.package:
+ name: podman
+ state: present
+ - name: Pull image
+ containers.podman.podman_image:
+ name: "{{ baikal.container_image }}"
+ state: present
+ - name: "Copy container"
+ ansible.builtin.template:
+ src: ./templates/container/baikal.container.j2
+ dest: /etc/containers/systemd/baikal.container
+ mode: "0644"
+ - name: Reload systemd
+ ansible.builtin.command:
+ cmd: systemctl daemon-reload
+ - name: Stop service
+ ansible.builtin.systemd_service:
+ name: baikal.service
+ state: stopped
+ enabled: true
+ - name: Enable service
+ ansible.builtin.systemd_service:
+ name: baikal.service
+ state: started
+ enabled: true
+ - name: Setup firewall
+ block:
+ - name: Create firewalld service
+ ansible.builtin.template:
+ src: ./templates/firewall/baikal.xml.j2
+ dest: /etc/firewalld/services/baikal.xml
+ mode: "0644"
+ - name: Reload firewalld
+ ansible.builtin.command:
+ argv:
+ - firewall-cmd
+ - --reload
+ - name: Enable firewalld service
+ ansible.posix.firewalld:
+ service: baikal
+ state: enabled
+ permanent: true
+ immediate: true
+ offline: true
+ when: setup_firewall
diff --git a/sabre-dav/readme.md b/sabre-dav/readme.md
new file mode 100644
index 0000000..e924b6c
--- /dev/null
+++ b/sabre-dav/readme.md
@@ -0,0 +1,11 @@
+# Self Hosted Baïkal server
+
+Baïkal is a lightweight CalDAV+CardDAV server. It offers an extensive web interface with easy management of users,
+address books and calendars. It is fast and simple to install and only needs a basic php capable server.
+The data can be stored in a MySQL or a SQLite database.
+
+It was tested on Fedora 41.
+
+References:
+- https://sabre.io/baikal/
+- https://hub.docker.com/r/ckulka/baikal
\ No newline at end of file
diff --git a/sabre-dav/templates/container/baikal.container.j2 b/sabre-dav/templates/container/baikal.container.j2
new file mode 100644
index 0000000..a39a77c
--- /dev/null
+++ b/sabre-dav/templates/container/baikal.container.j2
@@ -0,0 +1,18 @@
+[Unit]
+Description=baikal
+
+[Container]
+Image={{ baikal.container_image }}
+AutoUpdate=registry
+PublishPort={{ baikal.port }}:80/tcp
+Volume={{ baikal.base_directory }}/{{ baikal.data_directory }}:/var/www/baikal/Specific:Z
+Volume={{ baikal.base_directory }}/{{ baikal.config_directory }}:/var/www/baikal/config:Z
+Environment=MSMTPRC={{ baikal.mail }}
+
+[Service]
+# Inform systemd of additional exit status
+SuccessExitStatus=0 143
+
+[Install]
+# Start by default on boot
+WantedBy=default.target
\ No newline at end of file
diff --git a/sabre-dav/templates/firewall/baikal.xml.j2 b/sabre-dav/templates/firewall/baikal.xml.j2
new file mode 100644
index 0000000..6330fe0
--- /dev/null
+++ b/sabre-dav/templates/firewall/baikal.xml.j2
@@ -0,0 +1,6 @@
+
+
+ Baikal Calendar
+ A lightweight CalDAV+CardDAV server.
+
+
diff --git a/sabre-dav/templates/selinux/baikal.cil b/sabre-dav/templates/selinux/baikal.cil
new file mode 100644
index 0000000..eb5695c
--- /dev/null
+++ b/sabre-dav/templates/selinux/baikal.cil
@@ -0,0 +1,8 @@
+(block freshrss
+ (blockinherit container)
+ (blockinherit restricted_net_container)
+
+ (allow process default_t (dir (setattr add_name write create)))
+ (allow process default_t (file (create)))
+ (allow process http_port_t (tcp_socket (name_bind)))
+)
diff --git a/sabre-dav/variables.yaml b/sabre-dav/variables.yaml
new file mode 100644
index 0000000..9350beb
--- /dev/null
+++ b/sabre-dav/variables.yaml
@@ -0,0 +1,19 @@
+setup_selinux: true
+setup_firewall: true # exposes the firewall ports
+baikal:
+ container_image: "docker.io/ckulka/baikal:nginx"
+ port: 8069
+ base_directory: "/baikal"
+ data_directory: "data"
+ config_directory: "config"
+ mail: "" # note you will need to inline the following variables
+# defaults
+# auth on
+# tls on
+# tls_trust_file /etc/ssl/certs/ca-certificates.crt
+# account default
+# host
+# port 587
+# from baikal@example.com
+# user
+# password
\ No newline at end of file