├── .envrc ├── .github ├── assets │ ├── banner_dark.svg │ ├── banner_light.svg │ ├── logo_dark.svg │ └── logo_light.svg └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── .vitepress │ ├── config.mts │ └── theme │ │ ├── index.ts │ │ └── style.css ├── package.json ├── pnpm-lock.yaml └── src │ ├── configurations │ ├── introduction.md │ └── structure.md │ ├── getting_started │ ├── first_modules.md │ ├── initialization.md │ ├── introduction.md │ └── transfer_to_denix.md │ ├── hosts │ ├── examples.md │ ├── introduction.md │ └── structure.md │ ├── index.md │ ├── modules │ ├── examples.md │ ├── introduction-nixos.md │ ├── introduction.md │ └── structure.md │ ├── options │ └── introduction.md │ ├── public │ ├── google87e02993e173d8bf.html │ └── robots.txt │ ├── real-configurations.md │ ├── rices │ ├── examples.md │ ├── introduction.md │ └── structure.md │ ├── ru │ ├── configurations │ │ ├── introduction.md │ │ └── structure.md │ ├── getting_started │ │ ├── first_modules.md │ │ ├── initialization.md │ │ ├── introduction.md │ │ └── transfer_to_denix.md │ ├── hosts │ │ ├── examples.md │ │ ├── introduction.md │ │ └── structure.md │ ├── index.md │ ├── modules │ │ ├── examples.md │ │ ├── introduction-nixos.md │ │ ├── introduction.md │ │ └── structure.md │ ├── options │ │ └── introduction.md │ ├── real-configurations.md │ ├── rices │ │ ├── examples.md │ │ ├── introduction.md │ │ └── structure.md │ └── troubleshooting.md │ └── troubleshooting.md ├── flake.lock ├── flake.nix ├── lib ├── attrset.nix ├── configurations │ ├── apply.nix │ ├── default.nix │ ├── host.nix │ ├── module.nix │ └── rice.nix ├── default.nix ├── options.nix └── umport.nix └── templates ├── minimal-no-rices ├── flake.nix ├── hosts │ └── desktop │ │ ├── default.nix │ │ └── hardware.nix └── modules │ └── config │ ├── constants.nix │ ├── home.nix │ ├── hosts.nix │ └── user.nix └── minimal ├── flake.nix ├── hosts └── desktop │ ├── default.nix │ └── hardware.nix ├── modules └── config │ ├── constants.nix │ ├── home.nix │ ├── hosts.nix │ ├── rices.nix │ └── user.nix └── rices └── dark └── default.nix /.envrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | use flake 3 | -------------------------------------------------------------------------------- /.github/assets/banner_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | logo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/assets/banner_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | logo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/assets/logo_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/assets/logo_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy documentation to Pages 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - docs/** 8 | 9 | workflow_dispatch: 10 | 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | concurrency: 17 | group: pages 18 | cancel-in-progress: false 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | defaults: 24 | run: 25 | working-directory: ./docs 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - uses: pnpm/action-setup@v3 32 | with: 33 | package_json_file: docs/package.json 34 | version: 9 35 | - name: Setup Node 36 | uses: actions/setup-node@v4 37 | with: 38 | node-version: 22 39 | cache: pnpm 40 | cache-dependency-path: docs/pnpm-lock.yaml 41 | - name: Setup Pages 42 | uses: actions/configure-pages@v4 43 | - name: Install dependencies 44 | run: pnpm install 45 | - name: Build with VitePress 46 | run: pnpm docs:build 47 | - name: Upload artifact 48 | uses: actions/upload-pages-artifact@v3 49 | with: 50 | path: docs/.vitepress/dist 51 | 52 | deploy: 53 | environment: 54 | name: github-pages 55 | url: ${{ steps.deployment.outputs.page_url }} 56 | needs: build 57 | runs-on: ubuntu-latest 58 | name: Deploy 59 | steps: 60 | - name: Deploy to GitHub Pages 61 | id: deployment 62 | uses: actions/deploy-pages@v4 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/.vitepress/cache 3 | **/.vitepress/dist 4 | /.pre-commit-config.yaml 5 | .direnv 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-2025 yunfachi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 | Denix 7 | 8 | 9 |

10 | 11 | Denix is a Nix library designed to help you build scalable configurations for [NixOS](https://nixos.org/), [Home Manager](https://github.com/nix-community/home-manager), and [Nix-Darwin](https://github.com/nix-darwin/nix-darwin). 12 | 13 | ## Documentation 14 | 15 | You can find the documentation here: [Denix Documentation](https://yunfachi.github.io/denix/getting_started/introduction) 16 | 17 | ## Key Features 18 | 19 | ### Modular System 20 | Custom modules allow you to define options and related configurations in a flexible way, simplifying the management of your entire system. 21 | 22 | ### Hosts and Rices 23 | * **Hosts**: Unique configurations tailored for each machine. 24 | * **Rices**: Customizations that can be applied to all hosts. 25 | 26 | ### Unified NixOS, Home Manager, and Nix-Darwin Configurations 27 | Write your NixOS, Home Manager, and Nix-Darwin configurations in a single file*, and Denix will automatically handle the separation for you. 28 | 29 | ## Templates 30 | 31 | ### [minimal](./templates/minimal/) (recommended) 32 | Hosts, rices, and initial modules for quick setup: 33 | ```sh 34 | nix flake init -t github:yunfachi/denix#minimal 35 | ``` 36 | 37 | ### [minimal-no-rices](./templates/minimal-no-rices/) 38 | Hosts and initial modules without rices: 39 | ```sh 40 | nix flake init -t github:yunfachi/denix#minimal-no-rices 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitepress" 2 | 3 | // https://vitepress.dev/reference/site-config 4 | export default defineConfig({ 5 | cleanUrls: true, 6 | lastUpdated: true, 7 | srcDir: './src', 8 | base: '/denix/', // github pages 9 | ignoreDeadLinks: [ 10 | '/TODO' 11 | ], 12 | sitemap: { 13 | hostname: 'https://yunfachi.github.io/denix/' 14 | }, 15 | head: [ 16 | ["link", { rel: "icon", href: "https://raw.githubusercontent.com/yunfachi/denix/master/.github/assets/logo_dark.svg" }], 17 | ], 18 | themeConfig: { 19 | logo: { 20 | light: "https://raw.githubusercontent.com/yunfachi/denix/master/.github/assets/logo_light.svg", 21 | dark: "https://raw.githubusercontent.com/yunfachi/denix/master/.github/assets/logo_dark.svg", 22 | }, 23 | // https://vitepress.dev/reference/default-theme-config 24 | socialLinks: [ 25 | { icon: "github", link: "https://github.com/yunfachi/denix" } 26 | ], 27 | search: { 28 | provider: "local", 29 | } 30 | }, 31 | locales: { 32 | root: themeConfigEnglish(), 33 | ru: themeConfigRussian() 34 | } 35 | }) 36 | 37 | function themeConfigEnglish() {return { 38 | label: "English", 39 | lang: "en", 40 | link: "/", 41 | title: "Denix Documentation", 42 | description: "Nix library for creating scalable NixOS, Home Manager, and Nix-Darwin configurations with modules, hosts, and rices", 43 | 44 | themeConfig: { 45 | editLink: { 46 | pattern: "https://github.com/yunfachi/denix/edit/master/docs/src/:path", 47 | text: "Edit this page on Github" 48 | }, 49 | 50 | nav: [ 51 | { text: "Home", link: "/" }, 52 | { text: "Introduction", link: "/getting_started/introduction" } 53 | ], 54 | 55 | sidebar: [ 56 | { 57 | text: "Getting Started", 58 | items: [ 59 | { text: "Introduction", link: "/getting_started/introduction" }, 60 | { text: "Initialization", link: "/getting_started/initialization" }, 61 | { text: "First Modules", link: "/getting_started/first_modules" }, 62 | { text: "Transfer to Denix", link: "/getting_started/transfer_to_denix" }, 63 | ], 64 | }, 65 | { 66 | text: "Modules", 67 | items: [ 68 | { text: "Introduction to NixOS Modules", link: "/modules/introduction-nixos" }, 69 | { text: "Introduction", link: "/modules/introduction" }, 70 | { text: "Structure", link: "/modules/structure" }, 71 | { text: "Examples", link: "/modules/examples" } 72 | ], 73 | }, 74 | { 75 | text: "Options", 76 | items: [ 77 | { text: "Introduction", link: "/options/introduction" }, 78 | ], 79 | }, 80 | { 81 | text: "Hosts", 82 | items: [ 83 | { text: "Introduction", link: "/hosts/introduction" }, 84 | { text: "Structure", link: "/hosts/structure" }, 85 | { text: "Examples", link: "/hosts/examples" } 86 | ], 87 | }, 88 | { 89 | text: "Configurations (flakes)", 90 | items: [ 91 | { text: "Introduction", link: "/configurations/introduction" }, 92 | { text: "Structure", link: "/configurations/structure" } 93 | ], 94 | }, 95 | { 96 | text: "Rices", 97 | items: [ 98 | { text: "Introduction", link: "/rices/introduction" }, 99 | { text: "Structure", link: "/rices/structure" }, 100 | { text: "Examples", link: "/rices/examples" } 101 | ], 102 | }, 103 | { text: "Common Errors", link: "/troubleshooting" }, 104 | { text: "Real Configurations", link: "/real-configurations" }, 105 | ], 106 | } 107 | }} 108 | 109 | function themeConfigRussian() {return { 110 | label: "Русский", 111 | lang: "ru", 112 | link: "/ru/", 113 | title: "Denix Документация", 114 | description: "Библиотека Nix для создания масштабируемых конфигураций NixOS, Home Manager и Nix-Darwin с модулями, хостами и райсами", 115 | 116 | themeConfig: { 117 | editLink: { 118 | pattern: "https://github.com/yunfachi/denix/edit/master/docs/src/:path", 119 | text: "Редактировать эту страницу на GitHub" 120 | }, 121 | 122 | nav: [ 123 | { text: "Главная", link: "/ru/" }, 124 | { text: "Вступление", link: "/ru/getting_started/introduction" } 125 | ], 126 | 127 | sidebar: [ 128 | { 129 | text: "Начнем", 130 | items: [ 131 | { text: "Вступление", link: "/ru/getting_started/introduction" }, 132 | { text: "Инициализация", link: "/ru/getting_started/initialization" }, 133 | { text: "Первые модули", link: "/ru/getting_started/first_modules" }, 134 | { text: "Перенос на Denix", link: "/ru/getting_started/transfer_to_denix" }, 135 | ], 136 | }, 137 | { 138 | text: "Модули", 139 | items: [ 140 | { text: "Вступление в модули NixOS", link: "/ru/modules/introduction-nixos" }, 141 | { text: "Вступление", link: "/ru/modules/introduction" }, 142 | { text: "Структура", link: "/ru/modules/structure" }, 143 | { text: "Примеры", link: "/ru/modules/examples" }, 144 | ], 145 | }, 146 | { 147 | text: "Опции", 148 | items: [ 149 | { text: "Вступление", link: "/ru/options/introduction" }, 150 | ], 151 | }, 152 | { 153 | text: "Хосты", 154 | items: [ 155 | { text: "Вступление", link: "/ru/hosts/introduction" }, 156 | { text: "Структура", link: "/ru/hosts/structure" }, 157 | { text: "Примеры", link: "/ru/hosts/examples" }, 158 | ], 159 | }, 160 | { 161 | text: "Конфигурации (флейки)", 162 | items: [ 163 | { text: "Вступление", link: "/ru/configurations/introduction" }, 164 | { text: "Структура", link: "/ru/configurations/structure" }, 165 | ], 166 | }, 167 | { 168 | text: "Райсы", 169 | items: [ 170 | { text: "Вступление", link: "/ru/rices/introduction" }, 171 | { text: "Структура", link: "/ru/rices/structure" }, 172 | { text: "Примеры", link: "/ru/rices/examples" }, 173 | ], 174 | }, 175 | { text: "Распространённые ошибки", link: "/ru/troubleshooting" }, 176 | { text: "Реальные конфигурации", link: "/ru/real-configurations" }, 177 | ], 178 | } 179 | }} 180 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import { h } from 'vue' 3 | import type { Theme } from 'vitepress' 4 | import DefaultTheme from 'vitepress/theme' 5 | import './style.css' 6 | 7 | export default { 8 | extends: DefaultTheme, 9 | Layout: () => { 10 | return h(DefaultTheme.Layout, null, { 11 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 12 | }) 13 | }, 14 | enhanceApp({ app, router, siteData }) { 15 | // ... 16 | } 17 | } satisfies Theme 18 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Customize default theme styling by overriding CSS variables: 3 | * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css 4 | */ 5 | 6 | /** 7 | * Colors 8 | * 9 | * Each colors have exact same color scale system with 3 levels of solid 10 | * colors with different brightness, and 1 soft color. 11 | * 12 | * - `XXX-1`: The most solid color used mainly for colored text. It must 13 | * satisfy the contrast ratio against when used on top of `XXX-soft`. 14 | * 15 | * - `XXX-2`: The color used mainly for hover state of the button. 16 | * 17 | * - `XXX-3`: The color for solid background, such as bg color of the button. 18 | * It must satisfy the contrast ratio with pure white (#ffffff) text on 19 | * top of it. 20 | * 21 | * - `XXX-soft`: The color used for subtle background such as custom container 22 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors 23 | * on top of it. 24 | * 25 | * The soft color must be semi transparent alpha channel. This is crucial 26 | * because it allows adding multiple "soft" colors on top of each other 27 | * to create a accent, such as when having inline code block inside 28 | * custom containers. 29 | * 30 | * - `default`: The color used purely for subtle indication without any 31 | * special meanings attched to it such as bg color for menu hover state. 32 | * 33 | * - `brand`: Used for primary brand colors, such as link text, button with 34 | * brand theme, etc. 35 | * 36 | * - `tip`: Used to indicate useful information. The default theme uses the 37 | * brand color for this by default. 38 | * 39 | * - `warning`: Used to indicate warning to the users. Used in custom 40 | * container, badges, etc. 41 | * 42 | * - `danger`: Used to show error, or dangerous message to the users. Used 43 | * in custom container, badges, etc. 44 | * -------------------------------------------------------------------------- */ 45 | 46 | :root { 47 | --vp-c-default-1: var(--vp-c-gray-1); 48 | --vp-c-default-2: var(--vp-c-gray-2); 49 | --vp-c-default-3: var(--vp-c-gray-3); 50 | --vp-c-default-soft: var(--vp-c-gray-soft); 51 | 52 | --vp-c-tip-1: var(--vp-c-brand-1); 53 | --vp-c-tip-2: var(--vp-c-brand-2); 54 | --vp-c-tip-3: var(--vp-c-brand-3); 55 | --vp-c-tip-soft: var(--vp-c-brand-soft); 56 | 57 | --vp-c-warning-1: var(--vp-c-yellow-1); 58 | --vp-c-warning-2: var(--vp-c-yellow-2); 59 | --vp-c-warning-3: var(--vp-c-yellow-3); 60 | --vp-c-warning-soft: var(--vp-c-yellow-soft); 61 | 62 | --vp-c-danger-1: var(--vp-c-red-1); 63 | --vp-c-danger-2: var(--vp-c-red-2); 64 | --vp-c-danger-3: var(--vp-c-red-3); 65 | --vp-c-danger-soft: var(--vp-c-red-soft); 66 | } 67 | 68 | /** 69 | * Component: Button 70 | * -------------------------------------------------------------------------- */ 71 | 72 | :root { 73 | --vp-button-brand-border: transparent; 74 | --vp-button-brand-text: var(--vp-c-white); 75 | --vp-button-brand-bg: var(--vp-c-brand-3); 76 | --vp-button-brand-hover-border: transparent; 77 | --vp-button-brand-hover-text: var(--vp-c-white); 78 | --vp-button-brand-hover-bg: var(--vp-c-brand-2); 79 | --vp-button-brand-active-border: transparent; 80 | --vp-button-brand-active-text: var(--vp-c-white); 81 | --vp-button-brand-active-bg: var(--vp-c-brand-1); 82 | } 83 | 84 | /** 85 | * Component: Home 86 | * -------------------------------------------------------------------------- */ 87 | 88 | :root { 89 | --vp-home-hero-name-color: transparent; 90 | --vp-home-hero-name-background: -webkit-linear-gradient( 91 | 120deg, 92 | #95b695 30%, 93 | #6f866f 94 | ); 95 | 96 | --vp-home-hero-image-filter: blur(44px); 97 | } 98 | 99 | .dark { 100 | --vp-home-hero-image-background-image: linear-gradient( 101 | -45deg, 102 | #6f866f 50%, 103 | #6f866f 50% 104 | ); 105 | 106 | --vp-c-brand-1: #95b695; 107 | --vp-c-brand-2: #869e86; 108 | --vp-c-brand-3: #6f866f; 109 | --vp-c-brand-soft: var(--vp-c-green-soft); 110 | } 111 | 112 | html:not(.dark) { 113 | --vp-home-hero-image-background-image: linear-gradient( 114 | -45deg, 115 | #a7caa7 50%, 116 | #a7caa7 50% 117 | ); 118 | 119 | --vp-c-brand-1: #6f866f; 120 | --vp-c-brand-2: #869e86; 121 | --vp-c-brand-3: #95b695; 122 | --vp-c-brand-soft: var(--vp-c-green-soft); 123 | } 124 | 125 | @media (min-width: 640px) { 126 | :root { 127 | --vp-home-hero-image-filter: blur(56px); 128 | } 129 | } 130 | 131 | @media (min-width: 960px) { 132 | :root { 133 | --vp-home-hero-image-filter: blur(68px); 134 | } 135 | } 136 | 137 | /** 138 | * Component: Custom Block 139 | * -------------------------------------------------------------------------- */ 140 | 141 | :root { 142 | --vp-custom-block-tip-border: transparent; 143 | --vp-custom-block-tip-text: var(--vp-c-text-1); 144 | --vp-custom-block-tip-bg: var(--vp-c-brand-soft); 145 | --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); 146 | } 147 | 148 | /** 149 | * Component: Algolia 150 | * -------------------------------------------------------------------------- */ 151 | 152 | .DocSearch { 153 | --docsearch-primary-color: var(--vp-c-brand-1) !important; 154 | } 155 | 156 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "vitepress": "^1.6.3" 4 | }, 5 | "scripts": { 6 | "docs:dev": "vitepress dev", 7 | "docs:build": "vitepress build", 8 | "docs:preview": "vitepress preview" 9 | } 10 | } -------------------------------------------------------------------------------- /docs/src/configurations/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction to Denix Configurations (Flakes) {#introduction} 2 | The `delib.configurations` function is used to create lists of `nixosConfigurations`, `homeConfigurations`, and `darwinConfigurations` for flakes. 3 | 4 | In addition to all hosts, it also adds combinations of each host with every **non-`inheritanceOnly`** rice, which allows for quickly switching between rice configurations without editing the code. For example, if the "desktop" host is set to use the "light" rice, executing the following command: 5 | 6 | ```sh 7 | nixos-rebuild switch --flake .#desktop --use-remote-sudo 8 | ``` 9 | 10 | will use the "desktop" host with the "light" rice. However, if you need to quickly switch to another rice, for example, "dark" you can run the following command: 11 | 12 | ```sh 13 | nixos-rebuild switch --flake .#desktop-dark --use-remote-sudo 14 | ``` 15 | 16 | In this case, the host remains "desktop", but the rice changes to "dark". 17 | 18 | It is important to note that when switching rice in this way, only the value of the `${myConfigName}.rice` option changes, while the value of `${myConfigName}.hosts.${hostName}.rice` remains the same. 19 | 20 | ## Principle of Configuration List Generation {#principle} 21 | The configuration list is generated based on the following principle: 22 | 23 | - `{hostName}` - where `hostName` is the name of any host. 24 | - `{hostName}-{riceName}` - where `hostName` is the name of any host, and `riceName` is the name of any rice where `inheritanceOnly` is `false`. 25 | 26 | If `moduleSystem` from the [function arguments](/configurations/structure#function-arguments) is set to `home`, then a prefix of `{homeManagerUser}@` is added to all configurations in the list. 27 | 28 | ## Example {#example} 29 | An example of a flake's `outputs` for `nixosConfigurations`, `homeConfigurations`, and `darwinConfigurations`: 30 | 31 | ```nix 32 | outputs = {denix, nixpkgs, ...} @ inputs: let 33 | mkConfigurations = moduleSystem: 34 | denix.lib.configurations rec { 35 | inherit moduleSystem; 36 | homeManagerUser = "sjohn"; 37 | 38 | paths = [./hosts ./modules ./rices]; 39 | 40 | specialArgs = { 41 | inherit inputs moduleSystem homeManagerUser; 42 | }; 43 | }; 44 | in { 45 | nixosConfigurations = mkConfigurations "nixos"; 46 | homeConfigurations = mkConfigurations "home"; 47 | darwinConfigurations = mkConfigurations "darwin"; 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/src/configurations/structure.md: -------------------------------------------------------------------------------- 1 | # Structure {#structure} 2 | 3 | ## Function Arguments {#function-arguments} 4 | - `myconfigName` (string): the category for all Denix module options, hosts, and rices. Default is `myconfig`; changes are not recommended. 5 | - `denixLibName` (string): the name of the Denix library in `specialArgs` (`{denixLibName, ...}: denixLibName.module { ... }`). Default is `delib`; changes are not recommended. 6 | - `homeManagerNixpkgs` (nixpkgs): used in the `pkgs` attribute of the `home-manager.lib.homeManagerConfiguration` function in the format: `homeManagerNixpkgs.legacyPackages.${host :: homeManagerSystem}`. By default, it takes `nixpkgs` from the flake, so if you've set `inputs.denix.inputs.nixpkgs.follows = "nixpkgs";`, specifying `homeManagerNixpkgs` is typically unnecessary. 7 | - `homeManagerUser` (string): the username, used in `home-manager.users.${homeManagerUser}` and for generating the Home Manager configuration list. 8 | - `moduleSystem` ("nixos", "home", and "darwin"): specifies which module system the configuration list should be generated for - NixOS, Home Manager, or Nix-Darwin. 9 | - `paths` (listOf string): paths to be imported; add hosts, rices, and modules here. Default is `[]`. 10 | - `exclude` (listOf string): paths to be excluded from importing. Default is `[]`. 11 | - `recursive` (boolean): determines whether to recursively search for paths to import. Default is `true`. 12 | - `specialArgs` (attrset): `specialArgs` to be passed to `lib.nixosSystem`, `home-manager.lib.homeManagerConfiguration`, and `nix-darwin.lib.darwinSystem`. Default is `{}`. 13 | - **EXPERIMENTAL** `extraModules` (list): default is `[]`. 14 | - **EXPERIMENTAL** `mkConfigurationsSystemExtraModule` (attrset): a module used in the internal NixOS configuration that receives the list of hosts and rices to generate the configuration list. Default is `{nixpkgs.hostPlatform = "x86_64-linux";}`. 15 | 16 | ## Pseudocode {#pseudocode} 17 | ```nix 18 | delib.configurations { 19 | myconfigName = "myconfig"; 20 | denixLibName = "delib"; 21 | homeManagerNixpkgs = inputs.nixpkgs; 22 | homeManagerUser = "sjohn"; 23 | moduleSystem = "nixos"; 24 | paths = [./modules ./hosts ./rices]; 25 | exclude = [./modules/deprecated]; 26 | recursive = true; 27 | specialArgs = { 28 | inherit inputs; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/getting_started/first_modules.md: -------------------------------------------------------------------------------- 1 | # First Modules {#first-modules} 2 | In this section, we will create modules for some programs. To start, create the `programs` and `services` subdirectories in the `modules` directory. 3 | 4 | Creating your own modules is almost the same as creating regular NixOS modules, so you can look for NixOS options [here](https://search.nixos.org/options?), Home Manager options [here](https://home-manager-options.extranix.com/), and Nix-Darwin options [here](https://nix-darwin.github.io/nix-darwin/manual/index.html). 5 | 6 | ## Git {#git} 7 | Let's assume you already have a constants module. Create a file `modules/programs/git.nix` with the following content: 8 | ```nix 9 | {delib, ...}: 10 | delib.module { 11 | name = "programs.git"; 12 | 13 | options.programs.git = with delib; { 14 | enable = boolOption true; 15 | enableLFS = boolOption true; 16 | }; 17 | 18 | home.ifEnabled.programs.git = {myconfig, cfg, ...}: { 19 | enable = cfg.enable; 20 | lfs.enable = cfg.enableLFS; 21 | 22 | userName = myconfig.constants.username; 23 | userEmail = myconfig.constants.useremail; 24 | }; 25 | } 26 | ``` 27 | 28 | ### Code Explanation: 29 | - `enable` - an option to enable or disable the module entirely. 30 | - `enableLFS` - an option to enable [Git Large File Storage](https://github.com/git-lfs/git-lfs). 31 | 32 | ## Hyprland {#hyprland} 33 | Assume that your host has the options `type` and `isDesktop`. Create a subdirectory `hyprland` in the `modules/programs/` directory, and in it, create a file `default.nix` with the following content: 34 | ```nix 35 | {delib, ...}: 36 | delib.module { 37 | name = "programs.hyprland"; 38 | 39 | options = {myconfig, ...} @ args: delib.singleEnableOption myconfig.host.isDesktop args; 40 | 41 | nixos.ifEnabled.programs.hyprland.enable = true; 42 | home.ifEnabled.wayland.windowManager.hyprland.enable = true; 43 | } 44 | ``` 45 | 46 | Also, create a file `settings.nix` in this directory: 47 | ```nix 48 | {delib, ...}: 49 | delib.module { 50 | name = "programs.hyprland"; 51 | 52 | home.ifEnabled.wayland.windowManager.hyprland.settings = { 53 | "$mod" = "SUPER"; 54 | 55 | general = { 56 | gaps_in = 5; 57 | gaps_out = 10; 58 | }; 59 | }; 60 | } 61 | ``` 62 | 63 | And finally, the file `binds.nix`: 64 | ```nix 65 | {delib, ...}: 66 | delib.module { 67 | name = "programs.hyprland"; 68 | 69 | home.ifEnabled.wayland.windowManager.hyprland.settings = { 70 | bindm = [ 71 | "$mod, mouse:272, movewindow" 72 | "$mod, mouse:273, resizewindow" 73 | ]; 74 | 75 | bind = [ 76 | "$mod, q, killactive," 77 | "CTRLALT, Delete, exit," 78 | 79 | "$mod, Return, exec, kitty" # your terminal 80 | ]; 81 | }; 82 | } 83 | ``` 84 | 85 | We have created a small configuration for Hyprland, which you can expand and add new options to as needed. 86 | -------------------------------------------------------------------------------- /docs/src/getting_started/initialization.md: -------------------------------------------------------------------------------- 1 | # Configuration Initialization {#initialization} 2 | This section will describe creating the `minimal` template from scratch. 3 | 4 | You do not have to do this; you can simply clone the minimal configuration template with the following command: 5 | ```sh 6 | nix flake init -t github:yunfachi/denix#minimal 7 | ``` 8 | 9 | You can also clone the minimal configuration template without the rices: 10 | ```sh 11 | nix flake init -t github:yunfachi/denix#minimal-no-rices 12 | ``` 13 | 14 | ## Flake {#flake} 15 | First, create a directory for your configuration and a `flake.nix` file with the following content: 16 | ```nix 17 | { 18 | description = "Modular configuration of NixOS, Home Manager, and Nix-Darwin with Denix"; 19 | 20 | inputs = { 21 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 22 | home-manager = { 23 | url = "github:nix-community/home-manager/master"; 24 | inputs.nixpkgs.follows = "nixpkgs"; 25 | }; 26 | denix = { 27 | url = "github:yunfachi/denix"; 28 | inputs.nixpkgs.follows = "nixpkgs"; 29 | inputs.home-manager.follows = "home-manager"; 30 | }; 31 | }; 32 | 33 | outputs = { 34 | denix, 35 | nixpkgs, 36 | ... 37 | } @ inputs: let 38 | mkConfigurations = moduleSystem: 39 | denix.lib.configurations { 40 | inherit moduleSystem; 41 | homeManagerUser = "sjohn"; #!!! REPLACEME 42 | 43 | paths = [./hosts ./modules ./rices]; 44 | 45 | specialArgs = { 46 | inherit inputs; 47 | }; 48 | }; 49 | in { 50 | # If you're not using NixOS, Home Manager, or Nix-Darwin, 51 | # you can safely remove the corresponding lines below. 52 | nixosConfigurations = mkConfigurations "nixos"; 53 | homeConfigurations = mkConfigurations "home"; 54 | darwinConfigurations = mkConfigurations "darwin"; 55 | }; 56 | } 57 | ``` 58 | 59 | If you are not familiar with `inputs` and `outputs`, read [NixOS Wiki Flakes](https://nixos.wiki/wiki/Flakes). 60 | 61 | Code explanation: 62 | - `mkConfigurations` - a function to reduce code repetition, which takes `moduleSystem` and passes it to `denix.lib.configurations`. 63 | - `denix.lib.configurations` - [Configurations (flakes) - Introduction](/configurations/introduction). 64 | - `paths = [./hosts ./modules ./rices];` - paths that will be recursively imported by Denix as modules. Remove `./rices` if you don't plan to use rices. 65 | 66 | ## Hosts {#hosts} 67 | Create a `hosts` directory, and within it, create a subdirectory with the name of your host, for example, `desktop`. 68 | 69 | In this subdirectory, create a `default.nix` file with the following content: 70 | ```nix 71 | {delib, ...}: 72 | delib.host { 73 | name = "desktop"; #!!! REPLACEME 74 | } 75 | ``` 76 | 77 | In the same directory, create a `hardware.nix` file: 78 | ```nix 79 | {delib, ...}: 80 | delib.host { 81 | name = "desktop"; #!!! REPLACEME 82 | 83 | homeManagerSystem = "x86_64-linux"; #!!! REPLACEME 84 | home.home.stateVersion = "24.05"; #!!! REPLACEME 85 | 86 | # If you're not using NixOS, you can remove this entire block. 87 | nixos = { 88 | nixpkgs.hostPlatform = "x86_64-linux"; #!!! REPLACEME 89 | system.stateVersion = "24.05"; #!!! REPLACEME 90 | 91 | # nixos-generate-config --show-hardware-config 92 | # other generated code here... 93 | }; 94 | 95 | # If you're not using Nix-Darwin, you can remove this entire block. 96 | darwin = { 97 | nixpkgs.hostPlatform = "aarch64-darwin"; #!!! REPLACEME 98 | system.stateVersion = 6; #!!! REPLACEME 99 | }; 100 | } 101 | ``` 102 | 103 | The `default.nix` file will be modified later after adding modules and rices, so you can keep it open. 104 | 105 | ## Rices {#rices} 106 | Skip this section if you do not wish to use rices. 107 | 108 | Create a `rices` directory, and within it, create a subdirectory with the name of your rice, for example, `dark`. 109 | 110 | In this subdirectory, create a `default.nix` file with the following content: 111 | ```nix 112 | {delib, ...}: 113 | delib.rice { 114 | name = "dark"; #!!! REPLACEME 115 | } 116 | ``` 117 | 118 | ## Modules {#modules} 119 | Create a `modules` directory, and within it, create a `config` subdirectory (typically, it contains modules that are not tied to a specific program or service). 120 | 121 | It should be mentioned that modules represent your configuration, meaning it's up to your imagination, and you are free to change the modules as you wish. 122 | 123 | ### Constants {#modules-constants} 124 | In this subdirectory, create a `constants.nix` file with the following content: 125 | ```nix 126 | {delib, ...}: 127 | delib.module { 128 | name = "constants"; 129 | 130 | options.constants = with delib; { 131 | username = readOnly (strOption "sjohn"); #!!! REPLACEME 132 | userfullname = readOnly (strOption "John Smith"); #!!! REPLACEME 133 | useremail = readOnly (strOption "johnsmith@example.com"); #!!! REPLACEME 134 | }; 135 | } 136 | ``` 137 | 138 | This file is optional, as are any of its options, which are only used by you, but it is recommended as good practice. 139 | 140 | ### Hosts {#modules-hosts} 141 | Also, create a `hosts.nix` file in this same directory (`modules/config`), and write any example from [Hosts - Examples](/hosts/examples). 142 | 143 | For example, we will take ["With the `type` Option"](/hosts/examples#type-option): 144 | ```nix 145 | {delib, ...}: 146 | delib.module { 147 | name = "hosts"; 148 | 149 | options = with delib; let 150 | host = {config, ...}: { 151 | options = 152 | hostSubmoduleOptions 153 | // { 154 | type = noDefault (enumOption ["desktop" "server"] null); 155 | 156 | isDesktop = boolOption (config.type == "desktop"); 157 | isServer = boolOption (config.type == "server"); 158 | }; 159 | }; 160 | in { 161 | host = hostOption host; 162 | hosts = hostsOption host; 163 | }; 164 | 165 | home.always = {myconfig, ...}: { 166 | assertions = delib.hostNamesAssertions myconfig.hosts; 167 | }; 168 | } 169 | ``` 170 | 171 | If you added an example with new options (`type`, `displays`, etc.) or made your own options, don't forget to add values for these options in the hosts. 172 | 173 | In our example, we added the `type` option, so open the `default.nix` file in your host's directory and add the attribute `type` to the `delib.host` function: 174 | ```nix 175 | {delib, ...}: 176 | delib.host { 177 | name = "desktop"; #!!! REPLACEME 178 | 179 | type = "desktop" #!!! REPLACEME ["desktop"|"server"] 180 | 181 | # ... 182 | } 183 | ``` 184 | 185 | ### Rices {#modules-rices} 186 | Skip this section if you are not using rices. 187 | 188 | In the `modules/config` directory, create a `rices.nix` file, and write any example from [Rices - Examples](/rices/examples). 189 | 190 | For example, we will take ["Minimally Recommended Rice Module"](/rices/examples#minimally-recommended): 191 | ```nix 192 | delib.module { 193 | name = "rices"; 194 | 195 | options = with delib; let 196 | rice = { 197 | options = riceSubmoduleOptions; 198 | }; 199 | in { 200 | rice = riceOption rice; 201 | rices = ricesOption rice; 202 | }; 203 | 204 | home.always = {myconfig, ...}: { 205 | assertions = delib.riceNamesAssertions myconfig.rices; 206 | }; 207 | } 208 | ``` 209 | 210 | Also, open the `default.nix` file of your host and add the attribute `rice` to the `delib.host` function: 211 | ```nix 212 | {delib, ...}: 213 | delib.host { 214 | name = "desktop"; #!!! REPLACEME 215 | 216 | rice = "dark" #!!! REPLACEME 217 | 218 | # ... 219 | } 220 | ``` 221 | 222 | ### Home Manager {#modules-home-manager} 223 | If you created a [constants module](#modules-constants), just create a `home.nix` file with the following content: 224 | ```nix 225 | {delib, pkgs, ...}: 226 | delib.module { 227 | name = "home"; 228 | 229 | home.always = {myconfig, ...}: let 230 | inherit (myconfig.constants) username; 231 | in { 232 | home = { 233 | inherit username; 234 | # If you don't need Nix-Darwin, or if you're using it exclusively, 235 | # you can keep the string here instead of the condition. 236 | homeDirectory = 237 | if pkgs.stdenv.isDarwin 238 | then "/Users/${username}" 239 | else "/home/${username}"; 240 | }; 241 | }; 242 | } 243 | ``` 244 | 245 | If you did not use the [constants module](#modules-constants), the content of the file will be: 246 | ```nix 247 | {delib, pkgs, ...}: 248 | delib.module { 249 | name = "home"; 250 | 251 | home.always.home = { 252 | username = "sjohn"; #!!! REPLACEME 253 | # If you don't need Nix-Darwin, or if you're using it exclusively, 254 | # you can keep the string here instead of the condition. 255 | homeDirectory = 256 | if pkgs.stdenv.isDarwin 257 | then "/Users/sjohn" #!!! REPLACEME 258 | else "/home/sjohn"; #!!! REPLACEME 259 | }; 260 | } 261 | ``` 262 | 263 | ### User {#modules-user} 264 | You can also create a `user.nix` file with the configuration of your NixOS and Nix-Darwin user: 265 | ```nix 266 | {delib, ...}: 267 | delib.module { 268 | name = "user"; 269 | 270 | # If you're not using NixOS, you can remove this entire block. 271 | nixos.always = {myconfig, ...}: let 272 | inherit (myconfig.constants) username; 273 | in { 274 | users = { 275 | groups.${username} = {}; 276 | 277 | users.${username} = { 278 | isNormalUser = true; 279 | initialPassword = username; 280 | extraGroups = ["wheel"]; 281 | }; 282 | }; 283 | }; 284 | 285 | # If you're not using Nix-Darwin, you can remove this entire block. 286 | darwin.always = {myconfig, ...}: let 287 | inherit (myconfig.constants) username; 288 | in { 289 | users.users.${username} = { 290 | name = username; 291 | home = "/Users/${username}"; 292 | }; 293 | }; 294 | } 295 | ``` 296 | 297 | If you did not use the [constants module](#modules-constants), the content of the file will be: 298 | ```nix 299 | {delib, ...}: 300 | delib.module { 301 | name = "user"; 302 | 303 | # If you're not using NixOS, you can remove this entire block. 304 | nixos.always.users = { 305 | groups.sjohn = {}; #!!! REPLACEME 306 | 307 | users.sjohn = { #!!! REPLACEME 308 | isNormalUser = true; 309 | initialPassword = "sjohn"; #!!! REPLACEME 310 | extraGroups = ["wheel"]; 311 | }; 312 | }; 313 | 314 | # If you're not using Nix-Darwin, you can remove this entire block. 315 | darwin.always.users.users."sjohn" = { #!!! REPLACEME 316 | name = "sjohn"; #!!! REPLACEME 317 | home = "/Users/sjohn"; #!!! REPLACEME 318 | }; 319 | } 320 | ``` 321 | 322 | ## Conclusion {#conclusion} 323 | If you have followed the instructions precisely, you will end up with the following configuration directory tree: 324 | ```plaintext 325 | hosts 326 | - desktop 327 | - default.nix 328 | - hardware.nix 329 | modules 330 | - config 331 | - constants.nix 332 | - home.nix 333 | - hosts.nix 334 | - rices.nix 335 | - user.nix 336 | rices 337 | - dark 338 | - default.nix 339 | flake.nix 340 | ``` 341 | 342 | You can check if everything is set up correctly using the command: 343 | ```sh 344 | nix flake check .# 345 | ``` 346 | -------------------------------------------------------------------------------- /docs/src/getting_started/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction {#introduction} 2 | In this section, you will learn about what Denix is, why it is needed, who can benefit from it, and popular practices for system configuration. 3 | 4 | ## What is Denix {#what-is-denix} 5 | Denix is a Nix library designed for creating scalable configurations for [NixOS](https://nixos.org/), [Home Manager](https://github.com/nix-community/home-manager), and [Nix-Darwin](https://github.com/nix-darwin/nix-darwin). 6 | 7 | It provides functions that transform input data into a module according to a specific algorithm. Thanks to this, if for any reason you need to create a module without using Denix, it will be sufficient to import the file with it, and everything will work. 8 | 9 | The provided functions are generally divided into five categories: 10 | - Creating configurations for Flake ([NixOS](https://nixos.org/), [Home Manager](https://github.com/nix-community/home-manager), or [Nix-Darwin](https://github.com/nix-darwin/nix-darwin)) 11 | - Options - an analogue of types and functions for creating options from [Nixpkgs](https://github.com/NixOS/nixpkgs) 12 | - [Modules](#modules) 13 | - [Hosts](#hosts) 14 | - [Rices](#rices) 15 | 16 | ## Why and Who Needs Denix {#why-and-who-needs-denix} 17 | Denix is primarily needed to simplify the creation, editing, and readability of configuration code. It eliminates the need to create typical expressions for your own modules, hosts, rices, etc. 18 | 19 | If you plan to expand your configuration across multiple machines (hosts), want to have various system settings (rices) that can be changed with a single command, and strive to write readable and clean code, then you should consider trying Denix. Conversely, if you're creating a small configuration for a single machine and don't plan to expand it, Denix might be unnecessary. 20 | 21 | Configuration templates using Denix can be found in the `templates` directory of the GitHub repository: [github:yunfachi/denix?path=templates](https://github.com/yunfachi/denix/tree/master/templates). 22 | 23 | ## Modules {#modules} 24 | Custom modules are possibly the best practice for creating scalable configurations. 25 | 26 | A module includes options, configuration, and importing other modules. 27 | - `options = {};`: Similar to declaring variables in programming, but here it's a declaration of options. 28 | - `config = {};`: Similar to initializing variables, but here it's about specifying values for options. 29 | - `imports = [];`: A list of paths to modules or just module code (attrset or lambda that returns attrset). 30 | 31 | NixOS, Home Manager, and Nix-Darwin have their own modules, which you have likely already worked with. You can search for options on these sites: 32 | - NixOS: https://search.nixos.org/options 33 | - Home Manager: https://home-manager-options.extranix.com/ 34 | - Nix-Darwin: https://nix-darwin.github.io/nix-darwin/manual/ 35 | 36 | Example of custom NixOS module code without using Denix: 37 | ```nix 38 | { 39 | lib, 40 | config, 41 | ... 42 | }: { 43 | options = { 44 | example = { 45 | enable = lib.mkEnableOption "example" // {default = true;}; 46 | hostnames = lib.mkOption { 47 | type = lib.types.listOf lib.types.str; 48 | default = []; 49 | }; 50 | }; 51 | }; 52 | 53 | config = lib.mkIf (!config.example.enable) { 54 | networking.hosts."127.0.0.1" = config.example.hostnames; 55 | }; 56 | } 57 | ``` 58 | The same functionality but using Denix: 59 | ```nix 60 | {delib, ...}: 61 | delib.module { 62 | name = "example"; 63 | 64 | options.example = with delib; { 65 | enable = enableOption true; 66 | hostnames = listOfOption str []; 67 | }; 68 | 69 | nixos.ifEnabled = {cfg, ...}: { 70 | networking.hosts."127.0.0.1" = cfg.hostnames; 71 | }; 72 | } 73 | ``` 74 | You can learn more about Denix modules in [Modules](/modules/introduction). 75 | 76 | ## Hosts {#hosts} 77 | Host is any machine, such as a personal computer, server, etc. 78 | 79 | The essence of this practice is to separate the configuration into a shared part and a unique part for each host. 80 | 81 | Example of a host configuration module using Denix: 82 | ```nix 83 | {delib, ...}: 84 | delib.host { 85 | name = "macbook"; 86 | 87 | rice = "dark"; 88 | type = "desktop"; 89 | 90 | homeManagerSystem = "x86_64-darwin"; 91 | home.home.stateVersion = "24.05"; 92 | 93 | shared.myconfig = { 94 | services.openssh.authorizedKeys = ["ssh-ed25519 ..."]; 95 | }; 96 | } 97 | ``` 98 | You can learn more about Denix hosts in [Hosts](/hosts/introduction). 99 | 100 | ## Rices {#rices} 101 | Rice is a slang term used to describe system settings, especially related to appearance. 102 | 103 | In our case, this is any configuration not tied to a specific host. 104 | 105 | Example of a rice configuration module using Denix: 106 | ```nix 107 | {delib, ...}: 108 | delib.rice { 109 | name = "dark"; 110 | 111 | home.stylix = { 112 | polarity = "dark"; 113 | colors = { 114 | # ... 115 | }; 116 | }; 117 | } 118 | ``` 119 | You can learn more about Denix rices in [Rices](/rices/introduction). 120 | -------------------------------------------------------------------------------- /docs/src/getting_started/transfer_to_denix.md: -------------------------------------------------------------------------------- 1 | # Transfer to Denix {#transfer-to-denix} 2 | If you already have a NixOS, Home Manager, or Nix-Darwin configuration, you can transfer most of the code without significant changes and then adapt it for Denix. 3 | 4 | However, you will need to create the following from scratch: 5 | - Hosts 6 | - Rices (if you want) 7 | - Some initial modules 8 | 9 | The main part of the configuration can be fully reused from your old setup. The key is to separate the hardware configuration from the general configuration. See the section [How Does It Work?](#how-it-works). 10 | 11 | ## How Does It Work? {#how-it-works} 12 | All Denix modules are standard NixOS, Home Manager, or Nix-Darwin modules but with additional logic for enabling and disabling configurations. 13 | 14 | This means that you can add code or files from the old configuration into the new one, so they are imported through [`delib.configurations`](/configurations/introduction). You can place this code in the `modules` directory or create a new one, for example, `modules_nixos_old` for older configurations. 15 | 16 | ## Example of a Simple Configuration {#example-of-simple-configuration} 17 | Suppose you have an old configuration consisting of two files: `configuration.nix` and `hardware-configuration.nix`, and you've already created hosts and the `modules/config` directory following the instructions. The configuration for your host should include `hardware-configuration.nix`, so all that remains is to copy `configuration.nix` into the `modules` directory and remove unnecessary options, like `system.stateVersion`. 18 | 19 | ## Example of a Complex Configuration {#example-of-complex-configuration} 20 | Suppose you have an old configuration with hosts and multiple modules split across files. Hosts are typically specific to your system, so you will need to transfer them manually, usually by just copying the code. 21 | 22 | Modules (e.g., for programs and services) can simply be copied into the `modules` directory or other files imported via [`delib.configurations`](/configurations/introduction). 23 | -------------------------------------------------------------------------------- /docs/src/hosts/examples.md: -------------------------------------------------------------------------------- 1 | # Examples {#examples} 2 | 3 | ## Minimally Recommended Host Module: {#minimally-recommended} 4 | An example of a minimal host module configuration that serves as a baseline for all further settings: 5 | 6 | ```nix 7 | {delib, ...}: 8 | delib.module { 9 | name = "hosts"; 10 | 11 | options = with delib; let 12 | host = { 13 | options = hostSubmoduleOptions; 14 | }; 15 | in { 16 | host = hostOption host; 17 | hosts = hostsOption host; 18 | }; 19 | 20 | home.always = {myconfig, ...}: { 21 | assertions = delib.hostNamesAssertions myconfig.hosts; 22 | }; 23 | } 24 | ``` 25 | 26 | ## With the `type` Option {#type-option} 27 | The `type` option **is very** useful for default values in your modules. For example, the option `enable = boolOption host.isDesktop` can be used for some GUI programs. This simplifies configuration management based on the type of device. 28 | 29 | ```nix 30 | {delib, ...}: 31 | delib.module { 32 | name = "hosts"; 33 | 34 | options = with delib; let 35 | host = {config, ...}: { 36 | options = 37 | hostSubmoduleOptions 38 | // { 39 | type = noDefault (enumOption ["desktop" "server"] null); 40 | 41 | isDesktop = boolOption (config.type == "desktop"); 42 | isServer = boolOption (config.type == "server"); 43 | }; 44 | }; 45 | in { 46 | host = hostOption host; 47 | hosts = hostsOption host; 48 | }; 49 | 50 | home.always = {myconfig, ...}: { 51 | assertions = delib.hostNamesAssertions myconfig.hosts; 52 | }; 53 | } 54 | ``` 55 | 56 | ## With the `displays` Option {#displays-option} 57 | This option can be useful for configuring monitors; however, it can be implemented as a separate module. 58 | 59 | ```nix 60 | {delib, ...}: 61 | delib.module { 62 | name = "hosts"; 63 | 64 | options = with delib; let 65 | host = {config, ...}: { 66 | options = 67 | hostSubmoduleOptions 68 | // { 69 | displays = listOfOption (submodule { 70 | options = { 71 | enable = boolOption true; 72 | touchscreen = boolOption false; 73 | 74 | # e.g. DP-1, HDMI-A-1 75 | name = noDefault (strOption null); 76 | primary = boolOption (builtins.length config.displays == 1); 77 | refreshRate = intOption 60; 78 | 79 | width = intOption 1920; 80 | height = intOption 1080; 81 | x = intOption 0; 82 | y = intOption 0; 83 | }; 84 | }) []; 85 | }; 86 | }; 87 | in { 88 | host = hostOption host; 89 | hosts = hostsOption host; 90 | }; 91 | 92 | home.always = {myconfig, ...}: { 93 | assertions = delib.hostNamesAssertions myconfig.hosts; 94 | }; 95 | } 96 | ``` 97 | 98 | ## Short Version {#short-version} 99 | Using `delib.hostNamesAssertions` is strongly recommended, but it can also be omitted. 100 | 101 | ```nix 102 | {delib, ...}: 103 | delib.module { 104 | name = "hosts"; 105 | 106 | options = with delib; let 107 | host.options = hostSubmoduleOptions; 108 | in { 109 | host = hostOption host; 110 | hosts = hostsOption host; 111 | }; 112 | } 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/src/hosts/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction to Denix Hosts {#introduction} 2 | A Denix host is an attribute set returned by the function [`delib.host`](/hosts/structure), which enables or disables specific configurations depending on the value of the option `${myconfigName}.host`. It also passes the input attribute set of the `delib.host` function to the `${myconfigName}.host.${delib.host :: name}` option. 3 | 4 | In simple terms, this allows you to separate the NixOS, Home Manager, Nix-Darwin, and custom options configuration into those that apply only to the current host and those that apply to all hosts. For example, the former can be used to enable or disable certain programs, while the latter can be used to add the current host's SSH key to the `authorizedKeys` of all hosts. 5 | 6 | A host can also be compared to a module, because all hosts are imported regardless of which one is active, but the applied configurations depend on the active host. 7 | 8 | ## Options {#options} 9 | For hosts to work, the configuration must include the options `${myconfigName}.host` and `${myconfigName}.hosts`, which **you define yourself** in one of the modules. 10 | 11 | Here is an example of a minimal recommended host configuration: 12 | 13 | ```nix 14 | {delib, ...}: 15 | delib.module { 16 | name = "hosts"; 17 | 18 | options = with delib; let 19 | host = { 20 | options = hostSubmoduleOptions; 21 | }; 22 | in { 23 | host = hostOption host; 24 | hosts = hostsOption host; 25 | }; 26 | 27 | home.always = {myconfig, ...}: { 28 | assertions = delib.hostNamesAssertions myconfig.hosts; 29 | }; 30 | } 31 | ``` 32 | 33 | More examples can be found in the [Examples](/hosts/examples) section. 34 | -------------------------------------------------------------------------------- /docs/src/hosts/structure.md: -------------------------------------------------------------------------------- 1 | # Structure {#structure} 2 | 3 | ## Function Arguments {#function-arguments} 4 | - `name`: a string representing the host name. 5 | - `homeManagerSystem`: a string used in the `pkgs` attribute of the `home-manager.lib.homeManagerConfiguration` function, which is used in the [`delib.configurations`](/configurations/introduction) function as `homeManagerNixpkgs.legacyPackages.${homeManagerSystem}`. 6 | - `myconfig`: sets its value to `config.${myconfigName}` if `config.${myconfigName}.host` matches the current host. 7 | - `nixos`: sets its value to `config` if `moduleSystem` is `nixos` and `config.${myconfigName}.host` matches the current host. 8 | - `home`: sets its value to `config` if `moduleSystem` is `home` and `config.${myconfigName}.host` matches the current host. Otherwise, if `config.${myconfigName}.host` matches the current host, sets its value to `config.home-manager.users.${homeManagerUser}`. 9 | - `darwin`: sets its value to `config` if `moduleSystem` is `darwin` and `config.${myconfigName}.host` matches the current host. 10 | - `shared.myconfig`: sets its value to `config.${myconfigName}` for all hosts. 11 | - `shared.nixos`: sets its value to `config` if `moduleSystem` is `nixos`. Otherwise, does nothing. 12 | - `shared.home`: sets its value to `config` if `moduleSystem` is `home`. Otherwise, sets it to `config.home-manager.users.${homeManagerUser}`. 13 | - `shared.darwin`: sets its value to `config` if `moduleSystem` is `darwin`. Otherwise, does nothing. 14 | 15 | ## Passed Arguments {#passed-arguments} 16 | A list of arguments passed to `?(shared.)[myconfig|nixos|home|darwin]` if their type is `lambda`: 17 | 18 | - `name`: the same `name` as in the arguments of `delib.host`. 19 | - `myconfig`: equals `config.${myConfigName}`. 20 | - `cfg`: equals `config.${myConfigName}.hosts.${delib.host :: name}`. 21 | 22 | ## Pseudocode {#pseudocode} 23 | ```nix 24 | delib.host { 25 | name = ""; 26 | 27 | # homeManagerNixpkgs.legacyPackages.${homeManagerSystem} 28 | homeManagerSystem = "x86_64-linux"; 29 | 30 | # if config.${myconfigName}.host == name 31 | # then {config.${myConfigName} = ...;} 32 | # else {} 33 | myconfig = {name, cfg, myconfig, ...}: {}; 34 | 35 | # if config.${myconfigName}.host == name, then 36 | # if moduleSystem == "nixos" 37 | # then {config = ...;} 38 | # else {} 39 | # else {} 40 | nixos = {name, cfg, myconfig, ...}: {}; 41 | 42 | # if config.${myconfigName}.host == name, then 43 | # if moduleSystem == "home" 44 | # then {config = ...;} 45 | # else {config.home-manager.users.${homeManagerUser} = ...;} 46 | # else {} 47 | home = {name, cfg, myconfig, ...}: {}; 48 | 49 | # if config.${myconfigName}.host == name, then 50 | # if moduleSystem == "darwin" 51 | # then {config = ...;} 52 | # else {} 53 | # else {} 54 | darwin = {name, cfg, myconfig, ...}: {}; 55 | 56 | # config.${myConfigName} = ... 57 | shared.myconfig = {name, cfg, myconfig, ...}: {}; 58 | 59 | # if moduleSystem == "nixos" 60 | # then {config = ...;} 61 | # else {} 62 | shared.nixos = {name, cfg, myconfig, ...}: {}; 63 | 64 | # if moduleSystem == "home" 65 | # then {config = ...;} 66 | # else {config.home-manager.users.${homeManagerUser} = ...;} 67 | shared.home = {name, cfg, myconfig, ...}: {}; 68 | 69 | # if moduleSystem == "darwin" 70 | # then {config = ...;} 71 | # else {} 72 | shared.darwin = {name, cfg, myconfig, ...}: {}; 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Denix Documentation" 7 | text: "Nix Library for Configurations" 8 | tagline: "Denix provides tools for creating scalable NixOS, Home Manager, and Nix-Darwin configurations with modules, hosts, and rices" 9 | image: 10 | light: https://raw.githubusercontent.com/yunfachi/denix/master/.github/assets/logo_light.svg 11 | dark: https://raw.githubusercontent.com/yunfachi/denix/master/.github/assets/logo_dark.svg 12 | actions: 13 | - theme: brand 14 | text: Introduction 15 | link: /getting_started/introduction 16 | - theme: alt 17 | text: GitHub with Templates 18 | link: https://github.com/yunfachi/denix 19 | 20 | features: 21 | - title: Modules 22 | details: Custom modules contain options and related configurations, making it easy to manage the entire system 23 | - title: Hosts and Rices 24 | details: Hosts are unique configurations for each machine, while rices are customizations applicable to all hosts 25 | - title: NixOS, Home Manager, and Nix-Darwin 26 | details: NixOS, Home Manager, and Nix-Darwin configurations can be written in the same file, and Denix will automatically separate them 27 | 28 | --- 29 | -------------------------------------------------------------------------------- /docs/src/modules/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Constants {#constants} 4 | ```nix 5 | {delib, ...}: 6 | delib.module { 7 | name = "constants"; 8 | 9 | options.constants = with delib; { 10 | username = readOnly (strOption "sjohn"); 11 | userfullname = readOnly (strOption "John Smith"); 12 | useremail = readOnly (strOption "johnsmith@example.com"); 13 | }; 14 | } 15 | ``` 16 | 17 | ## Home Manager {#home-manager} 18 | With [constants](#constants): 19 | ```nix 20 | {delib, pkgs, ...}: 21 | delib.module { 22 | name = "home"; 23 | 24 | home.always = {myconfig, ...}: let 25 | inherit (myconfig.constants) username; 26 | in { 27 | home = { 28 | inherit username; 29 | # If you don't need Nix-Darwin, or if you're using it exclusively, 30 | # you can keep the string here instead of the condition. 31 | homeDirectory = 32 | if pkgs.stdenv.isDarwin 33 | then "/Users/${username}" 34 | else "/home/${username}"; 35 | }; 36 | }; 37 | } 38 | ``` 39 | 40 | ## User {#user} 41 | With [constants](#constants): 42 | ```nix 43 | {delib, ...}: 44 | delib.module { 45 | name = "user"; 46 | 47 | # If you're not using NixOS, you can remove this entire block. 48 | nixos.always = {myconfig, ...}: let 49 | inherit (myconfig.constants) username; 50 | in { 51 | users = { 52 | groups.${username} = {}; 53 | 54 | users.${username} = { 55 | isNormalUser = true; 56 | initialPassword = username; 57 | extraGroups = ["wheel"]; 58 | }; 59 | }; 60 | }; 61 | 62 | # If you're not using Nix-Darwin, you can remove this entire block. 63 | darwin.always = {myconfig, ...}: let 64 | inherit (myconfig.constants) username; 65 | in { 66 | users.users.${username} = { 67 | name = username; 68 | home = "/Users/${username}"; 69 | }; 70 | }; 71 | } 72 | ``` 73 | 74 | ## Git {#git} 75 | With [constants](#constants): 76 | ```nix 77 | {delib, ...}: 78 | delib.module { 79 | name = "programs.git"; 80 | 81 | options.programs.git = with delib; { 82 | enable = boolOption true; 83 | enableLFS = boolOption true; 84 | }; 85 | 86 | home.ifEnabled.programs.git = {myconfig, cfg, ...}: { 87 | enable = true; 88 | lfs.enable = cfg.enableLFS; 89 | 90 | userName = myconfig.constants.username; 91 | userEmail = myconfig.constants.useremail; 92 | }; 93 | } 94 | ``` 95 | 96 | ## Alejandra {#alejandra} 97 | ```nix 98 | {delib, ...}: 99 | delib.module { 100 | name = "programs.alejandra"; 101 | 102 | options = delib.singleEnableOption true; 103 | 104 | home.ifEnabled.home.packages = [pkgs.alejandra]; 105 | } 106 | ``` 107 | -------------------------------------------------------------------------------- /docs/src/modules/introduction-nixos.md: -------------------------------------------------------------------------------- 1 | # Introduction to NixOS Modules {#introduction-nixos} 2 | A NixOS module is a file containing a Nix expression with a specific structure. It defines options (options) and values for those options (config). For example, the `/etc/nixos/configuration.nix` file is also a module. 3 | 4 | Home Manager and Nix-Darwin modules work similarly, but they can be used not only in NixOS but also on other systems. 5 | 6 | This is not a comprehensive guide on Nix modules, so it is recommended to read the [NixOS Wiki article on modules](https://nixos.wiki/wiki/NixOS_modules). 7 | 8 | ## Structure {#structure} 9 | The structure of a NixOS module: 10 | 11 | ```nix 12 | { 13 | imports = [ 14 | # Paths to other modules or Nix expressions. 15 | ]; 16 | 17 | options = { 18 | # Declaration of options. 19 | }; 20 | 21 | config = { 22 | # Assigning values to previously declared options. 23 | # For example, networking.hostName = "denix"; 24 | }; 25 | 26 | # However, values are usually assigned to options not in config, but here. 27 | # For example, networking.hostName = "denix"; 28 | } 29 | ``` 30 | 31 | ### Function {#structure-function} 32 | A module can also be a function (lambda) that returns an attribute set: 33 | 34 | ```nix 35 | {lib, ...} @ args: { 36 | # Formally, this is config._module.args.hostname 37 | _module.args.hostname = lib.concatStrings ["de" "nix"]; 38 | 39 | imports = [ 40 | {hostname, config, ...}: { 41 | config = lib.mkIf config.customOption.enable { 42 | networking.hostName = hostname; 43 | }; 44 | } 45 | ]; 46 | } 47 | ``` 48 | 49 | ### Imports 50 | `imports` is a list of relative and absolute paths, as well as [functions](#structure-function) and attribute sets: 51 | 52 | ```nix 53 | { 54 | imports = [ 55 | ./pureModule.nix 56 | /etc/nixos/impureModule.nix 57 | {pkgs, ...}: { 58 | # ... 59 | } 60 | ]; 61 | } 62 | ``` 63 | 64 | ### Nixpkgs Options 65 | Options are usually declared using `lib.mkOption`: 66 | 67 | ```nix 68 | optionName = lib.mkOption { 69 | # ... 70 | }; 71 | ``` 72 | 73 | `lib.mkOption` accepts an attribute set. Some attributes include: 74 | 75 | - `type`: the type of value for this option, e.g., `lib.types.listOf lib.types.port`. 76 | - `default`: the default value for this option. 77 | - `example`: an example value for this option, used in documentation. 78 | - `description`: a description of this option, also used in documentation. 79 | - `readOnly`: whether the option can be modified after declaration. 80 | 81 | For more information on Nixpkgs options, see the [NixOS Wiki](https://nixos.wiki/wiki/Declaration). 82 | 83 | ### Denix Options 84 | Denix uses a different approach to options, although both methods can be used simultaneously. 85 | 86 | An example of a module with options using Denix: 87 | 88 | ```nix 89 | {delib, ...}: 90 | delib.module { 91 | name = "coolmodule"; 92 | 93 | options.coolmodule = with delib; { 94 | # boolOption 95 | enable = boolOption false; 96 | # noDefault (listOf) 97 | ports = noDefault (listOfOption port null); 98 | # allowNull (strOption) 99 | email = allowNull (strOption null); 100 | }; 101 | } 102 | ``` 103 | 104 | For more information on Denix options, see the [Options](/options/introduction) section. 105 | 106 | ## Creating Your Own Modules (Options) {#creating-own-modules} 107 | Declaring your own options is a great practice if you want to enable or disable certain parts of the code. This can be useful if you have multiple hosts (machines) with the same configuration, and, for example, machine `foo` does not need a program that is used on machine `bar`. 108 | 109 | An example of a NixOS module for simple git configuration: 110 | 111 | ```nix 112 | {lib, config, ...}: { 113 | options.programs.git = { 114 | enable = lib.mkEnableOption "git" // {default = true;}; 115 | }; 116 | 117 | config = lib.mkIf config.programs.git.enable { 118 | programs.git = { 119 | enable = true; 120 | lfs.enable = true; 121 | 122 | config = { 123 | init.defaultBranch = "master"; 124 | }; 125 | }; 126 | }; 127 | } 128 | ``` 129 | 130 | The same configuration but using Denix: 131 | 132 | ```nix 133 | {delib, ...}: 134 | delib.module { 135 | name = "programs.git"; 136 | 137 | options = delib.singleEnableOption true; 138 | 139 | nixos.ifEnabled.programs.git = { 140 | enable = true; 141 | lfs.enable = true; 142 | 143 | config = { 144 | init.defaultBranch = "master"; 145 | }; 146 | }; 147 | } 148 | ``` 149 | -------------------------------------------------------------------------------- /docs/src/modules/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction to Denix Modules {#introduction} 2 | A Denix module is the same as a NixOS, Home Manager, or Nix-Darwin module, but its attribute set is wrapped in the [`delib.module`](/modules/structure) function. 3 | 4 | ## No Limitations {#no-limitations} 5 | This means that you can use all three types of modules simultaneously, although it's unlikely you'll need anything other than the first option: 6 | 7 | ### Denix Module 8 | ```nix 9 | {delib, ...}: 10 | delib.module { 11 | name = "..."; 12 | } 13 | ``` 14 | 15 | ### Denix Module with NixOS/Home Manager/Nix-Darwin Module 16 | ```nix 17 | {delib, ...}: 18 | delib.module { 19 | name = "..."; 20 | } // { 21 | 22 | } 23 | ``` 24 | 25 | ### NixOS/Home Manager/Nix-Darwin Module Only 26 | ```nix 27 | { 28 | 29 | } 30 | ``` 31 | 32 | ## Simplicity and Cleanliness {#simplicity-and-cleanliness} 33 | Denix modules tend to look simpler and cleaner compared to NixOS/Home Manager/Nix-Darwin modules, due to the following reasons: 34 | 35 | 1. Simple yet fully functional option declaration (see [Options](/options/introduction)). 36 | 2. Built-in logic for separating configurations based on the value of `${delib.module :: name}.enable`: always, ifEnabled, ifDisabled. 37 | 3. Shared options but separated configurations for NixOS, Home Manager, Nix-Darwin, and custom options. 38 | -------------------------------------------------------------------------------- /docs/src/modules/structure.md: -------------------------------------------------------------------------------- 1 | # Structure {#structure} 2 | This section uses the variables `myconfigName`, `moduleSystem`, and `homeManagerUser`, so it's recommended to first review the corresponding subsection: [delib.configurations Function Arguments](/configurations/structure#function-arguments). 3 | 4 | ## Function Arguments {#function-arguments} 5 | - `name`{#function-arguments-name}: string. It is recommended that it matches the parent path of the module's `enable` option, such as `"programs.example"`, since the `cfg` argument depends on this path. 6 | - `options`: attrset or a lambda that returns an attrset (see [Options](/options/introduction)). 7 | - `[myconfig|nixos|home|darwin].[ifEnabled|ifDisabled|always]`: attrset or a lambda that returns an attrset. 8 | - `myconfig.*`: sets values in `config.${myconfigName}`. 9 | - `nixos.*`: sets values in `config` if `moduleSystem` is `nixos`. Otherwise, does nothing. 10 | - `home.*`: sets values in `config` if `ModuleSystem` is `home`. Otherwise, sets it in `config.home-manager.users.${homeManagerUser}`. 11 | - `darwin.*`: sets values in `config` if `moduleSystem` is `darwin`. Otherwise, does nothing. 12 | - `[myconfig|nixos|home|darwin].ifEnabled`: executed only if the `cfg.enable` option (see [Passed Arguments - cfg](#passed-arguments-cfg)) exists and is `true`. 13 | - `[myconfig|nixos|home|darwin].ifDisabled`: executed only if the `cfg.enable` option exists and is `false`. 14 | - `[myconfig|nixos|home|darwin].always`: always executed, even if the `cfg.enable` option doesn't exist. 15 | 16 | ## Passed Arguments {#passed-arguments} 17 | A list of arguments passed to `options` and `[myconfig|nixos|home|darwin].[ifEnabled|ifDisabled|always]`, if their type is a `lambda`: 18 | - `name`: the same [name](#function-arguments-name) from the `delib.module` arguments. 19 | - `myconfig`: equals `config.${myConfigName}`. 20 | - `cfg`{#passed-arguments-cfg}: equals the result of the expression `delib.getAttrByStrPath name config.${myConfigName} {}`, where `name` is the [argument](#function-arguments-name) from `delib.module`, and `{}` is the value returned if the attribute is not found. 21 | 22 | ## Pseudocode {#pseudocode} 23 | ```nix 24 | delib.module { 25 | name = ""; 26 | 27 | # options.${myConfigName} = ... 28 | options = {name, cfg, myconfig, ...}: {}; 29 | 30 | # config.${myConfigName} = ... 31 | myconfig.ifEnabled = {name, cfg, myconfig, ...}: {}; 32 | myconfig.ifDisabled = {name, cfg, myconfig, ...}: {}; 33 | myconfig.always = {name, cfg, myconfig, ...}: {}; 34 | 35 | # if moduleSystem == "nixos" 36 | # then {config = ...;} 37 | # else {} 38 | nixos.ifEnabled = {name, cfg, myconfig, ...}: {}; 39 | nixos.ifDisabled = {name, cfg, myconfig, ...}: {}; 40 | nixos.always = {name, cfg, myconfig, ...}: {}; 41 | 42 | # if moduleSystem == "home" 43 | # then {config = ...;} 44 | # else {config.home-manager.users.${homeManagerUser} = ...;} 45 | home.ifEnabled = {name, cfg, myconfig, ...}: {}; 46 | home.ifDisabled = {name, cfg, myconfig, ...}: {}; 47 | home.always = {name, cfg, myconfig, ...}: {}; 48 | 49 | # if moduleSystem == "darwin" 50 | # then {config = ...;} 51 | # else {} 52 | darwin.ifEnabled = {name, cfg, myconfig, ...}: {}; 53 | darwin.ifDisabled = {name, cfg, myconfig, ...}: {}; 54 | darwin.always = {name, cfg, myconfig, ...}: {}; 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/src/options/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction to Denix Options {#introduction} 2 | Denix options are a wrapper for `lib.mkOption`. This means that any Denix option can be created using the standard `lib.mkOption`, and using Denix options is optional. 3 | 4 | ## Why use Denix options? {#why} 5 | Using `lib.mkOption` can be cumbersome, and writing something like this every time: 6 | 7 | ```nix 8 | lib.mkOption {type = lib.types.listOf lib.types.str; default = [];} 9 | ``` 10 | 11 | is inconvenient, especially when options are used extensively in your configuration. Instead, you can write something more concise: 12 | 13 | ```nix 14 | delib.listOfOption delib.str [] 15 | ``` 16 | 17 | Thus, instead of creating an option by specifying all parameters, you can use Denix functions for more readable and cleaner code. 18 | 19 | ## Working Principle {#principle} 20 | Functions related to options are divided into four main types: 21 | 22 | ### Creating an Option with Type N 23 | - `*Option ` - for example, `strOption "foo"`. 24 | - `*OfOption ` - for example, `listOfOption str ["foo" "bar"]`. 25 | - `*ToOption ` - for example, `lambdaToOption int (x: 123)`. 26 | 27 | ### Adding Type N to Option Type X 28 | - `allow*