diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c9a8331
--- /dev/null
+++ b/README.md
@@ -0,0 +1,172 @@
+# Cloonar Assistant
+
+This repository contains a NixOS module (`cloonar-assistant`) designed to help configure a home network appliance or router with various integrated services.
+
+## Features
+
+* **VLAN-based Network Segmentation:** Creates multiple VLANs (`lan`, `server`, `infrastructure`, `multimedia`, `smart`, `guest`) for network isolation.
+* **DHCP Server:** Uses Kea DHCP to provide IP addresses for each configured VLAN.
+* **DNS Resolver:** Configures Unbound DNS for local hostname resolution (integrating with Kea DHCP leases) and secure DNS-over-TLS forwarding for external queries.
+* **Firewall:** Implements a stateful firewall using `nftables` with granular rules for inter-VLAN traffic, WAN access, and NAT.
+* **WireGuard VPN:** Sets up a WireGuard VPN server for secure remote access.
+* **Dynamic DNS:** Includes a client (`updns-client`) to update a specified domain name with the system's current public IP address via `updns.cloonar.com`.
+* **Home Assistant:** Runs Home Assistant in a systemd container with a MariaDB backend and an Nginx reverse proxy.
+* **SSL Certificates:** Automatically obtains and renews SSL certificates for the specified domain using ACME (Let's Encrypt).
+* **ISO Build Utility:** Provides configuration to build a bootable ISO for automated minimal NixOS installation (useful for bootstrapping).
+
+## Prerequisites
+
+* A machine running NixOS where you want to apply this configuration.
+
+## Configuration (Module Usage)
+
+The primary way to use `cloonar-assistant` is by importing its module into your own NixOS `configuration.nix`.
+
+1. **Import the Module:** Fetch the module source using `builtins.fetchTarball` and add it to your `imports`.
+2. **Configure Options:** Enable and configure the desired features under the `cloonar-assistant` attribute set.
+
+```nix
+# /etc/nixos/configuration.nix
+{ config, pkgs, ... }:
+let
+ # Fetch the source code for the cloonar-assistant module
+ cloonar-assistant-src = builtins.fetchTarball "https://git.cloonar.com/Cloonar/cloonar-assistant/archive/master.tar.gz";
+in
+{
+ imports = [
+ # Import the main module
+ "${cloonar-assistant-src}/modules/cloonar-assistant"
+
+ # Include your hardware-configuration.nix and other custom modules
+ ./hardware-configuration.nix
+ # ...
+ ];
+
+ # --- Configure Cloonar Assistant Options ---
+ cloonar-assistant = {
+ # Required: Define the first two octets for your internal networks
+ networkPrefix = "10.42"; # Example: Results in 10.42.96.0/24, 10.42.97.0/24, etc.
+
+ # Required: Define the domain name for local services and DDNS
+ domain = "home.example.com"; # Example
+
+ # Required: Define the network interface connected to the WAN/Internet
+ firewall.interfaces.wan = "eth0"; # Example
+
+ # Required: Define the network interface for internal VLANs
+ # Set to null if you only have one interface (WAN)
+ firewall.interfaces.internal = "eth1"; # Example
+
+ # Enable VPN Server
+ vpn.enable = true;
+ vpn.privateKeyFile = "/path/to/your/wireguard_private_key"; # Store securely!
+ vpn.clients = [
+ { name = "myphone"; publicKey = "..."; allowedIPs = [ "${config.cloonar-assistant.networkPrefix}.98.2/32" ]; }
+ # ... more clients
+ ];
+
+ # Enable Dynamic DNS Updates
+ updns.enable = true;
+ updns.key = "your-updns-key"; # Key provided by updns-client.cloonar.com
+ updns.secretFile = "/path/to/your/updns_secret"; # Store securely!
+
+ # Enable setup mode (allows WAN access for initial setup - disable for production)
+ setup = false;
+
+ # ... other options can be configured as needed.
+ };
+
+ # --- Other System Configuration ---
+ networking.hostName = "myrouter"; # Example hostname
+
+ # Ensure necessary packages for fetching are available if not using flakes
+ environment.systemPackages = [ pkgs.nix ];
+
+ system.stateVersion = "23.11"; # Set to your NixOS version
+}
+```
+
+## Module Options
+
+The main configuration options are available under the `cloonar-assistant` attribute set in your `configuration.nix`. Refer to `modules/cloonar-assistant/default.nix` within the fetched source for a detailed definition of all available options and their defaults. Key areas include:
+
+* `cloonar-assistant.networkPrefix`
+* `cloonar-assistant.domain`
+* `cloonar-assistant.firewall.*` (including `interfaces.wan`, `interfaces.internal`)
+* `cloonar-assistant.vpn.*`
+* `cloonar-assistant.updns.*`
+* `cloonar-assistant.setup`
+
+## ISO Build Utility (Optional)
+
+This repository also includes configuration in the `iso/` directory to build a bootable `.iso` file.
+
+**Purpose:** This ISO performs a fully automated, minimal NixOS installation on a target machine. It partitions the disk (creating a boot partition and a LUKS-encrypted root partition using `/dev/zero` as the keyfile - **WARNING: This is insecure and intended only for bootstrapping**), formats filesystems, and installs a base NixOS system defined in `iso/configuration.nix`. It does **not** install the `cloonar-assistant` configuration itself. It's primarily useful for quickly bootstrapping a bare-metal machine before applying your custom NixOS configuration (which imports the `cloonar-assistant` module).
+
+**Build Command (Non-Flake):**
+
+```bash
+nix-build ./iso/default.nix
+```
+
+This command should be run from the root of this repository checkout. The resulting ISO will be in the `result/` directory.
+
+**ISO Installation Flow:**
+
+```mermaid
+graph TD
+ subgraph "ISO Build & Install Utility"
+ A["Run nix-build command"] --> B("Generates ISO file");
+ B --> C{"Boot target machine from ISO"};
+ C --> D["Automated Install Script runs"];
+ D --> E{"Partitions Disk (BOOT + LUKS)"};
+ E --> F{"Formats Filesystems (FAT32 + EXT4)"};
+ F --> G{"Mounts Filesystems"};
+ G --> H["Installs base NixOS using iso/configuration.nix"];
+ H --> I("System reboots into minimal NixOS");
+ I --> J{"User creates custom config
importing cloonar-assistant module"};
+ J --> K["Run nixos-rebuild switch"];
+ K --> L("System running full configuration");
+ end
+```
+
+## Network Architecture
+
+The module sets up the following logical network structure when the firewall and VLANs are enabled:
+
+```mermaid
+graph LR
+ subgraph "Network Architecture"
+ Internet -- WAN --> R("Router/Firewall");
+ subgraph "Internal Network (VLANs on configured internal interface)"
+ R -- "VLAN 96" --> LAN["LAN Subnet
${config.cloonar-assistant.networkPrefix}.96.0/24"];
+ R -- "VLAN 97" --> SRV["Server Subnet
${config.cloonar-assistant.networkPrefix}.97.0/24"];
+ R -- "VLAN 101" --> INF["Infrastructure Subnet
${config.cloonar-assistant.networkPrefix}.101.0/24"];
+ R -- "VLAN 99" --> MM["Multimedia Subnet
${config.cloonar-assistant.networkPrefix}.99.0/24"];
+ R -- "VLAN 100" --> SMT["Smart Home Subnet
${config.cloonar-assistant.networkPrefix}.100.0/24"];
+ R -- "VLAN 254" --> GST["Guest Subnet
${config.cloonar-assistant.networkPrefix}.254.0/24"];
+ end
+ subgraph "VPN"
+ VPNClient -- "UDP 51820" --> R;
+ R -- "wg_cloonar" --> VPNNet["VPN Subnet
${config.cloonar-assistant.networkPrefix}.98.0/24"];
+ end
+ subgraph "Services on Router"
+ R --> DHCP("Kea DHCP");
+ R --> DNS("Unbound DNS");
+ R --> Firewall("NFTables");
+ R --> DynDNS("UpDNS Client");
+ R --> HAProxy("Nginx Proxy");
+ HAProxy --> HACont[("Container: Home Assistant")];
+ end
+ SRV --> HACont;
+ end
+```
+
+## Module Overview
+
+* `modules/cloonar-assistant/`: Contains the main NixOS module definition (`default.nix`) and imports submodules.
+* `modules/cloonar-assistant/networking/`: Configures network interfaces, VLANs, DHCP (Kea), DNS (Unbound), firewall (`nftables`), and WireGuard.
+* `modules/cloonar-assistant/updns/`: Configures the dynamic DNS client service.
+* `modules/cloonar-assistant/home-assistant/`: Configures the Home Assistant systemd container, MariaDB database, Nginx reverse proxy, and ACME SSL certificates.
+* `modules/cloonar-assistant/multiroom-audio/`: Currently unused, planned for future multiroom audio features.
+* `iso/`: Contains configuration files (`default.nix`, `configuration.nix`) for building the optional automated installation ISO.
diff --git a/iso/configuration.nix b/iso/configuration.nix
new file mode 100644
index 0000000..755b64d
--- /dev/null
+++ b/iso/configuration.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }: {
+ imports = [
+
+
+ ];
+
+ nixpkgs.config.allowUnfree = true;
+
+ zramSwap.enable = true;
+
+ security.sudo.wheelNeedsPassword = false;
+
+ networking.hostName = "install";
+
+ services.openssh.enable = true;
+ services.openssh.settings.PermitRootLogin = "yes";
+
+ users.mutableUsers = false;
+ users.users.root = {
+ # Password is "linux"
+ hashedPassword = lib.mkForce "$6$7IKExnDde920x.YH$ggegnnKJYdmg1Wt33fxuPpM.MmIaX32LXVyjL8ed7ohT385lKotFGzRpitncQ3pd9Lci1QCFGRn2tVJGxkFAm0";
+ };
+
+ services.avahi = {
+ enable = true;
+ ipv4 = true;
+ ipv6 = true;
+ nssmdns = true;
+ publish = { enable = true; domain = true; addresses = true; };
+ };
+
+ environment.systemPackages = with pkgs; [
+ coreutils
+ curl
+ file
+ git
+ htop
+ lsof
+ nano
+ openssl
+ pciutils
+ pv
+ tmux
+ tree
+ unar
+ vim_configurable
+ wget
+ zip
+ ];
+
+ system.stateVersion = "23.05"; # Did you read the comment?
+}
diff --git a/iso/default.nix b/iso/default.nix
new file mode 100644
index 0000000..2f87220
--- /dev/null
+++ b/iso/default.nix
@@ -0,0 +1,86 @@
+{
+ system ? "x86_64-linux",
+}:
+(import {
+ inherit system;
+ modules = [
+
+ ./configuration.nix
+ ({ config, pkgs, lib, ... }: {
+ systemd.services.install = {
+ description = "Bootstrap a NixOS installation";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" "polkit.service" ];
+ path = [ "/run/current-system/sw/" ];
+ script = with pkgs; ''
+ echo 'journalctl -fb -n100 -uinstall' >>~nixos/.bash_history
+
+ set -eux
+
+ wait-for() {
+ for _ in seq 10; do
+ if $@; then
+ break
+ fi
+ sleep 1
+ done
+ }
+
+ dev=/dev/sda
+ [ -b /dev/nvme0n1 ] && dev=/dev/nvme0n1
+ [ -b /dev/vda ] && dev=/dev/vda
+
+ ${utillinux}/bin/sfdisk --wipe=always $dev <<-END
+ label: gpt
+
+ name=BOOT, size=512MiB, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
+ name=NIXOS
+ END
+
+ sync
+ wait-for [ -b /dev/disk/by-partlabel/BOOT ]
+
+ mkfs.fat -F 32 -n boot /dev/disk/by-partlabel/BOOT
+ wait-for mkfs.fat -F 32 -n boot /dev/disk/by-partlabel/BOOT
+
+ wait-for [ -b /dev/disk/by-partlabel/NIXOS ]
+ ${cryptsetup}/bin/cryptsetup luksFormat --type=luks2 --label=root /dev/disk/by-partlabel/NIXOS /dev/zero --keyfile-size=1
+ ${cryptsetup}/bin/cryptsetup luksOpen /dev/disk/by-partlabel/NIXOS root --key-file=/dev/zero --keyfile-size=1
+ mkfs.ext4 -L nixos /dev/mapper/root
+
+ sync
+ mount /dev/mapper/root /mnt
+
+ mkdir /mnt/boot
+ wait-for mount /dev/disk/by-label/boot /mnt/boot
+
+ install -D ${./configuration.nix} /mnt/etc/nixos/configuration.nix
+ install -D ${./hardware-configuration.nix} /mnt/etc/nixos/hardware-configuration.nix
+
+ sed -i -E 's/(\w*)#installer-only /\1/' /mnt/etc/nixos/*
+
+ ${config.system.build.nixos-install}/bin/nixos-install \
+ --system ${(import {
+ inherit system;
+ modules = [
+ ./configuration.nix
+ ./hardware-configuration.nix
+ ];
+ }).config.system.build.toplevel} \
+ --no-root-passwd \
+ --cores 0
+
+ echo 'Shutting off in 1min'
+ ${systemd}/bin/shutdown +1
+ '';
+ environment = config.nix.envVars // {
+ inherit (config.environment.sessionVariables) NIX_PATH;
+ HOME = "/root";
+ };
+ serviceConfig = {
+ Type = "oneshot";
+ };
+ };
+ })
+ ];
+}).config.system.build.isoImage
diff --git a/iso/hardware-configuration.nix b/iso/hardware-configuration.nix
new file mode 100644
index 0000000..ee3a8b6
--- /dev/null
+++ b/iso/hardware-configuration.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, ... }: {
+ boot.loader.systemd-boot.enable = true;
+
+ fileSystems."/boot" = {
+ device = "/dev/disk/by-label/boot";
+ fsType = "vfat";
+ };
+
+ boot.initrd.luks.devices.root = {
+ device = "/dev/disk/by-label/root";
+
+ # WARNING: Leaks some metadata, see cryptsetup man page for --allow-discards.
+ allowDiscards = true;
+
+ # Set your own key with:
+ # cryptsetup luksChangeKey /dev/disk/by-label/root --key-file=/dev/zero --keyfile-size=1
+ # You can then delete the rest of this block.
+ keyFile = "/dev/zero";
+ keyFileSize = 1;
+ };
+
+ fileSystems."/" = {
+ device = "/dev/mapper/root";
+ fsType = "ext4";
+ };
+}