[
  {
    "path": ".envrc",
    "content": "use flake\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [pinpox]\nbuy_me_a_coffee: pinpox\n"
  },
  {
    "path": ".github/workflows/check-upstream-todos.yml",
    "content": "name: Check Upstream TODOs\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n  schedule:\n    - cron: \"0 0 * * 0\" # Weekly on Sunday\n\njobs:\n  check-todos:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Check for resolved upstream TODOs\n        run: |\n          #!/bin/bash\n          set -e\n\n          # Find all TODO comments with GitHub PR/issue links\n          echo \"Searching for TODO comments with GitHub links...\"\n\n          # Search for TODO (with or without colon) followed by GitHub URLs\n          TODO_PATTERN=\"TODO:?\\s*https://github\\.com/[^/]+/[^/]+/(pull|issues)/[0-9]+\"\n\n          # Find all matches and extract URLs\n          TODOS=$(grep -r -n -E \"$TODO_PATTERN\" . --exclude-dir=.git --exclude-dir=.github || true)\n\n          if [ -z \"$TODOS\" ]; then\n            echo \"No TODO comments with GitHub links found.\"\n            exit 0\n          fi\n\n          echo \"Found TODO comments:\"\n          echo \"$TODOS\"\n          echo \"\"\n\n          # Extract unique URLs from the matches\n          URLS=$(echo \"$TODOS\" | grep -oE \"https://github\\.com/[^/]+/[^/]+/(pull|issues)/[0-9]+\" | sort -u)\n\n          FAILED=false\n          FAILED_LOCATIONS=\"\"\n\n          for URL in $URLS; do\n            echo \"Checking: $URL\"\n            \n            # Find all locations where this URL appears\n            LOCATIONS=$(echo \"$TODOS\" | grep -F \"$URL\" || true)\n            \n            # Extract owner, repo, type (pull or issues), and number from URL\n            if [[ $URL =~ https://github\\.com/([^/]+)/([^/]+)/(pull|issues)/([0-9]+) ]]; then\n              OWNER=\"${BASH_REMATCH[1]}\"\n              REPO=\"${BASH_REMATCH[2]}\"\n              TYPE=\"${BASH_REMATCH[3]}\"\n              NUMBER=\"${BASH_REMATCH[4]}\"\n              \n              # Use GitHub API to check status\n              if [ \"$TYPE\" == \"pull\" ]; then\n                echo \"  Checking PR #$NUMBER in $OWNER/$REPO...\"\n                API_URL=\"https://api.github.com/repos/$OWNER/$REPO/pulls/$NUMBER\"\n                STATE=$(curl -s -H \"Accept: application/vnd.github.v3+json\" \"$API_URL\" | jq -r '.state // \"unknown\"')\n                MERGED=$(curl -s -H \"Accept: application/vnd.github.v3+json\" \"$API_URL\" | jq -r '.merged // false')\n                \n                if [ \"$STATE\" == \"closed\" ] && [ \"$MERGED\" == \"true\" ]; then\n                  echo \"  ❌ FAIL: PR #$NUMBER is MERGED! Remove TODO and workaround.\"\n                  echo \"  Locations:\"\n                  while IFS= read -r line; do\n                    echo \"    $line\"\n                  done <<< \"$LOCATIONS\"\n                  FAILED=true\n                  FAILED_LOCATIONS=\"$FAILED_LOCATIONS$LOCATIONS\"$'\\n'\n                elif [ \"$STATE\" == \"closed\" ] && [ \"$MERGED\" == \"false\" ]; then\n                  echo \"  ⚠️  PR #$NUMBER is closed but not merged.\"\n                elif [ \"$STATE\" == \"open\" ]; then\n                  echo \"  ✅ PR #$NUMBER is still open.\"\n                else\n                  echo \"  ⚠️  Could not determine PR status (state: $STATE, merged: $MERGED)\"\n                fi\n              else\n                echo \"  Checking Issue #$NUMBER in $OWNER/$REPO...\"\n                API_URL=\"https://api.github.com/repos/$OWNER/$REPO/issues/$NUMBER\"\n                STATE=$(curl -s -H \"Accept: application/vnd.github.v3+json\" \"$API_URL\" | jq -r '.state // \"unknown\"')\n                \n                if [ \"$STATE\" == \"closed\" ]; then\n                  echo \"  ❌ FAIL: Issue #$NUMBER is CLOSED! Remove TODO and workaround.\"\n                  echo \"  Locations:\"\n                  while IFS= read -r line; do\n                    echo \"    $line\"\n                  done <<< \"$LOCATIONS\"\n                  FAILED=true\n                  FAILED_LOCATIONS=\"$FAILED_LOCATIONS$LOCATIONS\"$'\\n'\n                elif [ \"$STATE\" == \"open\" ]; then\n                  echo \"  ✅ Issue #$NUMBER is still open.\"\n                else\n                  echo \"  ⚠️  Could not determine issue status (state: $STATE)\"\n                fi\n              fi\n            else\n              echo \"  ⚠️  Could not parse URL: $URL\"\n            fi\n            echo \"\"\n          done\n\n          if [ \"$FAILED\" == \"true\" ]; then\n            echo \"❌ One or more upstream issues/PRs have been resolved!\"\n            echo \"Please review and remove the corresponding TODO comments and workarounds.\"\n            echo \"\"\n            echo \"Summary of resolved TODOs:\"\n            echo \"$FAILED_LOCATIONS\"\n            exit 1\n          else\n            echo \"✅ All upstream issues/PRs are still unresolved.\"\n          fi\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/manual.yml",
    "content": "name: Build and Deploy Manual\non:\n  push:\n    branches:\n      - main\njobs:\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - name: Install Nix\n        uses: cachix/install-nix-action@v20\n      - name: Build manual\n        run: |\n          nix build '.#manual' --show-trace  -vv -L\n          mkdir docs\n          curl https://nixos.org/favicon.ico -o docs/favicon.ico\n          cp result/index.html  docs/index.html\n      - name: Deploy 🚀\n        uses: JamesIves/github-pages-deploy-action@4.1.0\n        with:\n          branch: gh-pages # The branch the action should deploy to.\n          folder: docs # The folder the action should deploy.\n"
  },
  {
    "path": ".gitignore",
    "content": "result\n### Vim\n# Swap\n[._]*.s[a-v][a-z]\n!*.svg  # comment out if you don't need vector files\n[._]*.sw[a-p]\n[._]s[a-rt-v][a-z]\n[._]ss[a-gi-z]\n[._]sw[a-p]\n\n# Session\nSession.vim\nSessionx.vim\n\n# Temporary\n.netrwhist\n*~\n# Auto-generated tag files\ntags\n# Persistent undo\n[._]*.un~\n\ntags.lock\ntags.temp\n.direnv\n.env\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: local\n    hooks:\n      - id: treefmt\n        name: treefmt\n        entry: nix fmt\n        language: system\n        pass_filenames: false\n    # -   id: nix lint\n    #     name: Nix lint\n    #     entry: nix run 'nixpkgs#nix-linter'\n    #     files: \\.nix$\n    #     language: system\n"
  },
  {
    "path": ".woodpecker/x86-64-linux.yaml",
    "content": "{\n  \"labels\": {\n    \"backend\": \"local\",\n    \"platform\": \"linux/amd64\"\n  },\n  \"steps\": [\n    {\n      \"commands\": [\n        \"nix flake show\"\n      ],\n      \"image\": \"bash\",\n      \"name\": \"Nix flake show\"\n    },\n    {\n      \"commands\": [\n        \"attic login lounge-rocks https://cache.lounge.rocks $ATTIC_KEY --set-default\"\n      ],\n      \"environment\": {\n        \"ATTIC_KEY\": {\n          \"from_secret\": \"attic_key\"\n        }\n      },\n      \"image\": \"bash\",\n      \"name\": \"Setup Attic\"\n    },\n    {\n      \"commands\": [\n        \"nix-fast-build --no-nom --skip-cached --attic-cache lounge-rocks:nix-cache --flake \\\".#ciBuilds.x86_64-linux\\\"\"\n      ],\n      \"image\": \"bash\",\n      \"name\": \"Build all machines and push to cache\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.birne' -o 'result-birne'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build birne\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-birne')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show birne info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.clementine' -o 'result-clementine'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build clementine\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-clementine')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show clementine info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.fichte' -o 'result-fichte'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build fichte\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-fichte')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show fichte info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.kartoffel' -o 'result-kartoffel'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build kartoffel\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-kartoffel')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show kartoffel info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.kfbox' -o 'result-kfbox'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build kfbox\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-kfbox')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show kfbox info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.kiwi' -o 'result-kiwi'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build kiwi\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-kiwi')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show kiwi info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.limette' -o 'result-limette'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build limette\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-limette')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show limette info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.porree' -o 'result-porree'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build porree\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-porree')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show porree info\"\n    },\n    {\n      \"commands\": [\n        \"nix build --print-out-paths '.#ciBuilds.x86_64-linux.tanne' -o 'result-tanne'\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Build tanne\"\n    },\n    {\n      \"commands\": [\n        \"nix path-info --closure-size -h $(readlink -f 'result-tanne')\"\n      ],\n      \"failure\": \"ignore\",\n      \"image\": \"bash\",\n      \"name\": \"Show tanne info\"\n    }\n  ],\n  \"when\": [\n    {\n      \"event\": \"manual\"\n    },\n    {\n      \"branch\": \"main\",\n      \"event\": \"push\"\n    }\n  ]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "![nixos](https://socialify.git.ci/pinpox/nixos/image?description=1&font=Source%20Code%20Pro&forks=1&issues=1&logo=https%3A%2F%2Fpablo.tools%2Fnixoscolorful.svg&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Light)\n\n**Configuration checks:** [![Build Status](https://build.lounge.rocks/api/badges/9/status.svg)](https://build.lounge.rocks/repos/9)\n\nAll Module options are documented at: https://pinpox.github.io/nixos/\n\nThis repository includes all configurations for my NixOS machines. Feel free to\nuse parts of it as you please, but keep it mind it is intended mostly for\npersonal use. I've written posts about certain aspects of this setup on my\n[personal blog](https://pablo.tools/posts).\n\n# Overview\n\nThe structure of this repository is meant to allow easy manual deployment while\nbeing [clan](https://clan.lol) compatible. Individual hosts are defined in\n`/machines/<hostname>` and import re-usable parts of the configuration as\nneeded. Deployment and management is done with [clan](https://clan.lol), and\nsecrets are stored in [passage](https://github.com/FiloSottile/passage), which\nuses age for encryption.\n\nThe current hosts are:\n\n| Configuration                     | Type    | Location  | Description                          |\n| --------------------------------- | ------- | --------- | ------------------------------------ |\n| [kartoffel](./machines/kartoffel) | Desktop | local     | Main desktop workstation             |\n| [kiwi](./machines/kiwi)           | Laptop  | local     | Framework 13 (AMD AI 300 series)     |\n| [limette](./machines/limette)     | Laptop  | local     | Notebook                             |\n| [fichte](./machines/fichte)       | Laptop  | local     | ThinkPad T490                        |\n| [tanne](./machines/tanne)         | Laptop  | local     | ThinkPad T480s                       |\n| [uconsole](./machines/uconsole)   | Handheld| local     | Clockwork uConsole (RPi CM4)         |\n| [birne](./machines/birne)         | Server  | local     | Local NAS                            |\n| [porree](./machines/porree)       | Server  | netcup.de | Server for pablo.tools               |\n| [kfbox](./machines/kfbox)         | Server  | netcup.de | Server for 0cx.de                    |\n| [clementine](./machines/clementine) | Server | netcup.de | Server for megaclan3000.de           |\n| [traube](./machines/traube)       | Server  | local     | aarch64 server                       |\n\n# Deployment\n\nDeployment is done via [clan CLI](https://clan.lol) provided via the flake's\ndefault nix shell. I use [direnv](https://direnv.net/) to automatically start it\nwhen entering the repository's directory. Run `direnv allow` on the first time,\nafter that, deployment can be done via:\n\n```sh\nclan machines update <hostname>\n```\n\n## Repository Organization\n\nThe configuration is organized as follows:\n\n- `machines/<hostname>`: Host-specific configurations\n- `modules`: System-level NixOS modules\n- `home-manager/modules`: User-level home-manager modules for specific applications\n- `home-manager/profiles`: Profiles that combine multiple home-manager modules\n- `home-manager/packages`: Custom packages for applications not present in nixpkgs\n- `clan-service-modules`: [Clan](https://clan.lol) services\n\n# Contributing?\n\nWhile contributions don't make much sense for a personal configuration repository,\nI'm always happy to get hints, tips and constructive criticism. If you find something\nthat could be done in a better way, please let me know!\n\n<a href=\"https://www.buymeacoffee.com/pinpox\"><img src=\"https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=&slug=pinpox&button_colour=82aaff&font_colour=000000&font_family=Inter&outline_colour=000000&coffee_colour=FFDD00\" alt=\"Buy Me A Coffee\"></a>\n"
  },
  {
    "path": "clan-service-modules/machine-type/default.nix",
    "content": "{\n  _class = \"clan.service\";\n  manifest.name = \"machine-type\";\n  manifest.readme = \"Machine classification/profiles\";\n\n  roles.server.perInstance.nixosModule = ./server.nix;\n  roles.server.description = \"Server machine settings, no GUI\";\n  roles.desktop.perInstance.nixosModule = ./desktop.nix;\n  roles.desktop.description = \"Desktop machine settings, including wayland and sway\";\n  roles.mobile.perInstance.nixosModule = ./mobile.nix;\n  roles.mobile.description = \"Mobile/ARM device settings, lightweight desktop\";\n\n  # Common configuration for all macine types\n  perMachine.nixosModule =\n    { lib, ... }:\n    {\n      security.acme.acceptTerms = true;\n      security.acme.defaults.email = lib.mkDefault \"letsencrypt@pablo.tools\";\n      clan.core.settings.state-version.enable = true;\n      hardware.enableRedistributableFirmware = true;\n      hardware.enableAllHardware = true;\n    };\n}\n"
  },
  {
    "path": "clan-service-modules/machine-type/desktop.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  nur,\n  flake-self,\n  wl-harmonograph,\n  promterm,\n  home-manager,\n  ...\n}:\n{\n\n  imports = [\n    ./nextcloud-desktop.nix\n    home-manager.nixosModules.home-manager\n  ];\n\n  services.fwupd.enable = true;\n  services.acpid.enable = true;\n  services.upower.enable = true;\n\n  # To build raspi images\n  boot.binfmt.emulatedSystems = [ \"aarch64-linux\" ];\n\n  # Enable networkmanager\n  networking.networkmanager.enable = true;\n\n  # Often hangs\n  systemd.services = {\n    NetworkManager-wait-online.enable = lib.mkForce false;\n    systemd-networkd-wait-online.enable = lib.mkForce false;\n  };\n\n  hardware.keyboard.qmk.enable = true;\n\n  services.udev.packages = [\n    # pkgs.via\n    pkgs.qmk-udev-rules # For QMK/Via\n    pkgs.libsigrok # For pulseview\n  ];\n\n  # DON'T set useGlobalPackages! It's not necessary in newer\n  # home-manager versions and does not work with configs using\n  # nixpkgs.config`\n  home-manager.useUserPackages = true;\n\n  # Backup files before overwriting them with home-manager\n  home-manager.backupFileExtension = \"hm-backup\";\n\n  # Pass all flake inputs to home-manager modules aswell so we can use them\n  # there.\n  # home-manager.extraSpecialArgs = flake-self.inputs;\n  home-manager.extraSpecialArgs = {\n    inherit\n      wl-harmonograph\n      flake-self\n      nur\n      promterm\n      ;\n\n    # Pass system configuration (top-level \"config\") to home-manager modules,\n    # so we can access it's values for conditional statements. Writing is NOT possible!\n    system-config = config;\n  };\n\n  nixpkgs.overlays = [\n    nur.overlays.default\n    flake-self.overlays.default\n  ];\n\n  # TODO parametrize the username\n  home-manager.users.pinpox = flake-self.homeConfigurations.desktop;\n\n  # Hardware accelleration\n  hardware.graphics = {\n    enable = true;\n    enable32Bit = true;\n  };\n\n  pinpox = {\n    defaults = {\n      bluetooth.enable = true;\n      environment.enable = true;\n      storagebox = {\n        enable = true;\n        mountOnAccess = true;\n      };\n      fonts.enable = true;\n      locale.enable = true;\n      locale.automatic-timezone = true;\n      networking.enable = true;\n      nix.enable = true;\n      sound.enable = true;\n      zsh.enable = true;\n      yubikey.enable = true;\n      lvm-grub.enable = true;\n      # home-manager.enable = true;\n      # home-manager.configuration = flake-self.homeConfigurations.desktop;\n    };\n\n    virtualisation = {\n      docker.enable =false;\n      virt-manager.enable = true;\n      virtualbox.enable = false;\n    };\n\n    services = {\n\n      unbound-desktop.enable = false;\n\n      wayland.enable = true;\n      openssh.enable = true;\n\n      restic-client = {\n        enable = true;\n        backup-paths-onsite = [\n          \"/home/pinpox/Notes\"\n          \"/home/pinpox\"\n          \"/home/pinpox/.mozilla/firefox/pinpox/places.sqlite\"\n          # \"*/.local/share/password-store\"\n          # \"*/.passage\"\n          # \"*/.gnupg\"\n          # \"*/.ssh\"\n        ];\n        backup-paths-offsite = [\n          \"/home/pinpox/.mozilla/firefox/pinpox/places.sqlite\"\n          \"/home/pinpox/Notes\"\n          \"/home/pinpox\"\n        ];\n\n      };\n    };\n  };\n\n  # List packages installed in system profile. To search, run:\n  # $ nix search wget\n\n  environment.systemPackages = with pkgs; [\n\n    # irc-announce irc.hackint.org 6697 testbot992 '#lounge-rocks2' 1 \"test2\"\n    # pkgs.nur.repos.mic92.irc-announce\n\n    firefox\n    openspec\n\n    gcc\n    comma\n    acpi\n    arandr\n    binutils\n    file\n    git\n    gnumake\n    killall\n    lm_sensors\n    neovim\n    nixpkgs-review\n    nix-init\n    nix-update\n    nodejs\n    ripgrep\n    time\n    usbutils\n    wget\n  ];\n\n  services.logind.settings.Login.RuntimeDirectorySize = \"20G\";\n\n  boot = {\n    # Use GRUB2 as EFI boot loader.\n    loader.grub.useOSProber = true;\n    tmp.useTmpfs = false;\n  };\n\n  programs.dconf.enable = true;\n\n  # For user-space mounting things like smb:// and ssh:// in thunar etc. Dbus\n  # is required.\n  services.gvfs = {\n    enable = true;\n    # Default package does not support all protocols. Use the full-featured\n    # gnome version\n    package = lib.mkForce pkgs.gnome.gvfs;\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/machine-type/mobile.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  nur,\n  flake-self,\n\n  promterm,\n  home-manager,\n  ...\n}:\n{\n\n  imports = [\n    home-manager.nixosModules.home-manager\n  ];\n\n  services.acpid.enable = true;\n  services.upower.enable = true;\n\n  # US QWERTY for TTY (mobile devices don't have colemak keyboards)\n  console.keyMap = lib.mkForce \"us\";\n\n  # Enable networkmanager\n  networking.networkmanager.enable = true;\n\n  # Often hangs\n  systemd.services = {\n    NetworkManager-wait-online.enable = lib.mkForce false;\n    systemd-networkd-wait-online.enable = lib.mkForce false;\n  };\n\n  # DON'T set useGlobalPackages! It's not necessary in newer\n  # home-manager versions and does not work with configs using\n  # nixpkgs.config`\n  home-manager.useUserPackages = true;\n\n  # Backup files before overwriting them with home-manager\n  home-manager.backupFileExtension = \"hm-backup\";\n\n  # Pass all flake inputs to home-manager modules aswell so we can use them\n  # there.\n  home-manager.extraSpecialArgs = {\n    inherit\n      flake-self\n      nur\n      promterm\n      ;\n\n    # Pass system configuration (top-level \"config\") to home-manager modules,\n    # so we can access it's values for conditional statements. Writing is NOT possible!\n    system-config = config;\n  };\n\n  nixpkgs.overlays = [\n    nur.overlays.default\n    flake-self.overlays.default\n  ];\n\n  # TODO parametrize the username\n  home-manager.users.pinpox = flake-self.homeConfigurations.mobile;\n\n  # Hardware accelleration\n  hardware.graphics = {\n    enable = true;\n  };\n\n  pinpox = {\n    defaults = {\n      bluetooth.enable = true;\n      environment.enable = true;\n      fonts.enable = false; # Disabled - noto-fonts-color-emoji can't cross-compile\n      locale.enable = true;\n      locale.automatic-timezone = true;\n      networking.enable = true;\n      nix.enable = true;\n      sound.enable = false;\n      zsh.enable = true;\n    };\n\n    services = {\n      wayland.enable = true;\n      openssh.enable = true;\n    };\n  };\n\n  # List packages installed in system profile\n  environment.systemPackages = with pkgs; [\n    gcc\n    comma\n    acpi\n    binutils\n    file\n    git\n    gnumake\n    killall\n    neovim\n    ripgrep\n    time\n    usbutils\n    wget\n  ];\n\n  programs.dconf.enable = true;\n\n  # Disable speech-dispatcher to avoid pulling in mbrola-voices (675MB)\n  services.speechd.enable = lib.mkForce false;\n\n  # Override fontconfig defaults to avoid noto-fonts-color-emoji\n  # which requires Python tools that can't cross-compile\n  fonts.fontconfig.defaultFonts.emoji = lib.mkForce [ ];\n\n  # Disable default font packages (includes noto-fonts-color-emoji)\n  fonts.enableDefaultPackages = false;\n\n  # Disable modemmanager - cross-compilation is broken\n  networking.modemmanager.enable = false;\n}\n"
  },
  {
    "path": "clan-service-modules/machine-type/nextcloud-desktop.nix",
    "content": "{\n  # Nextcloud on the desktop\n  services.davfs2 = {\n    enable = true;\n    # settings.globalSection.use_locks = false;\n    #     TODO: Note: Ordinary users can mount a davfs2 file system if they are a\n    #     member of the group dav_group as defined in the system wide\n    #     configuration. Make sure the option 'dav_group' is enabled in the system\n    #     wide configuration file.\n  };\n  # fileSystems.\"/home/pinpox/Nextcloud\" = {\n  #   device = \"https://files.pablo.tools/remote.php/dav/files/pinpox\";\n  #   fsType = \"davfs\";\n  #   options = [\n  #     \"user\"\n  #     \"rw\"\n  #     \"noauto\" # I'll mount it manually\n  #   ];\n  # };\n}\n"
  },
  {
    "path": "clan-service-modules/machine-type/scanners.nix",
    "content": "{\n  hardware.sane.enable = true;\n  users.users.pinpox.extraGroups = [\n    \"scanner\"\n    \"lp\"\n  ];\n}\n"
  },
  {
    "path": "clan-service-modules/machine-type/server.nix",
    "content": "{\n  restic-exporter,\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\n{\n\n  imports = [\n    restic-exporter.nixosModules.default\n  ];\n\n  # Limit log size for journal\n  services.journald.extraConfig = \"SystemMaxUse=1G\";\n\n  environment.systemPackages = with pkgs; [\n    universal-ctags\n    git\n    gnumake\n    go\n    htop\n    neovim\n    nix-index\n    nixfmt\n    ripgrep\n    wget\n    ncdu\n    duf\n    tmux\n  ];\n\n  pinpox.defaults = {\n    environment.enable = true;\n    locale.enable = true;\n    nix.enable = true;\n    zsh.enable = true;\n    networking.enable = true;\n  };\n\n  pinpox.services.openssh.enable = true;\n\n  # Backups\n  pinpox.services = {\n    restic-client = {\n      enable = true;\n      backup-paths-onsite = [\n        config.services.postgresqlBackup.location\n        \"/home\"\n        \"/root\"\n      ];\n    };\n  };\n\n  # Backup Postgres, if it is running\n  services.postgresqlBackup = {\n    enable = config.services.postgresql.enable;\n    startAt = \"*-*-* 01:15:00\";\n    location = \"/var/backup/postgresql\";\n    backupAll = true;\n  };\n\n  # Remove wayland dependencies on server machine type\n  nixpkgs.overlays = [\n\n    (final: super: {\n      passage = super.passage.override {\n        wl-clipboard = null;\n        xclip = null;\n      };\n\n      neovim = super.neovim.override {\n        waylandSupport = false;\n      };\n    })\n  ];\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/alert-rules.nix",
    "content": "{ lib, meta }:\n\nlet\n  # docker's filesystems disappear quickly, leading to false positives\n  deviceFilter = ''path!~\"^(/var/lib/docker|/nix/store).*\"'';\nin\nlib.mapAttrsToList\n  (name: opts: {\n    alert = name;\n    expr = opts.condition;\n    for = opts.time or \"2m\";\n    labels = { };\n    annotations.description = opts.description;\n  })\n  ({\n\n    # prometheus_too_many_restarts = {\n    #   condition = ''changes(process_start_time_seconds{job=~\"prometheus|alertmanager\"}[15m]) > 2'';\n    #   description = \"Prometheus has restarted more than twice in the last 15 minutes. It might be crashlooping.\";\n    # };\n\n    # alert_manager_config_not_synced = {\n    #   condition = ''count(count_values(\"config_hash\", alertmanager_config_hash)) > 1'';\n    #   description = \"Configurations of AlertManager cluster instances are out of sync.\";\n    # };\n\n    #alert_manager_e2e_dead_man_switch = {\n    #  condition = \"vector(1)\";\n    #  description = \"Prometheus DeadManSwitch is an always-firing alert. It's used as an end-to-end test of Prometheus through the Alertmanager.\";\n    #};\n\n    # prometheus_not_connected_to_alertmanager = {\n    #   condition = \"prometheus_notifications_alertmanagers_discovered < 1\";\n    #   description = \"Prometheus cannot connect the alertmanager\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\";\n    # };\n\n    # prometheus_rule_evaluation_failures = {\n    #   condition = \"increase(prometheus_rule_evaluation_failures_total[3m]) > 0\";\n    #   description = \"Prometheus encountered {{ $value }} rule evaluation failures, leading to potentially ignored alerts.\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\";\n    # };\n\n    # prometheus_template_expansion_failures = {\n    #   condition = \"increase(prometheus_template_text_expansion_failures_total[3m]) > 0\";\n    #   time = \"0m\";\n    #   description = \"Prometheus encountered {{ $value }} template text expansion failures\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\";\n    # };\n\n    # promtail_file_lagging = {\n    #   condition = ''abs(promtail_file_bytes_total - promtail_read_bytes_total) > 1e6'';\n    #   time = \"15m\";\n    #   description = ''{{ $labels.instance }} {{ $labels.job }} {{ $labels.path }} has been lagging by more than 1MB for more than 15m.'';\n    # };\n\n    filesystem_full_80percent = {\n      condition = ''100 - ((node_filesystem_avail_bytes{fstype!=\"rootfs\",mountpoint=\"/\"} * 100) / node_filesystem_size_bytes{fstype!=\"rootfs\",mountpoint=\"/\"}) > 80'';\n      time = \"10m\";\n      description = \"{{$labels.instance}} device {{$labels.device}} on {{$labels.mountpoint}} got less than 20% space left on its filesystem.\";\n    };\n\n    # filesystem_inodes_full = {\n    #   condition = ''disk_inodes_free / disk_inodes_total < 0.10'';\n    #   time = \"10m\";\n    #   description = \"{{$labels.instance}} device {{$labels.device}} on {{$labels.mountpoint}} got less than 10% inodes left on its filesystem.\";\n    # };\n\n    # daily_task_not_run = {\n    #   # give 6 hours grace period\n    #   condition = ''time() - task_last_run{state=\"ok\",frequency=\"daily\"} > (24 + 6) * 60 * 60'';\n    #   description = \"{{$labels.instance}}: {{$labels.name}} was not run in the last 24h\";\n    # };\n\n    # TODO for backups:\n    # - Has not run\n    # - No data for a wheek\n    # - Too old\n    # - to drastic file or size change\n    #   description = \"status of ${name} is unknown: no data for a day\";\n    #   description = \"{{$labels.instance}}: {{$labels.name}} was not run in the last week\";\n    #   description = \"status of restic is unknown: no data for a week\";\n\n    # homeassistant = {\n    #   condition = ''\n    #     homeassistant_entity_available{domain=\"persistent_notification\", entity!=\"persistent_notification.http_login\"} >= 0'';\n    #   description =\n    #     \"homeassistant notification {{$labels.entity}} ({{$labels.friendly_name}}): {{$value}}\";\n    # };\n\n    swap_using_20percent = {\n      condition = \"node_memory_SwapTotal_bytes - (node_memory_SwapCached_bytes + node_memory_SwapFree_bytes) > node_memory_SwapTotal_bytes * 0.2\";\n      time = \"30m\";\n      description = \"{{$labels.instance}} is using 20% of its swap space for at least 30 minutes.\";\n    };\n\n    systemd_service_failed = {\n      condition = ''node_systemd_unit_state{state=\"failed\"} == 1'';\n      description = \"{{$labels.instance}} failed to (re)start service {{$labels.name}}.\";\n    };\n\n    restic_backup_too_old = {\n      condition = ''(time() - restic_snapshots_latest_time)/(60*60) > 24'';\n      description = \"{{$labels.instance}} not backed up for more than 24 hours. ({{$value}})\";\n    };\n\n    host_down = {\n      condition = ''up{job=\"node-stats\", instance!~\"limette.${meta.domain}:9100|kartoffel.${meta.domain}:9100\"} == 0'';\n      description = \"{{$labels.instance}} is down!\";\n    };\n\n    # service_not_running = {\n    #   condition = ''systemd_units_active_code{name=~\"teamspeak3-server.service|tt-rss.service\", sub!=\"running\"}'';\n    #   description = \"{{$labels.instance}} should have a running {{$labels.name}}.\";\n    # };\n\n    ram_using_90percent = {\n      condition = \"node_memory_Buffers_bytes + node_memory_MemFree_bytes + node_memory_Cached_bytes < node_memory_MemTotal_bytes * 0.1\";\n      time = \"1h\";\n      description = \"{{$labels.instance}} is using at least 90% of its RAM for at least 1 hour.\";\n    };\n\n    cpu_using_90percent = {\n      condition = ''100 - (avg by (instance) (irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100) >= 90'';\n      time = \"10m\";\n      description = \"{{$labels.instance}} is running with cpu usage > 90% for at least 10 minutes: {{$value}}\";\n    };\n\n    reboot = {\n      condition = \"node_boot_time_seconds < 300\";\n      description = \"{{$labels.instance}} just rebooted.\";\n    };\n\n    uptime = {\n      condition = \"(time() - node_boot_time_seconds ) / (60*60*24) > 30\";\n      description = \"Uptime monster: {{$labels.instance}} has been up for more than 30 days.\";\n    };\n\n    flake_nixpkgs_outdated = {\n      condition = ''(time() - flake_input_last_modified{input=\"nixpkgs\"}) / (60*60*24) > 30'';\n      description = \"Nixpkgs outdated: Nixpkgs on {{$labels.instance}} has not been updated in 30 days\";\n    };\n\n    /*\n      ping = {\n      condition = \"ping_result_code{type!='mobile'} != 0\";\n      description = \"{{$labels.url}}: ping from {{$labels.instance}} has failed!\";\n      };\n\n      ping_high_latency = {\n      condition = \"ping_average_response_ms{type!='mobile'} > 5000\";\n      description = \"{{$labels.instance}}: ping probe from {{$labels.source}} is encountering high latency!\";\n      };\n    */\n    http_status = {\n      condition = ''probe_http_status_code{instance!~\"https://megaclan3000.de\"} != 200'';\n      description = \"http request failed from {{$labels.instance}}: {{$labels.result}}!\";\n    };\n    /*\n      http_match_failed = {\n      condition = \"http_response_response_string_match == 0\";\n      description = \"{{$labels.server}} : http body not as expected; status code: {{$labels.status_code}}!\";\n      };\n      dns_query = {\n      condition = \"dns_query_result_code != 0\";\n      description = \"{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}}!\";\n      };\n      secure_dns_query = {\n      condition = \"secure_dns_state != 0\";\n      description = \"{{$labels.domain}} : could retrieve A record {{$labels.instance}} from server {{$labels.server}}: {{$labels.result}} for protocol {{$labels.protocol}}!\";\n      };\n      connection_failed = {\n      condition = \"net_response_result_code != 0\";\n      description = \"{{$labels.server}}: connection to {{$labels.port}}({{$labels.protocol}}) failed from {{$labels.instance}}\";\n      };\n      healthchecks = {\n      condition = \"hc_check_up == 0\";\n      description = \"{{$labels.instance}}: healtcheck {{$labels.job}} fails!\";\n      };\n    */\n    cert_expiry = {\n      condition = \"(probe_ssl_earliest_cert_expiry - time())/(3600*24) < 30\";\n      description = \"{{$labels.instance}}: The TLS certificate will expire in less than 30 days: {{$value}}s\";\n    };\n\n    # ignore devices that disabled S.M.A.R.T (example if attached via USB)\n\n    # smart_errors = {\n    #   condition = ''smart_device_health_ok{enabled!=\"Disabled\"} != 1'';\n    #   description =\n    #     \"{{$labels.instance}}: S.M.A.R.T reports: {{$labels.device}} ({{$labels.model}}) has errors.\";\n    # };\n\n    oom_kills = {\n      condition = \"increase(node_vmstat_oom_kill[5m]) > 0\";\n      description = \"{{$labels.instance}}: OOM kill detected\";\n    };\n\n    /*\n      unusual_disk_read_latency = {\n      condition =\n      \"rate(diskio_read_time[1m]) / rate(diskio_reads[1m]) > 0.1 and rate(diskio_reads[1m]) > 0\";\n      description = ''\n      {{$labels.instance}}: Disk latency is growing (read operations > 100ms)\n      '';\n      };\n\n      unusual_disk_write_latency = {\n      condition =\n      \"rate(diskio_write_time[1m]) / rate(diskio_write[1m]) > 0.1 and rate(diskio_write[1m]) > 0\";\n      description = ''\n      {{$labels.instance}}: Disk latency is growing (write operations > 100ms)\n      '';\n      };\n    */\n\n    host_memory_under_memory_pressure = {\n      condition = \"rate(node_vmstat_pgmajfault[1m]) > 1000\";\n      description = \"{{$labels.instance}}: The node is under heavy memory pressure. High rate of major page faults: {{$value}}\";\n    };\n\n    # ext4_errors = {\n    #   condition = \"ext4_errors_value > 0\";\n    #   description =\n    #     \"{{$labels.instance}}: ext4 has reported {{$value}} I/O errors: check /sys/fs/ext4/*/errors_count\";\n    # };\n\n    # alerts_silences_changed = {\n    #   condition = ''abs(delta(alertmanager_silences{state=\"active\"}[1h])) >= 1'';\n    #   description =\n    #     \"alertmanager: number of active silences has changed: {{$value}}\";\n    # };\n  })\n"
  },
  {
    "path": "clan-service-modules/monitoring/alertmanager-irc-relay.nix",
    "content": "{ pkgs, config, ... }:\nlet\n  am-irc-conf = {\n    # listening host/port.\n    http_host = \"localhost\";\n    http_port = 8667;\n\n    # Connect to this IRC host/port.\n    irc_host = \"irc.hackint.org\";\n    irc_port = 6697;\n    # irc_host_password = \"myserver_password\";\n    irc_nickname = \"alertus-maximus\";\n    irc_nickname_password = \"mynickserv_key\";\n    irc_realname = \"myrealname\";\n\n    irc_channels = [ { name = \"#lounge-rocks-log\"; } ];\n\n    msg_once_per_alert_group = false;\n    # Use PRIVMSG instead of NOTICE (default) to send messages.\n    use_privmsg = true;\n\n    # Define how IRC messages should be formatted.\n    msg_template = \"⚠ ⚠ ⚠ [{{.Labels.instance}}] - {{ .Labels.alertname }} is {{.Status}} ⚠ ⚠ ⚠ {{.Annotations.description}} (@pinpox act accordingly)\";\n    # Note: When sending only one message per alert group the default\n    # msg_template is set to\n    # \"Alert {{ .GroupLabels.alertname }} for {{ .GroupLabels.job }} is {{ .Status }}\"\n\n    # Set the internal buffer size for alerts received but not yet sent to IRC.\n    alert_buffer_size = 2048;\n  };\n\n  confPath = pkgs.writeText \"config.yml\" (builtins.toJSON am-irc-conf);\nin\n{\n  # User and group\n  users.groups.\"alertmanager-irc-relay\" = { };\n  users.users.\"alertmanager-irc-relay\" = {\n    isSystemUser = true;\n    group = \"alertmanager-irc-relay\";\n  };\n\n  # Service\n  systemd.services.alertmanager-irc-relay = {\n    wantedBy = [ \"multi-user.target\" ];\n    serviceConfig = {\n      ExecStart = \"${pkgs.alertmanager-irc-relay}/bin/alertmanager-irc-relay --config ${confPath}\";\n      User = config.users.users.alertmanager-irc-relay.name;\n      Group = config.users.users.alertmanager-irc-relay.name;\n    };\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/blackbox.nix",
    "content": "{ pkgs, ... }:\n{\n  services.prometheus.exporters.blackbox = {\n    enable = true;\n    # Default port is 9115\n    # Listen on 0.0.0.0, but we only open the firewall for wg-clan\n    openFirewall = false;\n\n    configFile = pkgs.writeTextFile {\n      name = \"blackbox-exporter-config\";\n      text = ''\n        modules:\n          http_2xx:\n            prober: http\n            timeout: 5s\n            http:\n              valid_http_versions: [\"HTTP/1.1\", \"HTTP/2.0\"]\n              valid_status_codes: []  # Defaults to 2xx\n              method: GET\n              no_follow_redirects: false\n              fail_if_ssl: false\n              fail_if_not_ssl: false\n              tls_config:\n                insecure_skip_verify: false\n              preferred_ip_protocol: \"ip4\" # defaults to \"ip6\"\n              ip_protocol_fallback: true  # fallback to \"ip6\"\n      '';\n    };\n  };\n\n  networking.firewall.interfaces.wg-clan.allowedTCPPorts = [ 9115 ];\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/default.nix",
    "content": "{ lib, ... }:\n{\n  _class = \"clan.service\";\n  manifest.name = \"monitoring\";\n  manifest.description = \"Prometheus/Loki/Grafana monitoring stack\";\n  manifest.readme = ''\n    \t  Self-hosted monitoring with one role per component (prometheus, loki,\n    \t  grafana, blackbox, node-exporter, alertmanager-irc-relay)\n    \t  '';\n  manifest.categories = [ \"Monitoring\" ];\n  manifest.exports.out = [ \"endpoints\" \"auth\" ];\n\n  roles.node-exporter = {\n    description = \"Prometheus node-exporter for host metrics (assign to every host you want scraped)\";\n    perInstance.nixosModule = ./node-exporter.nix;\n  };\n\n  roles.loki = {\n    description = \"Loki log aggregation server\";\n    perInstance.nixosModule = ./loki.nix;\n  };\n\n  roles.grafana = {\n    description = \"Grafana dashboard server\";\n    interface =\n      { lib, meta, ... }:\n      {\n        options = {\n          domain = lib.mkOption {\n            type = lib.types.str;\n            default = \"status.${meta.domain}\";\n            example = \"dashboards.example.com\";\n            description = \"Domain for grafana\";\n          };\n\n          oidc = {\n            enable = lib.mkEnableOption \"OIDC-only authentication (e.g. via Authelia). Disables the local login form.\";\n\n            issuer = lib.mkOption {\n              type = lib.types.str;\n              default = \"\";\n              example = \"https://auth.example.com\";\n              description = \"Base URL of the OIDC provider (Authelia). Used to derive auth/token/userinfo endpoints.\";\n            };\n\n            clientId = lib.mkOption {\n              type = lib.types.str;\n              default = \"grafana\";\n              description = \"OIDC client ID registered with the provider\";\n            };\n\n            providerName = lib.mkOption {\n              type = lib.types.str;\n              default = \"Authelia\";\n              description = \"Display name shown on the Grafana login button\";\n            };\n          };\n        };\n      };\n    perInstance =\n      {\n        settings,\n        roles,\n        meta,\n        mkExports,\n        ...\n      }:\n      let\n\n\n\t\t  # TODO The generator shoudl be independeat of authelia or kanidm. Use a dependant one for authelia to create the hash\n\t\t  # TODO maybe we do not need a generato cofig at all, have a default?\n\n\n        # Generator definition without runtimeInputs (pkgs isn't available\n        # at inventory-eval time). Each nixosModule that declares this\n        # generator adds runtimeInputs locally where pkgs IS available.\n        oidcGenerator = lib.optionalAttrs settings.oidc.enable {\n          share = true;\n          files.client_secret = {\n            owner = \"grafana\";\n            group = \"authelia-main\";\n            mode = \"0440\";\n          };\n          files.client_secret_hash.owner = \"authelia-main\";\n          script = ''\n            mkdir -p $out\n            openssl rand -hex 32 > $out/client_secret\n            authelia crypto hash generate argon2 --password \"$(cat $out/client_secret)\" \\\n              | sed 's/^Digest: //' > $out/client_secret_hash\n          '';\n        };\n      in\n      {\n        exports = mkExports (\n          { endpoints.hosts = [ settings.domain ]; }\n          // lib.optionalAttrs settings.oidc.enable {\n            auth.client = {\n              clientId = settings.oidc.clientId;\n              clientName = \"Grafana\";\n              redirectUris = [ \"https://${settings.domain}/login/generic_oauth\" ];\n              scopes = [\n                \"openid\"\n                \"profile\"\n                \"email\"\n                \"groups\"\n              ];\n              public = false;\n            };\n            auth.varsGenerator = oidcGenerator;\n          }\n        );\n        nixosModule = import ./grafana.nix {\n          inherit settings roles meta oidcGenerator;\n        };\n      };\n  };\n\n  roles.blackbox = {\n    description = \"Prometheus blackbox-exporter for HTTP/TLS probes\";\n    perInstance.nixosModule = ./blackbox.nix;\n  };\n\n  roles.alertmanager-irc-relay = {\n    description = \"Relay alertmanager notifications to an IRC channel\";\n    perInstance.nixosModule = ./alertmanager-irc-relay.nix;\n  };\n\n  roles.prometheus = {\n    description = \"Prometheus server (with bundled alertmanager). Discovers node-exporter and blackbox role members automatically.\";\n    interface =\n      { lib, meta, ... }:\n      {\n        options = {\n          domain = lib.mkOption {\n            type = lib.types.str;\n            default = \"prometheus.${meta.domain}\";\n            example = \"metrics.example.com\";\n            description = \"Domain for the prometheus web UI (reverse proxied via caddy)\";\n          };\n          blackboxTargets = lib.mkOption {\n            type = lib.types.listOf lib.types.str;\n            default = [ \"https://pablo.tools\" ];\n            example = [ \"https://github.com\" ];\n            description = \"Targets to monitor with the blackbox-exporter\";\n          };\n          jsonTargets = lib.mkOption {\n            type = lib.types.listOf lib.types.str;\n            default = [ ];\n            example = [ \"http://birne.pin/restic-ahorn.json\" ];\n            description = \"Targets to probe with the json-exporter\";\n          };\n          webExternalUrl = lib.mkOption {\n            type = lib.types.str;\n            default = \"https://vpn.prometheus.pablo.tools\";\n            description = \"Prometheus web external URL\";\n          };\n          alertmanagerWebExternalUrl = lib.mkOption {\n            type = lib.types.str;\n            default = \"https://vpn.alerts.pablo.tools\";\n            description = \"Alertmanager web external URL\";\n          };\n        };\n      };\n    perInstance =\n      {\n        settings,\n        roles,\n        meta,\n        mkExports,\n        ...\n      }:\n      let\n        # oauth2-proxy OIDC generator definition (without runtimeInputs —\n        # added by each nixosModule where pkgs is available).\n        # Produces: client_secret (raw), client_secret_hash (argon2 for\n        # authelia), envfile (oauth2-proxy env with client+cookie secrets).\n        prometheusOidcGenerator = {\n          share = true;\n          files.client_secret = {\n            owner = \"oauth2_proxy\";\n            group = \"authelia-main\";\n            mode = \"0440\";\n          };\n          files.client_secret_hash.owner = \"authelia-main\";\n          files.envfile = {\n            owner = \"oauth2_proxy\";\n            mode = \"0400\";\n          };\n          script = ''\n            mkdir -p $out\n            CLIENT_SECRET=$(openssl rand -hex 32)\n            COOKIE_SECRET=$(openssl rand -hex 16)\n            printf '%s' \"$CLIENT_SECRET\" > $out/client_secret\n            authelia crypto hash generate argon2 --password \"$CLIENT_SECRET\" \\\n              | sed 's/^Digest: //' > $out/client_secret_hash\n            printf 'OAUTH2_PROXY_CLIENT_SECRET=%s\\nOAUTH2_PROXY_COOKIE_SECRET=%s\\n' \\\n              \"$CLIENT_SECRET\" \"$COOKIE_SECRET\" > $out/envfile\n          '';\n        };\n      in\n      {\n        exports = mkExports (\n          { endpoints.hosts = [ settings.domain ]; }\n          // {\n            auth.client = {\n              clientId = \"prometheus\";\n              clientName = \"Prometheus\";\n              redirectUris = [ \"https://${settings.domain}/oauth2/callback\" ];\n              scopes = [\n                \"openid\"\n                \"profile\"\n                \"email\"\n                \"groups\"\n              ];\n              public = false;\n            };\n            auth.varsGenerator = prometheusOidcGenerator;\n          }\n        );\n        nixosModule = import ./prometheus.nix {\n          inherit settings roles meta prometheusOidcGenerator;\n        };\n      };\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/grafana.nix",
    "content": "{ settings, roles, meta, oidcGenerator }:\n{ config, lib, pkgs, ... }:\nlet\n  prometheusHosts = builtins.attrNames (roles.prometheus.machines or { });\n  lokiHosts = builtins.attrNames (roles.loki.machines or { });\n\n  oidc = settings.oidc;\nin\n{\n  # SMTP password file\n  clan.core.vars.generators.\"grafana\".prompts.smtp-password.persist = true;\n\n  # OIDC client secret — declared here so the producing host (where grafana\n  # runs) has the files. The same generator definition is also exported via\n  # auth.varsGenerator so the authelia host declares it too (share=true).\n  # runtimeInputs is added here (not in the export) because pkgs is only\n  # available inside nixosModule, not at inventory-eval time.\n  clan.core.vars.generators.\"authelia-oidc-${oidc.clientId}\" = lib.mkIf oidc.enable (\n    oidcGenerator\n    // {\n      runtimeInputs = with pkgs; [\n        coreutils\n        openssl\n        authelia\n        gnused\n      ];\n    }\n  );\n\n  # Grafana secret key for signing\n  clan.core.vars.generators.\"grafana-secret-key\" = {\n    files.\"secret-key\".owner = \"grafana\";\n    runtimeInputs = [ pkgs.openssl ];\n    script = ''\n      openssl rand -hex 32 > \"$out/secret-key\"\n    '';\n  };\n\n  # Backup Graphana dir, contains stateful config\n  pinpox.services.restic-client.backup-paths-offsite = [ \"/var/lib/grafana\" ];\n\n  # Reverse proxy\n  services.caddy = {\n    enable = true;\n    virtualHosts.\"${settings.domain}\".extraConfig = \"reverse_proxy 127.0.0.1:9005\";\n  };\n\n  # Graphana fronend\n  services.grafana = {\n\n    enable = true;\n\n    settings = {\n      server = {\n        domain = settings.domain;\n        root_url = \"https://${settings.domain}/\";\n        # Default is 3000\n        http_port = 9005;\n        http_addr = \"127.0.0.1\";\n      };\n\n      security.secret_key = \"$__file{${config.clan.core.vars.generators.\"grafana-secret-key\".files.\"secret-key\".path}}\";\n\n      # Mail notifications\n      smtp = {\n        enabled = true;\n        host = \"smtp.sendgrid.net:587\";\n        user = \"apikey\";\n        passwordFile = \"${config.clan.core.vars.generators.\"grafana\".files.\"smtp-password\".path}\";\n        fromAddress = \"status@pablo.tools\";\n      };\n    }\n    // lib.optionalAttrs oidc.enable {\n      # SCIM is enabled by default in recent Grafana versions and intercepts\n      # OIDC user provisioning, causing \"Failed to create user: user not\n      # found\". Disable it so the standard generic_oauth flow can create users.\n      feature_toggles.enableSCIM = false;\n\n      # OIDC-only login: hide the username/password form, do not auto-create\n      # local users via signup, redirect straight to the OIDC provider.\n      auth = {\n        disable_login_form = true;\n        signout_redirect_url = \"${oidc.issuer}/logout\";\n        # Self-healing OIDC user linking: if user_auth has no row for the\n        # OIDC subject (e.g. fresh DB restore, or Authelia rebuilt and the\n        # sub UUID changed), Grafana falls back to matching by email and\n        # auto-creates the user_auth link. Safe here because Authelia is a\n        # fully trusted IdP we control.\n        oauth_allow_insecure_email_lookup = true;\n      };\n      users = {\n        # Must be true so the OIDC provisioning path can create new users.\n        # disable_login_form = true (above) already prevents the local signup\n        # form from being rendered, so this only enables OIDC-initiated signup.\n        allow_sign_up = true;\n        auto_assign_org = true;\n        auto_assign_org_role = \"Admin\";\n      };\n      \"auth.generic_oauth\" = {\n        enabled = true;\n        name = oidc.providerName;\n        icon = \"signin\";\n        auto_login = true;\n        client_id = oidc.clientId;\n        client_secret = \"$__file{${config.clan.core.vars.generators.\"authelia-oidc-${oidc.clientId}\".files.client_secret.path}}\";\n        scopes = \"openid profile email groups\";\n        empty_scopes = false;\n        auth_url = \"${oidc.issuer}/api/oidc/authorization\";\n        token_url = \"${oidc.issuer}/api/oidc/token\";\n        api_url = \"${oidc.issuer}/api/oidc/userinfo\";\n        login_attribute_path = \"preferred_username\";\n        email_attribute_path = \"email\";\n        name_attribute_path = \"name\";\n        groups_attribute_path = \"groups\";\n        # Authelia already restricts who can reach the client via its\n        # authorization policy, so anyone who successfully logs in is granted\n        # Admin in Grafana.\n        role_attribute_path = \"'Admin'\";\n        allow_sign_up = true;\n        use_pkce = true;\n      };\n    };\n\n    # TODO provision the dashboards as currently configured\n\n    provision.datasources.settings = {\n      datasources =\n        lib.optional (prometheusHosts != [ ]) {\n          name = \"Prometheus\";\n          url = \"http://${builtins.head prometheusHosts}.${meta.domain}:9090\";\n          type = \"prometheus\";\n          isDefault = true;\n        }\n        ++ lib.optional (lokiHosts != [ ]) {\n          name = \"loki\";\n          url = \"http://${builtins.head lokiHosts}.${meta.domain}:3100\";\n          type = \"loki\";\n        };\n    };\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/loki.nix",
    "content": "{ ... }:\nlet\n  port-loki = 3100;\nin\n{\n  pinpox.services.restic-client.backup-paths-exclude = [ \"/var/lib/loki\" ];\n\n  networking.firewall = {\n    enable = true;\n    interfaces.wg-clan.allowedTCPPorts = [ port-loki ];\n  };\n\n  services.loki = {\n    enable = true;\n    configuration = {\n      auth_enabled = false;\n\n      server.http_listen_port = port-loki;\n\n      ingester = {\n        lifecycler = {\n          address = \"0.0.0.0\";\n          ring = {\n            kvstore.store = \"inmemory\";\n            replication_factor = 1;\n          };\n          final_sleep = \"0s\";\n        };\n\n        # Any chunk not receiving new logs in this time will be flushed\n        chunk_idle_period = \"1h\";\n\n        # All chunks will be flushed when they hit this age, default is 1h\n        max_chunk_age = \"1h\";\n        # Loki will attempt to build chunks up to 1.5MB, flushing first if\n        # chunk_idle_period or max_chunk_age is reached first\n        chunk_target_size = 1048576;\n\n        # Must be greater than index read cache TTL if using an index cache (Default\n        # index read cache TTL is 5m)\n        chunk_retain_period = \"30s\";\n      };\n\n      schema_config.configs = [\n        {\n          from = \"2020-10-24\";\n          store = \"boltdb-shipper\";\n          object_store = \"filesystem\";\n          schema = \"v13\";\n          index = {\n            prefix = \"index_\";\n            period = \"24h\";\n          };\n        }\n      ];\n\n      storage_config = {\n\n        boltdb_shipper = {\n          active_index_directory = \"/var/lib/loki/boltdb-shipper-active\";\n          cache_location = \"/var/lib/loki/boltdb-shipper-cache\";\n\n          # Can be increased for faster performance over longer query periods,\n          # uses more disk space\n          cache_ttl = \"24h\";\n        };\n\n        filesystem.directory = \"/var/lib/loki/chunks\";\n      };\n\n      limits_config = {\n        reject_old_samples = true;\n        reject_old_samples_max_age = \"168h\";\n        allow_structured_metadata = false;\n      };\n\n      table_manager = {\n        retention_deletes_enabled = false;\n        retention_period = \"0s\";\n      };\n\n      compactor.working_directory = \"/var/lib/loki/boltdb-shipper-compactor\";\n    };\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/node-exporter.nix",
    "content": "{ ... }:\n{\n  services.prometheus.exporters.node = {\n    enable = true;\n    # Default port is 9100\n    # Listen on 0.0.0.0, but we only open the firewall for wg-clan\n    openFirewall = false;\n    enabledCollectors = [\n      \"cgroups\"\n      \"systemd\"\n    ];\n\n    extraFlags = [ \"--collector.textfile.directory=/etc/nix\" ];\n  };\n\n  networking.firewall.interfaces.wg-clan.allowedTCPPorts = [ 9100 ];\n}\n"
  },
  {
    "path": "clan-service-modules/monitoring/prometheus.nix",
    "content": "{ settings, roles, meta, prometheusOidcGenerator }:\n{\n  lib,\n  pkgs,\n  config,\n  flake-self,\n  pinpox-utils,\n  ...\n}:\nlet\n  nodeExporterHosts = builtins.attrNames (roles.\"node-exporter\".machines or { });\nin\n{\n  clan.core.vars.generators.\"restic-exporter\" = pinpox-utils.mkEnvGenerator [\n    \"RESTIC_PASSWORD\"\n    \"AWS_ACCESS_KEY_ID\"\n    \"AWS_SECRET_ACCESS_KEY\"\n    \"RESTIC_REPOSITORY\"\n  ];\n\n  # OIDC client secret generator — declared here so the producing host has the\n  # files. The same definition is exported via auth.varsGenerator so the\n  # authelia host also declares it (share=true). runtimeInputs added here\n  # because pkgs isn't available at inventory-eval time.\n  clan.core.vars.generators.\"authelia-oidc-prometheus\" = prometheusOidcGenerator // {\n    runtimeInputs = with pkgs; [\n      coreutils\n      openssl\n      authelia\n      gnused\n    ];\n  };\n\n  # oauth2-proxy sits between Caddy and Prometheus and handles the OIDC\n  # client flow against Authelia. Prometheus itself stays on 127.0.0.1:9090\n  # (loopback only); the only way to reach it is through oauth2-proxy on\n  # 127.0.0.1:4180, which Caddy reverse-proxies as ${settings.domain}.\n  services.oauth2-proxy = {\n    enable = true;\n    provider = \"oidc\";\n    clientID = \"prometheus\";\n    keyFile = config.clan.core.vars.generators.\"authelia-oidc-prometheus\".files.envfile.path;\n    oidcIssuerUrl = \"https://auth.pablo.tools\";\n    redirectURL = \"https://${settings.domain}/oauth2/callback\";\n    upstream = [ \"http://127.0.0.1:9090\" ];\n    httpAddress = \"http://127.0.0.1:4180\";\n    cookie.secure = true;\n    cookie.refresh = \"1h\";\n    email.domains = [ \"*\" ];\n    setXauthrequest = true;\n    reverseProxy = true;\n    extraConfig = {\n      skip-provider-button = true;\n      # Authelia's prometheus client is registered with require_pkce = true,\n      # so oauth2-proxy must send a PKCE code_challenge on the authorize\n      # request and the matching verifier on the token exchange.\n      code-challenge-method = \"S256\";\n      # Authelia's authorization_policies already restrict who can reach this\n      # client; oauth2-proxy doesn't need to enforce its own allowlist.\n    };\n  };\n\n  # Reverse proxy for the prometheus web UI. The pki clan service auto-issues\n  # a TLS cert for ${settings.domain} and prepends a `tls` directive to this\n  # vhost; dm-dns distributes a CNAME so any clan-internal host can resolve\n  # it. Caddy hands traffic to oauth2-proxy, which performs the OIDC dance\n  # against Authelia and (if authorized) proxies upstream to Prometheus.\n  services.caddy = {\n    enable = true;\n    virtualHosts.\"${settings.domain}\".extraConfig = \"reverse_proxy 127.0.0.1:4180\";\n  };\n\n  services.prometheus = {\n    enable = true;\n\n    # Bind to localhost only — defense in depth on top of the host firewall.\n    # The only way in from outside the host is via Caddy → oauth2-proxy →\n    # 127.0.0.1:9090.\n    listenAddress = \"127.0.0.1\";\n\n    # Disable config checks. They will fail because they run sandboxed and\n    # can't access external files, e.g. the secrets stored in /run/keys\n    # https://github.com/NixOS/nixpkgs/blob/d89d7af1ba23bd8a5341d00bdd862e8e9a808f56/nixos/modules/services/monitoring/prometheus/default.nix#L1732-L1738\n    checkConfig = false;\n\n    webExternalUrl = settings.webExternalUrl;\n    extraFlags = [\n      \"--log.level=debug\"\n      \"--storage.tsdb.retention.size='6GB'\"\n    ];\n    ruleFiles = [\n      (pkgs.writeText \"prometheus-rules.yml\" (\n        builtins.toJSON {\n          groups = [\n            {\n              name = \"alerting-rules\";\n              rules = import ./alert-rules.nix { inherit lib meta; };\n            }\n          ];\n        }\n      ))\n    ];\n    alertmanagers = [ { static_configs = [ { targets = [ \"localhost:9093\" ]; } ]; } ];\n\n    scrapeConfigs = [\n      {\n        job_name = \"backup-reports\";\n        scrape_interval = \"60m\";\n        metrics_path = \"/probe\";\n        static_configs = [ { targets = settings.jsonTargets; } ];\n\n        relabel_configs = [\n          {\n            source_labels = [ \"__address__\" ];\n            target_label = \"__param_target\";\n          }\n          {\n            source_labels = [ \"__param_target\" ];\n            target_label = \"instance\";\n          }\n          {\n            target_label = \"__address__\";\n            replacement = \"127.0.0.1:7979\"; # The blackbox exporter's real hostname:port.\n          }\n        ];\n      }\n      # TODO: move restic-client to a clan service so this can be discovered\n      # via roles.<restic>.machines instead of filtering flake-self.\n      {\n        job_name = \"restic-exporter\";\n        scrape_interval = \"1h\";\n        metrics_path = \"/probe\";\n        static_configs = [\n          {\n            targets = (\n              builtins.attrNames (\n                lib.filterAttrs (\n                  n: v: v.config.pinpox.services.restic-client.enable\n                ) flake-self.nixosConfigurations\n              )\n            );\n          }\n        ];\n        relabel_configs = [\n          {\n            source_labels = [ \"__address__\" ];\n            target_label = \"__param_target\";\n          }\n          {\n            source_labels = [ \"__param_target\" ];\n            target_label = \"instance\";\n          }\n          {\n            target_label = \"__address__\";\n            replacement = \"127.0.0.1:${builtins.toString config.services.restic-exporter.port}\";\n          }\n        ];\n      }\n      {\n        job_name = \"blackbox\";\n        scrape_interval = \"2m\";\n        metrics_path = \"/probe\";\n        params = {\n          module = [ \"http_2xx\" ];\n        };\n        static_configs = [ { targets = settings.blackboxTargets; } ];\n\n        relabel_configs = [\n          {\n            source_labels = [ \"__address__\" ];\n            target_label = \"__param_target\";\n          }\n          {\n            source_labels = [ \"__param_target\" ];\n            target_label = \"instance\";\n          }\n          {\n            target_label = \"__address__\";\n            replacement = \"127.0.0.1:9115\"; # The blackbox exporter's real hostname:port.\n          }\n        ];\n      }\n      {\n        job_name = \"node-stats\";\n        static_configs = [\n          {\n            # Hosts assigned the \"node-exporter\" role of this monitoring instance\n            targets = map (h: \"${h}.${meta.domain}:9100\") nodeExporterHosts;\n          }\n        ];\n      }\n    ];\n    alertmanager = {\n      enable = true;\n      # port = 9093; # Default\n      listenAddress = \"127.0.0.1\";\n      webExternalUrl = settings.alertmanagerWebExternalUrl;\n      environmentFile = /var/src/secrets/alertmanager/envfile;\n      configuration = {\n\n        route = {\n          receiver = \"all\";\n          group_by = [ \"instance\" ];\n          group_wait = \"30s\";\n          group_interval = \"2m\";\n          repeat_interval = \"24h\";\n        };\n\n        receivers = [\n          {\n            name = \"all\";\n            webhook_configs = [\n              { url = \"http://127.0.0.1:11000/alert\"; } # matrix-hook\n              { url = with config.services.alertmanager-ntfy; \"http://${httpAddress}:${httpPort}\"; } # alertmanger-ntfy\n            ];\n          }\n        ];\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/navidrome.nix",
    "content": "{ ... }:\n{\n  _class = \"clan.service\";\n  manifest.name = \"navidrome\";\n  manifest.description = \"Navidrome music streaming server\";\n  manifest.readme = \"Self-hosted music streaming server with subsonic API\";\n  manifest.categories = [ \"Media\" ];\n  manifest.exports.out = [ \"endpoints\" ];\n\n  roles.default = {\n    description = \"Sets up navidrome music server with caddy reverse proxy\";\n    interface =\n      { lib, meta, ... }:\n      {\n        options = {\n          host = lib.mkOption {\n            type = lib.types.str;\n            default = \"music.${meta.domain}\";\n            description = \"Host serving the navidrome instance\";\n            example = \"party.0cx.de\";\n          };\n        };\n      };\n\n    perInstance =\n      {\n        settings,\n        mkExports,\n        ...\n      }:\n      {\n\n        exports = mkExports { endpoints.hosts = [ settings.host ]; };\n\n        nixosModule =\n          { config, ... }:\n          {\n            config = {\n              # Reverse proxy\n              services.caddy = {\n                enable = true;\n                virtualHosts.\"${settings.host}\".extraConfig =\n                  \"reverse_proxy 127.0.0.1:${toString config.services.navidrome.settings.Port}\";\n              };\n\n              # Mount storagebox\n              pinpox.defaults.storagebox = {\n                enable = true;\n                mountOnAccess = false;\n              };\n\n              # Add navidrome user to storage-users group for access to storagebox\n              users.users.navidrome.extraGroups = [ \"storage-users\" ];\n\n              # Set up navidrome\n              services.navidrome = {\n                enable = true;\n                settings.Port = 4533;\n                settings.Address = \"127.0.0.1\";\n                settings.MusicFolder = \"${config.pinpox.defaults.storagebox.mountPoint}/music\";\n              };\n\n              # Ensure storagebox is mounted before navidrome starts\n              systemd.services.navidrome = {\n                requires = [ \"mnt-storagebox.mount\" ];\n                after = [ \"mnt-storagebox.mount\" ];\n              };\n            };\n          };\n      };\n  };\n}\n"
  },
  {
    "path": "clan-service-modules/thelounge.nix",
    "content": "{ ... }:\n{\n  _class = \"clan.service\";\n  manifest.name = \"thelounge\";\n  manifest.description = \"The Lounge IRC client and bouncer\";\n  manifest.readme = \"Self-hosted IRC client and bouncer with web interface\";\n  manifest.categories = [ \"Communication\" ];\n  manifest.exports.out = [ \"endpoints\" ];\n\n  roles.default = {\n    description = \"Sets up The Lounge IRC client with caddy reverse proxy\";\n    interface =\n      { lib, meta, ... }:\n      {\n        options = {\n          host = lib.mkOption {\n            type = lib.types.str;\n            default = \"irc.${meta.domain}\";\n            description = \"Host serving the The Lounge instance.\";\n          };\n        };\n      };\n\n    perInstance =\n      {\n        settings,\n        mkExports,\n        ...\n      }:\n      {\n        exports = mkExports { endpoints.hosts = [ settings.host ]; };\n\n        nixosModule =\n          { config, ... }:\n          {\n            config = {\n              # Reverse proxy\n              services.caddy = {\n                enable = true;\n                virtualHosts.\"${settings.host}\".extraConfig =\n                  \"reverse_proxy 127.0.0.1:${toString config.services.thelounge.port}\";\n              };\n\n              # Set up The Lounge\n              services.thelounge = {\n                enable = true;\n                port = 9090;\n                public = false;\n                extraConfig = {\n                  host = \"127.0.0.1\";\n                  reverseProxy = true;\n                  storagePolicy = {\n                    enabled = true;\n                    maxAgeDays = 365;\n                    deletionPolicy = \"everything\";\n                  };\n                  theme = \"morning\";\n                };\n              };\n\n              # Backup paths\n              pinpox.services.restic-client.backup-paths-offsite = [\n                \"/var/lib/thelounge/certificates\"\n                \"/var/lib/thelounge/config.js\"\n                # Don't backup logs for now - too big.\n                # \"/var/lib/thelounge/logs\"\n                # \"/var/lib/thelounge/packages\"\n                \"/var/lib/thelounge/sts-policies.json\"\n                \"/var/lib/thelounge/users\"\n                \"/var/lib/thelounge/vapid.json\"\n              ];\n            };\n          };\n      };\n  };\n}\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"My machines\";\n\n  inputs = {\n\n    wrappers.url = \"github:lassulus/wrappers\";\n    wrappers.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    nixpkgs.url = \"github:nixos/nixpkgs/nixos-unstable?shallow=1\";\n\n\tclan-core.url = \"https://git.clan.lol/clan/clan-core/archive/main.tar.gz\";\n    clan-core.inputs.nixpkgs.follows = \"nixpkgs\";\n    clan-core.inputs.disko.follows = \"disko\";\n    clan-core.inputs.data-mesher.follows = \"data-mesher\";\n\n    data-mesher.url = \"git+https://git.clan.lol/clan/data-mesher\";\n    data-mesher.inputs.nixpkgs.follows = \"nixpkgs\";\n\n\tclan-community.url = \"https://git.clan.lol/clan/clan-community/archive/main.tar.gz\";\n    clan-community.inputs.clan-core.follows = \"clan-core\";\n    clan-community.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    rio.url = \"github:pinpox/rio\";\n    rio.inputs.nixpkgs.follows = \"nixpkgs\";\n    rio.inputs.systems.follows = \"clan-core/systems\";\n\n    khard.url = \"github:lucc/khard\";\n    khard.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    gif-searcher.url = \"github:pinpox/gif-searcher\";\n    gif-searcher.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    nix-index-database.url = \"github:nix-community/nix-index-database\";\n    nix-index-database.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    disko.url = \"github:nix-community/disko/latest\";\n    disko.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    jitsi-matrix-presence.url = \"github:pinpox/jitsi-matrix-presence\";\n    jitsi-matrix-presence.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    nixos-hardware.url = \"github:NixOS/nixos-hardware/master\";\n\n    aoe-taunt-discord-bot.url = \"github:pinpox/aoe-taunt-discord-bot\";\n    aoe-taunt-discord-bot.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    pinpox-keys.url = \"https://github.com/pinpox.keys\";\n    pinpox-keys.flake = false;\n\n    pinpox-neovim.url = \"github:pinpox/pinpox-neovim\";\n    pinpox-neovim.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    radio.url = \"github:pinpox/radio\";\n    radio.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    mc3000.url = \"github:pinpox/mc3000\";\n    mc3000.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    naersk.url = \"github:nix-community/naersk/master\";\n    naersk.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    promterm.url = \"github:pinpox/promterm\";\n    promterm.inputs = {\n      nixpkgs.follows = \"nixpkgs\";\n      naersk.follows = \"naersk\";\n    };\n\n    go-karma-bot.url = \"github:pinpox/go-karma-bot\";\n    go-karma-bot.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    retiolum.url = \"git+https://git.thalheim.io/Mic92/retiolum\";\n    retiolum.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    flake-compat.url = \"github:edolstra/flake-compat\";\n    flake-compat.flake = false;\n\n    home-manager.url = \"github:nix-community/home-manager\";\n    home-manager.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    distro.url = \"path:/home/pinpox/code/github.com/generational-infrastructure/distro\";\n    distro.inputs.nixpkgs.follows = \"nixpkgs\";\n    distro.inputs.opencrow.follows = \"opencrow\";\n    distro.inputs.treefmt-nix.follows = \"treefmt-nix\";\n\n    nur.url = \"github:pinpox/NUR\";\n    nur.inputs.nixpkgs.follows = \"nixpkgs\";\n    nur.inputs.flake-parts.follows = \"clan-core/flake-parts\";\n\n    wl-harmonograph.url = \"github:pinpox/wl-harmonograph\";\n    wl-harmonograph.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    restic-exporter.url = \"github:pinpox/restic-exporter\";\n    restic-exporter.inputs.nixpkgs.follows = \"nixpkgs\";\n    restic-exporter.inputs.flake-utils.follows = \"age-plugin-picohsm/flake-utils\";\n\n    alertmanager-ntfy = {\n      url = \"github:pinpox/alertmanager-ntfy\";\n      inputs = {\n        nixpkgs.follows = \"nixpkgs\";\n        flake-compat.follows = \"flake-compat\";\n        flake-utils.follows = \"age-plugin-picohsm/flake-utils\";\n      };\n    };\n\n    matrix-hook.url = \"github:pinpox/matrix-hook\";\n    matrix-hook.inputs = {\n      nixpkgs.follows = \"nixpkgs\";\n      flake-compat.follows = \"flake-compat\";\n      flake-utils.follows = \"age-plugin-picohsm/flake-utils\";\n    };\n\n    # ZSH plugins\n    zsh-abbrev-alias.url = \"github:momo-lab/zsh-abbrev-alias\";\n    zsh-abbrev-alias.flake = false;\n\n    zsh-colored-man-pages.url = \"github:ael-code/zsh-colored-man-pages\";\n    zsh-colored-man-pages.flake = false;\n\n    jj-zsh-prompt.url = \"github:pinpox/jj-zsh-prompt\";\n    jj-zsh-prompt.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    zsh-async.url = \"github:mafredri/zsh-async\";\n    zsh-async.flake = false;\n\n    nix-apple-fonts = {\n      url = \"github:pinpox/nix-apple-fonts\";\n      inputs.flake-compat.follows = \"flake-compat\";\n      inputs.flake-utils.follows = \"age-plugin-picohsm/flake-utils\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n\n    treefmt-nix.follows = \"clan-core/treefmt-nix\";\n\n    age-plugin-picohsm.url = \"github:pinpox/age-plugin-picohsm\";\n    age-plugin-picohsm.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    passage-secret-service.url = \"github:pinpox/passage-secret-service\";\n    passage-secret-service.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    llm-agents.url = \"github:numtide/llm-agents.nix\";\n    llm-agents.inputs.nixpkgs.follows = \"nixpkgs\";\n    llm-agents.inputs.treefmt-nix.follows = \"treefmt-nix\";\n\n    opencrow.url = \"github:pinpox/opencrow\";\n    opencrow.inputs.nixpkgs.follows = \"nixpkgs\";\n    opencrow.inputs.treefmt-nix.follows = \"treefmt-nix\";\n\n    mics-skills.url = \"github:Mic92/mics-skills\";\n    mics-skills.inputs.nixpkgs.follows = \"nixpkgs\";\n    mics-skills.inputs.treefmt-nix.follows = \"treefmt-nix\";\n\n    twitch-first.url = \"github:pinpox/twitch-first\";\n    twitch-first.inputs.nixpkgs.follows = \"nixpkgs\";\n\n    trippy-track.url = \"github:pinpox/trippy-track\";\n    trippy-track.inputs.nixpkgs.follows = \"nixpkgs\";\n  };\n  outputs =\n    { self, ... }@inputs:\n    with inputs;\n    let\n\n      # System types to support.\n      supportedSystems = [\n        \"x86_64-linux\"\n        \"x86_64-darwin\"\n        \"aarch64-linux\"\n        \"aarch64-darwin\"\n      ];\n\n      # Helper function to generate an attrset '{ x86_64-linux = f \"x86_64-linux\"; ... }'.\n      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;\n\n      # Nixpkgs instantiated for supported system types.\n      nixpkgsFor = forAllSystems (\n        system:\n        import nixpkgs {\n          inherit system;\n          overlays = [ self.overlays.default ];\n        }\n      );\n\n      # treefmt configuration\n      treefmtEval = import ./formatter.nix { inherit treefmt-nix nixpkgsFor forAllSystems; };\n\n      clan = clan-core.lib.clan {\n\n        # this needs to point at the repository root\n        inherit self;\n\n        # Vars backend configuration (moved from machine-level)\n        vars.settings.secretStore = \"password-store\";\n\n        # Make inputs and the flake itself accessible as module parameters.\n        # Technically, adding the inputs is redundant as they can be also\n        # accessed with flake-self.inputs.X, but adding them individually\n        # allows to only pass what is needed to each module.\n        specialArgs = {\n          flake-self = self;\n        }\n        // inputs;\n\n        # Register custom clan service modules\n        # desktop, localsend, wireguard-star moved to clan-community\n        modules.\"@pinpox/navidrome\" = ./clan-service-modules/navidrome.nix;\n        modules.\"@pinpox/thelounge\" = ./clan-service-modules/thelounge.nix;\n        modules.\"@pinpox/machine-type\" = ./clan-service-modules/machine-type;\n        modules.\"@pinpox/monitoring\" = ./clan-service-modules/monitoring;\n        inventory = import ./inventory.nix { inherit self; };\n      };\n    in\n    {\n\n      devShells = forAllSystems (\n        system: with nixpkgsFor.${system}; {\n          default = pkgs.mkShell {\n            packages = [\n              clan-core.packages.${system}.clan-cli\n              treefmtEval.${system}.config.build.wrapper\n            ];\n          };\n        }\n      );\n\n      # Custom packages added via the overlay are selectively exposed here, to\n      # allow using them from other flakes that import this one.\n      packages =\n        forAllSystems (\n          system: with nixpkgsFor.${system}; {\n            inherit\n              hello-custom\n              fritzbox_exporter\n              mqtt2prometheus\n              smartmon-script\n              machine-report\n              woodpecker-pipeline\n              manual\n              ;\n          }\n        )\n        // {\n          # Flashable SD card image for uconsole (uses binfmt emulation)\n          # Build with: nix build .#uconsole-image\n          # Then flash with: dd if=result/main.raw of=/dev/sdX bs=4M status=progress\n\n          # Build the image on the remote server\n          # ssh root@build01.clan.lol \\\n          #   'nix build github:pinpox/nixos#packages.aarch64-linux.uconsole-image -L'\n\n          # Download image (compressed)\n          # ssh root@build01.clan.lol \\\n          #  'zstd -c \\\n          #  $(nix build github:pinpox/nixos#packages.aarch64-linux.uconsole-image --print-out-paths)/main.raw' > \\\n          #  uconsole.img.zst\n\n          # Decompress\n          # zstd -d uconsole.img.zst\n\n          aarch64-linux = (forAllSystems (s: { })).aarch64-linux // {\n            uconsole-image = self.nixosConfigurations.uconsole.config.system.build.diskoImages;\n          };\n        };\n\n      # Expose overlay to flake outputs, to allow using it from other flakes.\n      # Flake inputs are passed to the overlay so that the packages defined in\n      # it can use the sources pinned in flake.lock\n      overlays.default = final: prev: (import ./overlays inputs self pinpox-utils) final prev;\n\n      # Use treefmt for 'nix fmt'\n      formatter = forAllSystems (system: treefmtEval.${system}.config.build.wrapper);\n\n      # Expose treefmt check for CI\n      checks = forAllSystems (system: {\n        formatting = treefmtEval.${system}.config.build.check self;\n      });\n\n      # Machine toplevels for CI builds and cache pushing\n      ciBuilds = forAllSystems (\n        system:\n        nixpkgs.lib.mapAttrs' (\n          name: config:\n          nixpkgs.lib.nameValuePair name config.config.system.build.toplevel\n        ) (nixpkgs.lib.filterAttrs (_: config: config.pkgs.stdenv.hostPlatform.system == system) self.nixosConfigurations)\n      );\n\n      # Each subdirectory in ./templates/<template-name> is a\n      # template, which can be used for new proects with:\n      # `nix flake init`\n      templates = builtins.listToAttrs (\n        map (name: {\n          inherit name;\n          value = {\n            path = ./templates + \"/${name}\";\n            description = (import (./templates + \"/${name}/flake.nix\")).description;\n          };\n        }) (builtins.attrNames (builtins.readDir ./templates))\n      );\n\n      # Output all modules in ./modules/<module-name> to flake. Modules should be in\n      # individual subdirectories and contain a default.nix file.\n      # Each subdirectory in ./modules/<module-name> is a nixos module\n      nixosModules = builtins.listToAttrs (\n        map (name: {\n          inherit name;\n          value = import (./modules + \"/${name}\");\n        }) (builtins.attrNames (builtins.readDir ./modules))\n      );\n\n      # Each subdirectory in ./machines/<machine-name> is a host config. Clan\n      # auto-imports all machines from ./machines\n      inherit (clan.config) clanInternals;\n      nixosConfigurations = clan.config.nixosConfigurations;\n      clan = clan.config;\n\n      # Each subdirectory in ./home-manager/profiles/<profile-name> is a\n      # home-manager profile\n      homeConfigurations = builtins.listToAttrs (\n        map\n          (name: {\n            inherit name;\n            value =\n              { ... }:\n              {\n                imports = [\n                  (./home-manager/profiles + \"/${name}\")\n                ]\n                ++ (builtins.attrValues self.homeManagerModules);\n              };\n          })\n          (\n            builtins.attrNames (\n              nixpkgs.lib.filterAttrs (n: v: v == \"directory\") (builtins.readDir ./home-manager/profiles)\n            )\n          )\n      );\n\n      # Each subdirectory in ./home-manager/modules/<module-name> is a\n      # home-manager module\n      homeManagerModules = builtins.listToAttrs (\n        map (name: {\n          inherit name;\n          value = import (./home-manager/modules + \"/${name}\");\n        }) (builtins.attrNames (builtins.readDir ./home-manager/modules))\n      );\n    };\n}\n"
  },
  {
    "path": "formatter.nix",
    "content": "{\n  treefmt-nix,\n  nixpkgsFor,\n  forAllSystems,\n}:\nforAllSystems (\n  system:\n  treefmt-nix.lib.evalModule nixpkgsFor.${system} {\n    projectRootFile = \"flake.nix\";\n    programs = {\n      nixfmt.enable = true;\n      flake-edit.enable = true;\n      nixfmt.package = nixpkgsFor.${system}.nixfmt;\n      prettier.enable = true;\n      shellcheck.enable = true;\n      shfmt.enable = true;\n    };\n    settings.formatter = {\n      prettier.includes = [\n        \"*.md\"\n        \"*.yaml\"\n        \"*.yml\"\n        \"*.json\"\n        \"*.toml\"\n      ];\n      shellcheck.includes = [ \"*.sh\" ];\n      shfmt.includes = [ \"*.sh\" ];\n    };\n  }\n)\n"
  },
  {
    "path": "home-manager/colorscheme.nix",
    "content": "{ lib, ... }:\nwith lib;\nlet\n  colornames = [\n    \"Black\"\n    \"BrightBlack\"\n    \"White\"\n    \"BrightWhite\"\n    \"Yellow\"\n    \"BrightYellow\"\n    \"Green\"\n    \"BrightGreen\"\n    \"Cyan\"\n    \"BrightCyan\"\n    \"Blue\"\n    \"BrightBlue\"\n    \"Magenta\"\n    \"BrightMagenta\"\n    \"Red\"\n    \"BrightRed\"\n  ];\nin\n{\n\n  options.pinpox.colors = builtins.listToAttrs (\n    map (c: {\n      name = c;\n      value = mkOption { type = types.str; };\n    }) colornames\n  );\n\n  config.pinpox.colors = {\n\n    Black = \"000000\"; # 000000\n    BrightBlack = \"262626\"; # 262626\n    White = \"d0d0d0\"; # d0d0d0\n    BrightWhite = \"ffffff\"; # ffffff\n    Red = \"d7005f\"; # d7005f\n    BrightRed = \"ff5f87\"; # ff5f87\n    Green = \"00af5f\"; # 00af5f\n    BrightGreen = \"00d75f\"; # 00d75f\n    Yellow = \"d78700\"; # d78700\n    BrightYellow = \"ffaf00\"; # ffaf00\n    Blue = \"0087d7\"; # 0087d7\n    BrightBlue = \"00afff\"; # 00afff\n    Magenta = \"d787d7\"; # d787d7\n    BrightMagenta = \"ff87ff\"; # ff87ff\n    Cyan = \"00afaf\"; # 00afaf\n    BrightCyan = \"00d7d7\"; # 00d7d7\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/audio-recording/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.audio-recording;\n  plugins = with pkgs; [\n    # Instruments\n    x42-avldrums\n    zynaddsubfx\n\n    # Effects\n    calf\n    lsp-plugins\n    zam-plugins\n    talentedhack\n    gxplugins-lv2\n    distrho\n  ];\nin\n{\n\n  # These settings yield 10ms@1024 spls latency in reaper for me:\n  # pw-metadata -n settings 0 clock.force-quantum 1024\n  # pw-metadata -n settings 0 clock.force-rate 96000\n\n  options.pinpox.defaults.audio-recording.enable =\n    mkEnableOption \"audio production setup (DAW and plugins)\";\n\n  config = mkIf cfg.enable {\n\n    home.packages =\n      with pkgs;\n      [\n        reaper\n        alsa-scarlett-gui\n      ]\n\n      # Some plugins also have a binary, so we also add them to PATH\n      ++ plugins;\n\n    # Place vst, vst3, clap, lv2 and ladspa plugins in the according\n    # directories where reaper will look for them\n    home.file =\n      let\n        all-audio-plugins = pkgs.symlinkJoin {\n          name = \"all-audio-plugins\";\n          paths = plugins;\n        };\n      in\n      {\n        all-lv2 = {\n          recursive = true;\n          source = \"${all-audio-plugins}/lib/lv2\";\n          target = \".lv2\";\n        };\n        all-clap = {\n          recursive = true;\n          source = \"${all-audio-plugins}/lib/clap\";\n          target = \".clap\";\n        };\n        all-vst = {\n          recursive = true;\n          source = \"${all-audio-plugins}/lib/vst\";\n          target = \".vst\";\n        };\n        all-vst3 = {\n          recursive = true;\n          source = \"${all-audio-plugins}/lib/vst3\";\n          target = \".vst3\";\n        };\n        all-ladspa = {\n          recursive = true;\n          source = \"${all-audio-plugins}/lib/ladspa\";\n          target = \".ladspa\";\n        };\n      };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/calendar/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  flake-inputs,\n  ...\n}:\n\nwith lib;\n\nlet\n  cfg = config.pinpox.defaults.calendar;\nin\n{\n  options.pinpox.defaults.calendar.enable = mkEnableOption \"Calendar config\";\n\n  config = mkIf cfg.enable {\n\n    programs.vdirsyncer.enable = true;\n    services.vdirsyncer.enable = true;\n\n    programs.khal.enable = true;\n    programs.khal.settings = {\n\n      default.default_calendar = \"Kalender\";\n\n      # view = {\n      #   agenda_event_format = \"{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol}{reset}\";\n      # };\n    };\n\n    programs.khard.enable = true;\n    programs.khard.package = flake-inputs.khard.packages.\"x86_64-linux\".default;\n\n    # khard.settings is broken, it does not support subsections, so we write\n    # the file directly\n    xdg.configFile.\"khard/khard.conf\".source = pkgs.writeTextFile {\n      name = \"khard.conf\";\n      text = ''\n        [addressbooks]\n        [[mailbox]]\n        path = ~/.local/share/office/contacts/mailbox/*\n        type = discover\n\n        [[gmail]]\n        path = ~/.local/share/office/contacts/gmail/*\n        type = discover\n\n        [[nextcloud]]\n        path = ~/.local/share/office/contacts/nextcloud/*\n        type = discover\n\n        [general]\n        debug = no\n        default_action = list\n        # These are either strings or comma seperated lists\n        editor = nvim, -i, NONE\n        merge_editor = nvim, -d\n      '';\n    };\n\n    accounts.contact.accounts = {\n\n      \"nextcloud\" = {\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n\n          userNameCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-nextcloud-user\"\n          ];\n        };\n        remote = {\n          type = \"carddav\";\n          url = \"https://files.pablo.tools\";\n          passwordCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-nextcloud\"\n          ];\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/contacts/nextcloud\";\n        };\n      };\n\n      \"mailbox\" = {\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n\n          userNameCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-mailbox-user\"\n          ];\n        };\n        remote = {\n          type = \"carddav\";\n          url = \"https://dav.mailbox.org/carddav/\";\n          passwordCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-mailbox\"\n          ];\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/contacts/mailbox\";\n        };\n      };\n\n      \"gmail\" = {\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n\n          clientIdCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-gmail-clientid\"\n          ];\n          clientSecretCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-gmail-clientsecret\"\n          ];\n          tokenFile = \"/home/pinpox/.local/share/vdirsyncer-contacts-gmail-token\";\n        };\n        remote = {\n          type = \"google_contacts\";\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/contacts/gmail\";\n        };\n      };\n    };\n\n    accounts.calendar.accounts = {\n\n      \"nextcloud\" = {\n\n        khal = {\n          enable = true;\n          type = \"discover\";\n          priority = 1;\n        };\n\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n\n          userNameCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-nextcloud-user\"\n          ];\n        };\n        remote = {\n          type = \"caldav\";\n          url = \"https://files.pablo.tools/\";\n\n          passwordCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-nextcloud\"\n          ];\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/calendars/nextcloud\";\n        };\n      };\n\n      \"mailbox\" = {\n\n        khal = {\n          enable = true;\n          type = \"discover\";\n          priority = 1;\n        };\n\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n\n          userNameCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-mailbox-user\"\n          ];\n        };\n        remote = {\n          type = \"caldav\";\n          url = \"https://dav.mailbox.org/caldav/\";\n\n          passwordCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-mailbox\"\n          ];\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/calendars/mailbox\";\n        };\n      };\n\n      \"icloud\" = {\n\n        khal = {\n          enable = true;\n          type = \"discover\";\n          priority = 2;\n        };\n\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n          userNameCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-icloud-user\"\n          ];\n        };\n        remote = {\n          type = \"caldav\";\n          url = \"https://caldav.icloud.com/\";\n          passwordCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-icloud\"\n          ];\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/calendars/icloud\";\n        };\n      };\n\n      \"gmail\" = {\n\n        khal = {\n          enable = true;\n          type = \"discover\";\n          priority = 3;\n        };\n\n        vdirsyncer = {\n          enable = true;\n          collections = [\n            \"from a\"\n            \"from b\"\n          ];\n          metadata = [\n            \"color\"\n            \"displayname\"\n          ];\n          clientIdCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-gmail-clientid\"\n          ];\n          clientSecretCommand = [\n            \"${pkgs.passage}/bin/passage\"\n            \"vdirsyncer-gmail-clientsecret\"\n          ];\n          tokenFile = \"/home/pinpox/.local/share/vdirsyncer-calendar-gmail-token\";\n        };\n        remote = {\n          type = \"google_calendar\";\n        };\n        local = {\n          type = \"filesystem\";\n          path = \"/home/pinpox/.local/share/office/calendars/gmail\";\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/chromium/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.programs.chromium;\nin\n{\n  options.pinpox.programs.chromium.enable = mkEnableOption \"chromium browser\";\n\n  config = mkIf cfg.enable {\n    programs.chromium = {\n      enable = true;\n      extensions = [\n        { id = \"nngceckbapebfimnlniiiahkandclblb\"; } # Bitwarden\n        { id = \"cjpalhdlnbpafiamejdnhcphjbkeiagm\"; } # Ublock Origin\n        { id = \"gcbommkclmclpchllfjekcdonpmejbdp\"; } # HTTPS everywhere\n        { id = \"mmpokgfcmbkfdeibafoafkiijdbfblfg\"; } # Merge windows\n        { id = \"agldajbhchobfgjcmmigehfdcjbmipne\"; } # Blank Dark New Tab\n      ];\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/claude-code/default.nix",
    "content": "{\n  pkgs,\n  config,\n  lib,\n  flake-self,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.claude-code;\n  mics-skills = flake-self.inputs.mics-skills;\n  mics-packages = mics-skills.packages.${pkgs.stdenv.hostPlatform.system};\n\n  firefoxPrefs = {\n    \"xpinstall.signatures.required\" = false;\n    \"extensions.autoDisableScopes\" = 0;\n    \"extensions.enabledScopes\" = 15;\n    \"browser.shell.checkDefaultBrowser\" = false;\n    \"datareporting.policy.dataSubmissionEnabled\" = false;\n    \"toolkit.telemetry.reportingpolicy.firstRun\" = false;\n    \"browser.uiCustomization.state\" = builtins.toJSON {\n      placements = {\n        widget-overflow-fixed-list = [ ];\n        unified-extensions-area = [ ];\n        nav-bar = [\n          \"back-button\"\n          \"forward-button\"\n          \"stop-reload-button\"\n          \"urlbar-container\"\n          \"downloads-button\"\n          \"browser-cli-controller_thalheim_io-browser-action\"\n          \"unified-extensions-button\"\n        ];\n        toolbar-menubar = [ \"menubar-items\" ];\n        TabsToolbar = [ \"tabbrowser-tabs\" \"new-tab-button\" \"alltabs-button\" ];\n        PersonalToolbar = [ \"personal-bookmarks\" ];\n      };\n      seen = [ \"browser-cli-controller_thalheim_io-browser-action\" ];\n      dirtyAreaCache = [ \"nav-bar\" ];\n      currentVersion = 20;\n      newElementCount = 0;\n    };\n  };\n\n  userJs = pkgs.writeText \"browser-cli-user.js\" (concatStringsSep \"\\n\" (\n    mapAttrsToList (k: v: \"user_pref(${builtins.toJSON k}, ${builtins.toJSON v});\") firefoxPrefs\n  ));\n\n  browser-cli-firefox = pkgs.writeShellScriptBin \"browser-cli-firefox\" ''\n    set -euo pipefail\n\n    PROFILE=\"$(mktemp -d)\"\n    trap 'rm -rf \"$PROFILE\"' EXIT\n\n    cp ${userJs} \"$PROFILE/user.js\"\n\n    mkdir -p \"$PROFILE/extensions\"\n    cp ${mics-packages.browser-cli-extension}/browser-cli-extension.xpi \\\n      \"$PROFILE/extensions/browser-cli-controller@thalheim.io.xpi\"\n\n    exec ${pkgs.firefox-devedition}/bin/firefox-devedition \\\n      --no-remote -profile \"$PROFILE\" \"$@\"\n  '';\nin\n{\n  imports = [\n    mics-skills.homeModules.default\n  ];\n\n  options.pinpox.programs.claude-code = {\n    enable = mkEnableOption \"claude-code\";\n  };\n\n  config = mkIf cfg.enable {\n\n    home.packages = [\n      pkgs.claude-code\n      browser-cli-firefox\n    ];\n\n    programs.mics-skills = {\n      enable = true;\n      package = mics-packages;\n      skills = [ \"browser-cli\" ];\n    };\n\n    # Declarative settings via settings.local.json (merged with mutable settings.json)\n    home.file.\".claude/settings.local.json\".text = builtins.toJSON {\n      permissions.allow = [\n        \"Bash(browser-cli:*)\"\n        \"Bash(browser-cli-firefox:*)\"\n        \"Bash(browser-cli-server:*)\"\n        \"Read(/tmp/**)\"\n      ];\n    };\n\n    # Supplement the browser-cli skill with browser launch instructions\n    home.file.\".claude/skills/browser-cli-setup/SKILL.md\".text = ''\n      ---\n      name: browser-cli-setup\n      description: How to start Firefox with the browser-cli extension pre-installed. Use this before using browser-cli commands.\n      ---\n\n      # Starting the browser\n\n      Before using `browser-cli`, you must launch Firefox Developer Edition\n      with the browser-cli extension already installed. Run:\n\n      ```bash\n      browser-cli-firefox &\n      ```\n\n      This starts Firefox Developer Edition with a temporary profile that has:\n      - The browser-cli WebExtension pre-installed\n      - Extension signature checks disabled\n      - Telemetry disabled\n\n      After the browser is running, use `browser-cli` commands as documented\n      in the browser-cli skill.\n    '';\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/credentials/age-recipients",
    "content": "age15m0pgtr06pqaql2wx8e2xqupcctzkf0dk0cc06hv3cjcgpe5u3ns59jaun\nage1picohsm1qjpqjd9pnlh8zem6wwz62ml9z995fsdz9e23dumamjj0nl4cq0m5dx5v5mm5njelm3hmv4w3mfs5mzvks3xtu6k723jr0am49hrk9mduxvxpps\n"
  },
  {
    "path": "home-manager/modules/credentials/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.credentials;\nin\n{\n  options.pinpox.defaults.credentials.enable = mkEnableOption \"credentials defaults\";\n\n  config = mkIf cfg.enable {\n\n    # Set up passage store directory with remote configured (but not cloned)\n    # On a fresh machine, just run: passage git pull\n    home.activation.setupPassageStore = lib.hm.dag.entryAfter [ \"writeBoundary\" ] ''\n      STORE_DIR=\"${config.home.homeDirectory}/.passage/store\"\n      if [ ! -d \"$STORE_DIR/.git\" ]; then\n        run mkdir -p \"$STORE_DIR\"\n        run ${pkgs.git}/bin/git -C \"$STORE_DIR\" init -b master\n        run ${pkgs.git}/bin/git -C \"$STORE_DIR\" remote add origin \"gitea@git.0cx.de:pinpox/passage.git\"\n        run ${pkgs.git}/bin/git -C \"$STORE_DIR\" config branch.master.remote origin\n        run ${pkgs.git}/bin/git -C \"$STORE_DIR\" config branch.master.merge refs/heads/master\n      fi\n    '';\n\n    # List of public keys (recipients) to encrypt to.\n    # Use `age-plugin-picohsm --list` to get the HSM key if needed. The second\n    # key is an offline last-resort backup key\n    home.sessionVariables.PASSAGE_RECIPIENTS_FILE = \"${./age-recipients}\";\n    programs.zsh.sessionVariables.PASSAGE_RECIPIENTS_FILE = \"${./age-recipients}\";\n\n    # The file ~/.config/age/identities still needs to be generated.\n    # Run `age-plugin-picohsm -list` and put the age-key identity\n    # (AGE-PLUGIN-PICOHSM-XXXXX) into the file. For sops, the recipients should\n    # be added as a comment in the line before. Full format example:\n    #\n    # # recipient: age1picohsm1qjpqxxxxxxxxxxxxxxxxxxxxxxxxx\n    # AGE-PLUGIN-PICOHSM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n    home.sessionVariables.PASSAGE_IDENTITIES_FILE = \"$HOME/.config/age/identities\";\n    programs.zsh.sessionVariables.PASSAGE_IDENTITIES_FILE = \"$HOME/.config/age/identities\";\n\n    # SOPS uses the same identity file as passage\n    home.sessionVariables.SOPS_AGE_KEY_FILE = \"$HOME/.config/age/identities\";\n    programs.zsh.sessionVariables.SOPS_AGE_KEY_FILE = \"$HOME/.config/age/identities\";\n\n    # The nixos agent is better\n    services.ssh-agent.enable = false;\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/easyeffects/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\n\nwith lib;\n\nlet\n  cfg = config.pinpox.programs.easyeffects;\n\nin\n{\n  options.pinpox.programs.easyeffects = {\n    enable = mkEnableOption \"EasyEffects audio effects\";\n  };\n\n  config = mkIf cfg.enable {\n    services.easyeffects = {\n      enable = true;\n      # preset = \"my-preset\";\n      # extraPresets = {\n      #   my-preset = {\n      #     input = {\n      #       blocklist = [\n      #\n      #       ];\n      #       \"plugins_order\" = [\n      #         \"rnnoise#0\"\n      #       ];\n      #       \"rnnoise#0\" = {\n      #         bypass = false;\n      #         \"enable-vad\" = false;\n      #         \"input-gain\" = 0.0;\n      #         \"model-path\" = \"\";\n      #         \"output-gain\" = 0.0;\n      #         release = 20.0;\n      #         \"vad-thres\" = 50.0;\n      #         wet = 0.0;\n      #       };\n      #     };\n      #   };\n      # };\n    };\n\n    # Add easyeffects to home packages\n    home.packages = with pkgs; [\n      easyeffects\n    ];\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/email/aerc-style.nix",
    "content": "{ config, ... }:\nwith config.pinpox.colors;\n{\n  \"*.default\" = true;\n  \"*error.bold\" = true;\n  \"statusline*.default\" = true;\n  \"border.bg\" = \"#${Black}\";\n  \"border.fg\" = \"#${Blue}\";\n  \"completion_pill.reverse\" = true;\n  \"default.bg\" = \"#${Black}\";\n  \"dirlist_default.selected.bg\" = \"#${BrightBlack}\";\n  \"dirlist_default.selected.fg\" = \"#${White}\";\n  \"dirlist_recent.selected.bg\" = \"#${White}\";\n  \"dirlist_recent.selected.fg\" = \"#${BrightBlack}\";\n  \"dirlist_unread.fg\" = \"#${BrightGreen}\";\n  \"dirlist_unread.selected.bg\" = \"#${BrightBlack}\";\n  \"dirlist_unread.selected.fg\" = \"#${BrightGreen}\";\n  \"error.fg\" = \"#${BrightRed}\";\n  \"header.bold\" = true;\n  \"header.fg\" = \"#${BrightBlue}\";\n  \"msglist_default.selected.bg\" = \"#${White}\";\n  \"msglist_default.selected.fg\" = \"#${BrightBlack}\";\n  \"msglist_deleted.fg\" = \"#${BrightRed}\";\n  \"msglist_deleted.selected.reverse\" = \"toggle\";\n  \"msglist_marked.fg\" = \"#${BrightYellow}\";\n  \"msglist_marked.selected.bg\" = \"#${BrightBlack}\";\n  \"msglist_marked.selected.fg\" = \"#${BrightYellow}\";\n  \"msglist_read.selected.bg\" = \"#${BrightBlack}\";\n  \"msglist_read.selected.fg\" = \"#${White}\";\n  \"msglist_result.fg\" = \"#${BrightYellow}\";\n  \"msglist_result.selected.bg\" = \"#${BrightBlack}\";\n  \"msglist_unread.bold\" = true;\n  \"msglist_unread.fg\" = \"#${BrightGreen}\";\n  \"msglist_unread.selected.bg\" = \"#${BrightBlack}\";\n  \"selector_chooser.bold\" = true;\n  \"selector_focused.reverse\" = true;\n  \"statusline_default.bg\" = \"#${Magenta}\";\n  \"statusline_default.fg\" = \"#${BrightWhite}\";\n  \"statusline_error.fg\" = \"#${BrightRed}\";\n  \"statusline_error.reverse\" = true;\n  \"success.fg\" = \"#${BrightGreen}\";\n  \"tab.bg\" = \"#${Blue}\";\n  \"tab.fg\" = \"#${White}\";\n  \"tab.selected.bg\" = \"#${BrightWhite}\";\n  \"tab.selected.fg\" = \"#${Blue}\";\n  \"title.reverse\" = true;\n  \"warning.fg\" = \"#${BrightYellow}\";\n}\n"
  },
  {
    "path": "home-manager/modules/email/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.email;\nin\n{\n  options.pinpox.defaults.email.enable = mkEnableOption \"email client config\";\n\n  config = mkIf cfg.enable {\n\n    accounts.email.maildirBasePath = \"Mail\";\n\n    programs.aerc = {\n      extraConfig.general.unsafe-accounts-conf = true;\n      enable = true;\n      # stylesets.pinpox = (import ./aerc-style.nix { inherit config;});\n\n      extraConfig = {\n        ui = {\n          # styleset-name = \"pinpox\";\n          icon-attachment = \"a \";\n          icon-old = \"\";\n          icon-replied = \"↩ \";\n        };\n        compose.address-book-cmd = \"carddav-query %s\";\n\n        filters = {\n          \"text/plain\" = \"colorize\";\n          \"text/calendar\" = \"calendar\";\n          \"message/delivery-status\" = \"colorize\";\n          \"message/rfc822\" = \"colorize\";\n          #text/html=pandoc -f html -t plain | colorize\n          \"text/html\" = \"! html\";\n          #text/html=! w3m -T text/html -I UTF-8\n          #text/*=bat -fP --file-name=\"$AERC_FILENAME\"\n          #application/x-sh=bat -fP -l sh\n          #image/*=catimg -w $(tput cols) -\n          #subject,~Git(hub|lab)=lolcat -f\n          #from,thatguywhodoesnothardwraphismessages=wrap -w 100 | colorize\n          \".headers\" = \"colorize\";\n          \"image/*\" = \"${pkgs.libsixel}/bin/img2sixel - \";\n          # image/*=catimg -w$(tput cols) -\n        };\n      };\n    };\n\n    accounts.email.accounts = {\n      pablo_tools = {\n        folders = {\n          # send = \"SENT\";\n          inbox = \"INBOX\";\n        };\n        aerc.enable = true;\n        address = \"mail@pablo.tools\";\n\n        aliases = [\n          \"git@pablo.tools\"\n          \"github@pablo.tools\"\n          \"pablo1@mailbox.org\"\n        ];\n\n        realName = \"Pablo Ovelleiro Corral\";\n        primary = true;\n        maildir.path = \"pablo_tools\";\n\n        signature = {\n          text = ''\n            Pablo Ovelleiro Corral\n\n            Web:     https://pablo.tools\n            Matrix:  @pinpox:matrix.org\n            Github:  https://github.com/pinpox\n          '';\n          showSignature = \"append\";\n        };\n\n        userName = \"pablo1@mailbox.org\";\n        passwordCommand = \"passage show mailbox.org/himalaya\";\n        imap = {\n          host = \"imap.mailbox.org\";\n          tls.enable = true;\n        };\n        smtp = {\n          host = \"smtp.mailbox.org\";\n          port = 465;\n        };\n      };\n    };\n\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/firefox/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.firefox;\nin\n{\n  options.pinpox.programs.firefox.enable = mkEnableOption \"firefox browser\";\n\n  config = mkIf cfg.enable {\n\n    # Browserpass\n    programs.browserpass = {\n      enable = true;\n      # browsers = [ \"chromium\" \"firefox\" ];\n      browsers = [ \"firefox\" ];\n    };\n\n    programs.firefox = {\n      enable = true;\n      package = pkgs.firefox;\n\n      profiles = {\n        pinpox = {\n\n          # containers = {\n\n          #   # dangerous = {\n          #   #   color = \"red\";\n          #   #   icon = \"fruit\";\n          #   #   id = 2;\n          #   # };\n\n          #   work = {\n          #     color = \"blue\";\n          #     icon = \"cart\";\n          #     id = 1;\n          #   };\n          # };\n\n          search = {\n            force = true;\n            engines = {\n              \"Nix Options\" = {\n                urls = [\n                  {\n                    template = \"https://search.nixos.org/options\";\n                    params = [\n                      {\n                        name = \"channel\";\n                        value = \"unstable\";\n                      }\n                      {\n                        name = \"query\";\n                        value = \"{searchTerms}\";\n                      }\n                    ];\n                  }\n                ];\n                icon = \"${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg\";\n                definedAliases = [ \"@no\" ];\n              };\n              \"Nix Packages\" = {\n                urls = [\n                  {\n                    template = \"https://search.nixos.org/packages\";\n                    params = [\n                      {\n                        name = \"type\";\n                        value = \"packages\";\n                      }\n                      {\n                        name = \"query\";\n                        value = \"{searchTerms}\";\n                      }\n                    ];\n                  }\n                ];\n                icon = \"${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg\";\n                definedAliases = [ \"@np\" ];\n              };\n            };\n          };\n\n          extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [\n            bitwarden\n            darkreader\n            web-search-navigator\n            ublock-origin\n            simple-tab-groups\n          ];\n\n          isDefault = true;\n          settings = {\n\n            # 0 => blank page\n            # 1 => your home page(s) {default}\n            # 2 => the last page viewed in Firefox\n            # 3 => previous session windows and tabs\n            \"browser.startup.page\" = \"3\";\n\n            # Set the homepage\n            \"browser.startup.homepage\" = \"https://nixos.org\";\n\n            # Export bookmarks to bookmarks.html when closing firefox\n            \"browser.bookmarks.autoExportHTML\" = \"true\";\n\n            # Path where to export. Default is:\n            # ~/.mozilla/firefox/pinpox/bookmarks.html\n            # \"browser.bookmarks.file\" =\n\n            # \"browser.display.background_color\" = \"#${config.pinpox.colors.Black}\";\n            # \"browser.display.foreground_color\" = \"#${config.pinpox.colors.White}\";\n            \"browser.display.use_system_colors\" = \"true\";\n            \"browser.anchor_color\" = \"#${config.pinpox.colors.Yellow}\";\n            \"browser.display.use_document_colors\" = \"false\";\n            # \"browser.search.region\" = \"GB\";\n            # \"browser.search.isUS\" = false;\n            # \"distribution.searchplugins.defaultLocale\" = \"en-GB\";\n            # \"general.useragent.locale\" = \"en-GB\";\n            # \"browser.bookmarks.showMobileBookmarks\" = true;\n            # TODO if possible, enable sync (log in)\n            \"toolkit.legacyUserProfileCustomizations.stylesheets\" = true;\n            \"extensions.activeThemeID\" = \"default-theme@mozilla.org\";\n            \"devtools.theme\" = \"dark\";\n            \"dom.security.https_only_mode\" = \"true\"; # HTTPS everywhere\n\n            # Disable password managger\n            \"signon.rememberSignons\" = \"false\";\n            \"signon.autofillForms\" = \"false\";\n            \"signon.autofillForms.http\" = \"false\";\n          };\n\n          # userChrome = builtins.readFile\n          #   (utils.renderMustache \"userChrome.css\" ./userchrome.css.mustache\n          #     { colors = config.pinpox.colors; font = fonts; });\n\n          # TODO\n          userContent = ''\n            @import url(\"userChrome.css\");\n\n            /* Removes white loading page */\n            @-moz-document url(about:blank), url(about:newtab), url(about:home) {\n              html:not(#ublock0-epicker), html:not(#ublock0-epicker) body, #newtab-customize-overlay {\n                background: var(--mff-bg) !important;\n              }\n            }\n          '';\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/firefox/userchrome.css.mustache",
    "content": ":root {\n\n\t\t/* Minimal Functional Fox variables*/\n\t\t--mff-bg: #{{colors.Black}};\n\t\t--mff-icon-color: #{{colors.Blue}};\n\t\t--mff-nav-toolbar-padding: 8px;\n\t\t--mff-sidebar-bg: var(--mff-bg);\n\t\t--mff-sidebar-color: #{{colors.White}};\n\n\t\t--mff-tab-border-radius: 0px;\n\t\t--mff-tab-color: #{{colors.White}};\n\t\t--mff-tab-font-family: \"{{font.normal}}\";\n\t\t--mff-tab-font-size: 11pt;\n\t\t--mff-tab-font-weight: 500;\n\t\t--mff-tab-height: 32px;\n\t\t--mff-tab-pinned-bg: #{{colors.Magenta}};\n\t\t--mff-tab-selected-bg: #{{colors.BrightBlack}};\n\t\t--mff-tab-selected-fg: #{{colors.Yellow}};\n\t\t--mff-tab-soundplaying-bg: #{{colors.Magenta}};\n\n\t\t--mff-urlbar-color: #{{colors.White}};\n\t\t--mff-urlbar-focused-color: #{{colors.Blue}};\n\t\t--mff-urlbar-font-family: \"{{font.normal}}\";\n\t\t--mff-urlbar-font-size: 12pt;\n\t\t--mff-urlbar-font-weight: 500;\n\t\t--mff-urlbar-results-color: #{{colors.White}};\n\t\t--mff-urlbar-results-font-family: \"{{font.normal}}\";\n\t\t--mff-urlbar-results-font-size: 12pt;\n\t\t--mff-urlbar-results-font-weight: 500;\n\t\t--mff-urlbar-results-url-color: #{{colors.Blue}};\n\n\t\t/*   --mff-tab-selected-bg: linear-gradient(90deg, rgba(232,74,95,1) 0%, rgba(255,132,124,1) 50%, rgba(254,206,168,1) 100%); */\n\t\t/*   --mff-urlbar-font-weight: 500; */\n\n\t\t/* Overriden Firefox variables*/\n\t\t--autocomplete-popup-background: var(--mff-bg) !important;\n\t\t--default-arrowpanel-background: var(--mff-bg) !important;\n\t\t--default-arrowpanel-color: #{{colors.White}}!important;\n\t\t--lwt-toolbarbutton-icon-fill: var(--mff-icon-color) !important;\n\t\t--panel-disabled-color: #{{colors.White}}00;\n\t\t--toolbar-bgcolor: var(--mff-bg) !important;\n\t\t--urlbar-separator-color: transparent !important;\n}\n\n/* Tabs */\n\n.tab-background[selected=\"true\"] {\n\t\tbackground: var(--mff-tab-selected-bg) !important;\n}\n\n.tab-text[selected=\"true\"] {\n\t\tcolor: #{{colors.Yellow}} !important;\n}\n\n.tab-background:not[visuallyselected] {\n\t\tbackground: var(--mff-tab-selected-bg) !important;\n\t\tcolors: red;\n\t\topacity: 0.5 !important;\n}\n\n/* This positions the tabs under the navaigator container */\n#titlebar {\n\t\t-moz-box-ordinal-group: 3 !important;\n}\n\n.tabbrowser-tab::after,\n.tabbrowser-tab::before {\n\t\tborder-left: none !important;\n}\n\n.tab-background {\n\t\tborder: none !important;\n\t\tbackground: #{{colors.BrightBlack}} !important;\n}\n\n.tabbrowser-arrowscrollbox {\n\t\tmargin-inline-start: 4px !important;\n\t\tmargin-inline-end: 0px !important;\n}\n\n.tab-close-button {\n\t\tdisplay: none !important;\n}\n\n.tab-text {\n\t\tfont-family: var(--mff-tab-font-family);\n\t\tfont-weight: var(--mff-tab-font-weight);\n\t\tfont-size: var(--mff-tab-font-size) !important;\n\t\tcolor: var(--mff-tab-color);\n}\n\n/* Show the favicon for tabs that are pinned */\nhbox.tab-content[pinned=true] .tab-icon-image {\n\t\tdisplay: initial !important;\n}\n\nhbox.tab-content[pinned=true] .tab-text {\n\t\tdisplay: none !important;\n}\n\n#tabbrowser-tabs {\n\t\t--tab-loading-fill: #033433 !important;\n}\n\n.tab-label-container:not([textoverflow]) {\n\t\tdisplay: flex;\n\t\toverflow: hidden;\n\t\tjustify-content: center;\n\t\twidth: 50% !important;\n\t\tmax-width: 50% !important;\n\t\tmin-width: 50% !important;\n}\n\n.tab-line {\n\t\tdisplay: none !important;\n}\n\n.tabbrowser-tab {\n\t\tborder-radius: var(--mff-tab-border-radius) !important;\n\t\tborder-width: 0;\n\t\theight: var(--mff-tab-height) !important;\n\t\tmargin-bottom: 4px !important;\n\t\tmargin-inline-end: 4px !important;\n\t\tmargin-top: 4px !important;\n\t\tmax-height: var(--mff-tab-height) !important;\n\t\tmin-height: var(--mff-tab-height) !important;\n}\n\n.tabbrowser-tab[soundplaying=\"true\"] {\n\t\tbackground-color: var(--mff-tab-soundplaying-bg) !important;\n}\n\n.tab-icon-sound {\n\t\tdisplay: none !important;\n}\n\n/* Toolbar */\n\n.urlbar-icon > image {\n\t\tfill: var(--mff-icon-color) !important;\n\t\tcolor: var(--mff-icon-color) !important;\n}\n\n.toolbarbutton-text {\n\t\tcolor: var(--mff-icon-color)  !important;\n}\n.urlbar-icon {\n\t\tcolor: var(--mff-icon-color)  !important;\n}\n\n.toolbarbutton-icon {\n\t\t/* filter: drop-shadow(0 0 0.75rem crimson); */\n}\n\n#urlbar-results {\n\t\tfont-family: var(--mff-urlbar-results-font-family);\n\t\tfont-weight: var(--mff-urlbar-results-font-weight);\n\t\tfont-size: var(--mff-urlbar-results-font-size) !important;\n\t\tcolor: var(--mff-urlbar-results-color) !important;\n}\n\n.urlbarView-row[type=\"bookmark\"] > span{\n\t\tcolor: green !important;\n}\n\n.urlbarView-row[type=\"switchtab\"] > span{\n\t\tcolor: orange !important;\n}\n\n.urlbarView-url, .search-panel-one-offs-container {\n\t\tcolor: var(--mff-urlbar-results-url-color) !important;\n\t\tfont-family: var(--mff-urlbar-font-family);\n\t\tfont-weight: var(--mff-urlbar-results-font-weight);\n\t\tfont-size: var(--mff-urlbar-font-size) !important;\n}\n\n.urlbarView-favicon, .urlbarView-type-icon {\n\t\tdisplay: none !important;\n}\n\n#urlbar-input {\n\t\tfont-size: var(--mff-urlbar-font-size) !important;\n\t\tcolor: var(--mff-urlbar-color) !important;\n\t\tfont-family: var(--mff-urlbar-font-family) !important;\n\t\tfont-weight: var(--mff-urlbar-font-weight)!important;\n\t\ttext-align: center !important;\n}\n\n#tracking-protection-icon-container, #identity-box {\n\t\tdisplay: none;\n}\n\n#back-button > .toolbarbutton-icon{\n\t\t--backbutton-background: transparent !important;\n\t\tborder: none !important;\n}\n\ntoolbar {\n\t\tbackground-image: none !important;\n}\n\n#urlbar-background {\n\t\topacity: .98 !important;\n}\n\n#navigator-toolbox, toolbaritem {\n\t\tborder: none !important;\n}\n\n#urlbar-background {\n\t\tbackground-color: var(--mff-bg) !important;\n\t\tborder: none !important;\n}\n\n.toolbar-items {\n\t\tbackground-color: var(--mff-bg) !important;\n}\n\n#sidebar-search-container {\n\t\tbackground-color: var(--mff-sidebar-bg) !important;\n}\n\nbox.panel-arrowbox {\n\t\tdisplay: none;\n}\n\nbox.panel-arrowcontent {\n\t\tborder-radius: 8px !important;\n\t\tborder: none !important;\n}\n\ntab.tabbrowser-tab {\n\t\toverflow: hidden;\n}\n\ntab.tabbrowser-tab:hover {\n\t\tbox-shadow: 0 1px 4px rgba(0,0,0,.05);\n}\n\nimage#star-button {\n\t\tdisplay: none;\n}\n\ntoolbar#nav-bar {\n\t\tpadding: var(--mff-nav-toolbar-padding) !important;\n}\n\ntoolbar#nav-bar {\n\t\tpadding: 4px !important;\n}\n\n#urlbar {\n\t\tmax-width: 70% !important;\n\t\tmargin: 0 15% !important;\n\t\t/*\tposition: unset!important; */;\n}\n\n#urlbar-input:focus {\n\t\tcolor: var(--mff-urlbar-focused-color) !important;\n}\n\n.megabar[breakout-extend=\"true\"]:not([open=\"true\"]) > #urlbar-background {\n\t\tbox-shadow: none !important;\n\t\tbackground-color: transparent !important;\n}\n\ntoolbarbutton {\n\t\tbox-shadow: none !important;\n}\n\n/* Sidebar */\n\n.close-icon, .urlbar-icon {\n\t\tfill: var(--mff-icon-color) !important;\n}\n\n.sidebar-placesTree {\n\t\tcolor: var(--mff-sidebar-color) !important;\n}\n\n#sidebar-box {\n\t\t--sidebar-background-color: var(--mff-sidebar-bg) !important;\n}\n\nsplitter#sidebar-splitter {\n\t\topacity: 0 !important;\n}\n\nsplitter#sidebar-splitter {\n\t\tborder: none !important;\n\t\tbackground-color: transparent !important;\n}\n\nimage#sidebar-icon {\n\t\tdisplay: none;\n}\n\n/* Arrow panel */\n\n.panel-arrowcontent {\n\t\tpadding: 0px !important;\n\t\tmargin: 0px !important;\n}\n\ntoolbarseparator {\n\t\tdisplay: none;\n}\n"
  },
  {
    "path": "home-manager/modules/fonts/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.fonts;\nin\n{\n\n  options.pinpox = {\n\n    defaults.fonts.enable = mkEnableOption \"font defaults\";\n\n    font = {\n      normal = {\n        family = mkOption {\n          type = types.str;\n          default = \"Berkeley Mono\";\n        };\n        style = mkOption {\n          type = types.str;\n          default = \"Regular\";\n        };\n      };\n      bold = {\n        family = mkOption {\n          type = types.str;\n          default = \"Berkeley Mono\";\n        };\n        style = mkOption {\n          type = types.str;\n          default = \"Bold\";\n        };\n      };\n      italic = {\n        family = mkOption {\n          type = types.str;\n          default = \"Berkeley Mono\";\n        };\n        style = mkOption {\n          type = types.str;\n          default = \"Regular Italic\";\n        };\n      };\n      size = 10;\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    fonts.fontconfig.enable = true;\n    # home.packages =\n    #   [ flake-inputs.nix-apple-fonts.packages.\"x86_64-linux\".sf-mono ];\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/foot/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.foot;\nin\n{\n  options.pinpox.programs.foot.enable = mkEnableOption \"foot terminal emulator\";\n\n  config = mkIf cfg.enable {\n\n    home.packages = with pkgs; [\n      nerd-fonts.inconsolata\n      foot\n    ];\n\n    programs.foot = {\n      enable = true;\n      server.enable = true;\n      settings = {\n\n        main = {\n          term = \"xterm-256color\";\n\n          font = \"Berkeley Mono:size=13\";\n          # dpi-aware = \"yes\"; # Defaults to auto\n        };\n\n        scrollback.lines = 10000;\n\n        cursor = {\n          style = \"beam\";\n          blink = \"yes\";\n          # beam-thickness =\n        };\n\n        colors = {\n\n          alpha = \"0.9\";\n          # background = \"${config.pinpox.colors.White}\";\n          # foreground = \"${config.pinpox.colors.Black}\";\n          background = \"${config.pinpox.colors.Black}\";\n          foreground = \"${config.pinpox.colors.White}\";\n\n          ## Normal/regular colors (color palette 0-7)\n          regular0 = \"${config.pinpox.colors.Black}\"; # black\n          regular1 = \"${config.pinpox.colors.Red}\"; # red\n          regular2 = \"${config.pinpox.colors.Green}\"; # green\n          regular3 = \"${config.pinpox.colors.Yellow}\"; # yellow\n          regular4 = \"${config.pinpox.colors.Blue}\"; # blue\n          regular5 = \"${config.pinpox.colors.Magenta}\"; # magenta\n          regular6 = \"${config.pinpox.colors.Cyan}\"; # cyan\n          regular7 = \"${config.pinpox.colors.White}\"; # white\n\n          ## Bright colors (color palette 8-15)\n          bright0 = \"${config.pinpox.colors.BrightBlack}\"; # black\n          bright1 = \"${config.pinpox.colors.BrightRed}\"; # red\n          bright2 = \"${config.pinpox.colors.BrightGreen}\"; # green\n          bright3 = \"${config.pinpox.colors.BrightYellow}\"; # yellow\n          bright4 = \"${config.pinpox.colors.BrightBlue}\"; # blue\n          bright5 = \"${config.pinpox.colors.BrightMagenta}\"; # magenta\n          bright6 = \"${config.pinpox.colors.BrightCyan}\"; # cyan\n          bright7 = \"${config.pinpox.colors.BrightWhite}\"; # white\n        };\n\n        # mouse = {\n        #   hide-when-typing = \"yes\";\n        # };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/games/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.games;\nin\n{\n  options.pinpox.programs.games.enable = mkEnableOption \"games\";\n  config = mkIf cfg.enable {\n    home.packages = with pkgs; [ retroarch-free ];\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/git/default.nix",
    "content": "{\n  pkgs,\n  config,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.git;\n\n  prePushHook = pkgs.writeShellApplication {\n    name = \"pre-push\";\n    runtimeInputs = [ pkgs.jq ];\n    text = ''\n      [ -f flake.lock ] || exit 0\n      matches=$(jq -r '\n        .nodes | to_entries[]\n        | select(\n            .value.locked.type == \"path\"\n            or ((.value.locked.url // \"\") | startswith(\"file://\"))\n          )\n        | \"\\(.key): \\(.value.locked.path // .value.locked.url)\"\n      ' flake.lock)\n      if [ -n \"$matches\" ]; then\n        echo \"warning: local-path inputs in flake.lock:\" >&2\n        echo \"$matches\" >&2\n        if [ -e /dev/tty ]; then\n          read -r -p \"Push anyway? [y/N] \" reply < /dev/tty\n          [[ \"$reply\" =~ ^[Yy]$ ]] || { echo \"aborted.\" >&2; exit 1; }\n        else\n          echo \"error: no tty for confirmation — refusing push\" >&2\n          exit 1\n        fi\n      fi\n    '';\n  };\nin\n{\n  options.pinpox.defaults.git.enable = mkEnableOption \"git defaults\";\n\n  config = mkIf cfg.enable {\n\n    home.packages = with pkgs; [\n      tig\n      jjui\n    ];\n\n    programs = {\n\n      lazygit = {\n        enable = true;\n\n        # https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md\n        settings = {\n\n          # reporting = \"off\";\n          # update.method = \"never\";\n\n          shortTimeFormat = \"15h:30:13\";\n          gui.showFileTree = true;\n          os = {\n            edit = \"nvim {{filename}}\";\n            editAtLine = \"nvim +{{line}} {{filename}}\";\n            editAtLineAndWait = \"nvim --remote-wait +{{line}} {{filename}}\";\n            editInTerminal = true;\n          };\n        };\n      };\n\n      git = {\n        enable = true;\n        lfs.enable = true;\n\n        ignores = [\n          \"tags\"\n          \"*.swp\"\n          \"result\"\n          \".claude\"\n        ];\n\n        settings = {\n\n          init.defaultBranch = \"main\";\n\n          core.hooksPath = \"${prePushHook}/bin\";\n\n          pull = {\n            rebase = true;\n            autostash = true;\n            twohead = \"ort\";\n          };\n\n          push = {\n            default = \"simple\";\n            autoSetupRemote = true;\n          };\n\n          # rerere = {\n          #   autoUpdate = true\n          #   enabled = true\n          # };\n\n          branch = {\n            autoSetupRebase = \"always\";\n            autoSetupMerge = \"always\";\n          };\n\n          rebase = {\n            stat = true;\n            autoStash = true;\n            autoSquash = true;\n            updateRefs = true;\n          };\n\n          help.autocorrect = 10;\n\n          signing = {\n            format = \"ssh\";\n            key = \"~/.ssh/key.pub\";\n            signByDefault = true;\n          };\n\n          alias = {\n            s = \"status\";\n            d = \"diff\";\n            a = \"add\";\n            c = \"commit\";\n            p = \"push\";\n            o = \"checkout\";\n            co = \"checkout\";\n            uncommit = \"reset --soft HEAD^\";\n            comma = \"commit --amend\";\n            reset-pr = \"reset --hard FETCH_HEAD\";\n            force-push = \"push --force-with-lease\";\n          };\n\n          user.email = \"git@pablo.tools\";\n          user.name = \"pinpox\";\n        };\n      };\n    };\n\n    # [kiwi] evaluation warning: pinpox profile: The option `programs.git.aliases' defined in `/nix/store/fsc89mqrbiij6pnl4vrf0l0plv0i8pp6-source/clan-service-modules/machine-type/desktop.nix' has been renamed to `programs.git.settings.alias'.\n    # [kiwi] evaluation warning: pinpox profile: The option `programs.git.extraConfig' defined in `/nix/store/fsc89mqrbiij6pnl4vrf0l0plv0i8pp6-source/clan-service-modules/machine-type/desktop.nix' has been renamed to `programs.git.settings'.\n\n    programs.jujutsu = {\n      enable = true;\n      settings = {\n        merge-tools.meld.merge-args = [\n          \"$left\"\n          \"$base\"\n          \"$right\"\n          \"-o\"\n          \"$output\"\n          \"--auto-merge\"\n        ];\n        signing = {\n          behavior = \"own\";\n          backend = \"ssh\";\n          key = \"~/.ssh/key.pub\";\n          allowed-signers = \"~/.ssh/allowed_signers\";\n        };\n        ui = {\n\n          pager = \"less -FRX\";\n\n          default-command = \"log\";\n          merge-editor = [\n            \"meld\"\n            \"$left\"\n            \"$base\"\n            \"$right\"\n            \"-o\"\n            \"$output\"\n          ];\n        };\n        user = {\n          email = \"git@pablo.tools\";\n          name = \"pinpox\";\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/go/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.programs.go;\nin\n{\n  options.pinpox.programs.go.enable = mkEnableOption \"go compiler\";\n\n  config = mkIf cfg.enable {\n\n    programs = {\n      go = {\n        enable = true;\n        env.GOPATH = \"/home/pinpox/.go\";\n        #      packages = {\n        #        \"golang.org/x/text\" =\n        #          builtins.fetchGit \"https://go.googlesource.com/text\";\n        #        \"golang.org/x/time\" =\n        #          builtins.fetchGit \"https://go.googlesource.com/time\";\n        #      };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/grobi/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.grobi;\nin\n{\n  options.pinpox.defaults.grobi.enable = mkEnableOption \"grobi defaults\";\n\n  config = mkIf cfg.enable {\n    services = {\n\n      grobi = {\n        # enable = true;\n        enable = false;\n        # executeAfter = [ \" \" ];\n        rules = [\n          {\n            name = \"kartoffel\";\n            outputs_connected = [\n              \"DVI-D-0\"\n              \"DP-0\"\n              \"DVI-D-1\"\n            ];\n            configure_row = [\n              \"DVI-D-0\"\n              \"DP-0\"\n              \"DVI-D-1\"\n            ];\n            atomic = true;\n            execute_after = [\n              ''\n                ${pkgs.xorg.xrandr}/bin/xrandr \\\n                --output DVI-D-0 --mode 1920x1200 --pos 3460x0 --rotate normal \\\n                --output DP-0 --primary --mode 2560x1440 --pos 900x0 --rotate normal \\\n                --output DVI-D-1 --mode 1440x900 --pos 0x0 --rotate right \\\n                --output DP-1 --off \\\n                --output HDMI-0 --off\n              ''\n            ];\n          }\n        ];\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/gtk/banana.nix",
    "content": "{\n  config,\n  pkgs,\n  ...\n}:\n{\n  config = {\n    home.pointerCursor = {\n      name = \"Banana\";\n      size = 32;\n      package = pkgs.banana-cursor;\n      x11.enable = true;\n      gtk.enable = true;\n    };\n\n    wayland.windowManager.sway.config.seat.\"*\".xcursor_theme =\n      \"${config.gtk.cursorTheme.name} ${toString config.gtk.cursorTheme.size}\";\n\n    gtk.cursorTheme = {\n      name = \"Banana\";\n      size = 32;\n      package = pkgs.banana-cursor;\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/gtk/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n\n  cfg = config.pinpox.defaults.gtk;\n\n  # TODO use flake inputs\n  materia-theme = pkgs.fetchFromGitHub {\n    owner = \"nana-4\";\n    repo = \"materia-theme\";\n    rev = \"76cac96ca7fe45dc9e5b9822b0fbb5f4cad47984\";\n    sha256 = \"sha256-0eCAfm/MWXv6BbCl2vbVbvgv8DiUH09TAUhoKq7Ow0k=\";\n    # Old version\n    # \"e329aaee160c82e85fe91a6467c666c7f9f2a7df\";\n    # sha256 = \"1qmq5ycfpzv0rcp5aav4amlglkqy02477i4bdi7lgpbn0agvms6c\";\n    fetchSubmodules = true;\n  };\n  materia_colors = pkgs.writeTextFile {\n    name = \"gtk-generated-colors\";\n    text = ''\n      BTN_BG=${config.pinpox.colors.BrightBlack}\n      BTN_FG=${config.pinpox.colors.BrightWhite}\n\n      FG=${config.pinpox.colors.White}\n      BG=${config.pinpox.colors.Black}\n\n      HDR_BTN_BG=${config.pinpox.colors.BrightBlack}\n      HDR_BTN_FG=${config.pinpox.colors.White}\n\n      ACCENT_BG=${config.pinpox.colors.Green}\n      ACCENT_FG=${config.pinpox.colors.Black}\n\n      HDR_FG=${config.pinpox.colors.White}\n      HDR_BG=${config.pinpox.colors.BrightBlack}\n\n      MATERIA_SURFACE=${config.pinpox.colors.BrightBlack}\n      MATERIA_VIEW=${config.pinpox.colors.BrightBlack}\n\n      MENU_BG=${config.pinpox.colors.BrightBlack}\n      MENU_FG=${config.pinpox.colors.BrightWhite}\n\n      SEL_BG=${config.pinpox.colors.Blue}\n      SEL_FG=${config.pinpox.colors.Magenta}\n\n      TXT_BG=${config.pinpox.colors.BrightBlack}\n      TXT_FG=${config.pinpox.colors.BrightWhite}\n\n      WM_BORDER_FOCUS=${config.pinpox.colors.White}\n      WM_BORDER_UNFOCUS=${config.pinpox.colors.BrightBlack}\n\n      UNITY_DEFAULT_LAUNCHER_STYLE=False\n      NAME=generated\n      MATERIA_COLOR_VARIANT=dark\n      MATERIA_STYLE_COMPACT=True\n    '';\n  };\nin\n\n{\n  options.pinpox.defaults.gtk.enable = mkEnableOption \"gtk defaults\";\n\n  imports = [ ./banana.nix ];\n\n  config = mkIf cfg.enable {\n\n    nixpkgs.overlays = [\n      (self: super: {\n        rendersvg = self.runCommand \"rendersvg\" { } ''\n          mkdir -p $out/bin\n          ln -s ${self.resvg}/bin/resvg $out/bin/rendersvg\n        '';\n        generated-gtk-theme = self.stdenv.mkDerivation {\n\n          name = \"generated-gtk-theme\";\n          src = materia-theme;\n          buildInputs = with self; [\n            sassc\n            bc\n            which\n            rendersvg\n            meson\n            ninja\n            dart-sass\n            gtk4.dev\n            optipng\n          ];\n          MATERIA_COLORS = materia_colors;\n          phases = [\n            \"unpackPhase\"\n            \"installPhase\"\n          ];\n          installPhase = ''\n            HOME=/build\n            chmod 777 -R .\n            patchShebangs .\n            mkdir -p $out/share/themes\n            mkdir bin\n            sed -e 's/handle-horz-.*//' -e 's/handle-vert-.*//' -i ./src/gtk-2.0/assets.txt\n            echo \"Changing colours:\"\n            ./change_color.sh -o Generated \"$MATERIA_COLORS\" -i False -t \"$out/share/themes\"\n            chmod 555 -R .\n          '';\n        };\n      })\n    ];\n\n    # GTK settings\n    gtk = {\n      enable = true;\n\n      font = {\n        name = \"Berkeley Mono\";\n        # package = pkgs.iosevka;\n      };\n\n      iconTheme = {\n        name = \"Papirus-Dark\";\n        package = pkgs.papirus-icon-theme;\n      };\n\n      theme = {\n        name = \"Generated\";\n        package = pkgs.generated-gtk-theme;\n      };\n    };\n\n    home.sessionVariables.GTK_THEME = \"Generated\";\n\n    # Set default dark theme via dconf (can be changed at runtime)\n    dconf.settings = {\n      \"org/gnome/desktop/interface\" = {\n        color-scheme = \"prefer-dark\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/helix/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\n\nwith lib;\n\nlet\n  cfg = config.pinpox.programs.helix;\n\nin\n{\n  options.pinpox.programs.helix = {\n    enable = mkEnableOption \"Helix editor configuration\";\n  };\n\n  config = mkIf cfg.enable {\n    programs.helix = {\n      enable = true;\n\n      # https://docs.helix-editor.com/languages.html\n      languages = {\n        language = [\n          {\n            name = \"nix\";\n            auto-format = false;\n            formatter.command = \"${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt\";\n          }\n        ];\n      };\n\n      settings = {\n        editor = {\n          indent-guides.render = true;\n          bufferline = \"multiple\";\n          cursorline = true;\n          cursor-shape = {\n            insert = \"bar\";\n            normal = \"block\";\n            select = \"underline\";\n          };\n\n          lsp.display-messages = true;\n        };\n\n        theme = \"catppuccin_mocha\";\n\n        keys = {\n          normal = {\n            \";\" = \"command_mode\";\n            \"C-g\" = [\n              \":new\"\n              \":insert-output ${pkgs.lazygit}/bin/lazygit\"\n              \":buffer-close!\"\n              \":redraw\"\n            ];\n          };\n          select = {\n            \";\" = \"command_mode\";\n          };\n        };\n      };\n    };\n\n    # Add helix to home packages\n    home.packages = with pkgs; [\n      helix\n    ];\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/k9s/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.k9s;\nin\n{\n\n  options.pinpox.programs.k9s.enable = mkEnableOption \"k9s kubernetes CLI\";\n\n  config = mkIf cfg.enable {\n\n    home.packages = [ pkgs.k9s ];\n\n    xdg = {\n      enable = true;\n      configFile.k9s_theme = {\n        target = \"k9s/skin.yml\";\n        text = builtins.toJSON {\n          k9s = {\n            body = {\n              fgColor = \"#cad3f5\";\n              bgColor = \"#24273a\";\n              logoColor = \"#cba6f7\";\n            };\n            info = {\n              fgColor = \"#74c7ec\";\n              sectionColor = \"#8aadf4\";\n            };\n            frame = {\n              border = {\n                fgColor = \"#cba6f7\";\n                focusColor = \"#f5bde6\";\n              };\n              menu = {\n                fgColor = \"#eed49f\";\n                keyColor = \"#8aadf4\";\n                numKeyColor = \"#f5bde6\";\n              };\n              crumbs = {\n                fgColor = \"#cad3f5\";\n                bgColor = \"#8aadf4\";\n                activeColor = \"#74c7ec\";\n              };\n              status = {\n                newColor = \"#a6da95\";\n                modifyColor = \"#68f288\";\n                addColor = \"#74c7ec\";\n                errorColor = \"#ed8796\";\n                highlightcolor = \"#8aadf4\";\n                killColor = \"#747c9e\";\n                completedColor = \"#5b6078\";\n              };\n              title = {\n                fgColor = \"#8bd5ca\";\n                bgColor = \"#5b6078\";\n                highlightColor = \"#74c7ec\";\n                counterColor = \"#cba6f7\";\n                filterColor = \"#5b6078\";\n              };\n            };\n            views = {\n              table = {\n                fgColor = \"#cad3f5\";\n                bgColor = \"#24273a\";\n                cursorColor = \"#8bd5ca\";\n                header = {\n                  fgColor = \"#24273a\";\n                  bgColor = \"#8aadf4\";\n                };\n              };\n              yaml = {\n                keyColor = \"#8bd5ca\";\n                colonColor = \"#aee2da\";\n                valueColor = \"#a6da95\";\n              };\n              logs = {\n                fgColor = \"#cad3f5\";\n                bgColor = \"#24273a\";\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/kanshi/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.kanshi;\nin\n{\n  options.pinpox.programs.kanshi.enable = mkEnableOption \"kanshi screen setup\";\n\n  config = mkIf cfg.enable {\n\n    home.packages = with pkgs; [ kanshi ];\n\n    # output eDP-1 mode 1920x1080 position 0,0\n    # output DP-1 mode 2560x1440 position 1080,0\n    # output DP-2 mode 2560x1440 position 3640,0\n\n    services.kanshi = {\n      enable = true;\n      settings = [\n        {\n          profile.name = \"laptop-only\";\n          profile.outputs = [\n            {\n              criteria = \"eDP-1\";\n              mode = \"2880x1920@120Hz\";\n              scale = 2.0;\n            }\n          ];\n        }\n        {\n          profile.name = \"laptop-external-right\";\n          profile.outputs = [\n            {\n              criteria = \"eDP-1\";\n              mode = \"2880x1920@120Hz\";\n              position = \"0,0\";\n              scale = 2.0;\n              status = \"enable\";\n            }\n            {\n              criteria = \"Lenovo Group Limited Y27h-30 U3V040YW\";\n              mode = \"2560x1440\";\n              position = \"1440,0\";\n              status = \"enable\";\n            }\n          ];\n        }\n      ];\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/mako/default.nix",
    "content": "{\n  pkgs,\n  config,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.mako;\nin\n{\n  options.pinpox.programs.mako.enable = mkEnableOption \"mako notifications\";\n\n  config = mkIf cfg.enable {\n\n    # Needed for firefox and thunderbird\n    home.packages = [ pkgs.libnotify ];\n\n    services.mako = {\n      enable = true;\n\n      settings = {\n        anchor = \"top-right\";\n        background-color = \"#285577FF\";\n        border-color = \"#4C7899FF\";\n        # progressColor = \"over #5588AAFF\";\n        text-color = \"#FFFFFFFF\";\n        border-radius = \"5\";\n        border-size = \"5\";\n        default-timeout = \"10000\"; # In milliseconds\n        # extraConfig = '''';\n        font = \"Berkeley Mono 12\";\n        # %a   Application name\n        # %s   Notification summary\n        # %b   Notification body\n        # %g   Number of notifications in the current group\n        # %i   Notification id\n        # format = \"<b>%s</b>\\\\n%b\";\n        # groupBy = \"\";\n        height = \"200\";\n        width = \"300\";\n        # iconPath = \"\";\n        icons = \"true\";\n        margin = \"10\";\n        padding = \"5\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/mpv/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.mpv;\nin\n{\n  options.pinpox.programs.mpv.enable = mkEnableOption \"mpv media player\";\n\n  config = mkIf cfg.enable {\n\n    programs.mpv = {\n      enable = true;\n\n      config = {\n        # Screenshot settings\n        screenshot-directory = \"~/Pictures\";\n        screenshot-format = \"png\";\n        screenshot-template = \"photo-%F-%T\";\n\n        # Low latency for camera preview\n        profile = \"low-latency\";\n        untimed = \"yes\";\n      };\n\n      # Rotation for webcam (applied to v4l2 protocol)\n      profiles = {\n        \"protocol.av\" = {\n          vf = \"rotate=PI\";\n        };\n      };\n\n      bindings = {\n        # Screenshot bindings\n        \"s\" = \"screenshot\";\n        \"MBTN_LEFT\" = \"screenshot\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/neomutt/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.programs.neomutt;\nin\n{\n  options.pinpox.programs.neomutt.enable = mkEnableOption \"neomutt mail client\";\n\n  config = mkIf cfg.enable {\n    programs.neomutt = {\n      enable = true;\n      sidebar = {\n        enable = true;\n      };\n      extraConfig = ''\n        set imap_user = \"pablo1@mailbox.org\"\n        set imap_pass = \"`pass mailbox.org/pablo1@mailbox.org`\"\n      '';\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/newsboat/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.programs.newsboat;\n  splitString = str: builtins.filter builtins.isString (builtins.split \"\\n\" str);\nin\n{\n  options.pinpox.programs.newsboat.enable = mkEnableOption \"newsboat RSS reader\";\n\n  config = mkIf cfg.enable {\n\n    programs.newsboat = {\n      enable = true;\n      autoReload = true;\n      urls = [\n        # https://hackaday.com/blog/feed/\n        {\n          title = \"nixOS mobile\";\n          tags = [\n            \"nixos\"\n            \"nix\"\n          ];\n          url = \"https://mobile.nixos.org/index.xml\";\n        }\n        {\n          title = \"r/NixOS\";\n          tags = [\n            \"nixos\"\n            \"nix\"\n            \"reddit\"\n          ];\n          url = \"https://www.reddit.com/r/NixOS.rss\";\n        }\n        {\n          title = \"NixOS weekly\";\n          tags = [\n            \"nixos\"\n            \"nix\"\n          ];\n          url = \"https://weekly.nixos.org/feeds/all.rss.xml\";\n        }\n      ]\n      ++ (map (x: {\n        url = x;\n        tags = [ \"rss\" ];\n      }) (splitString (builtins.readFile ./newsboat/rss.txt)))\n\n      ++ (map (x: {\n        url = x;\n        tags = [ \"podcast\" ];\n      }) (splitString (builtins.readFile ./newsboat/podcast.txt)))\n\n      ++ (map (x: {\n        url = x;\n        tags = [ \"youtube\" ];\n      }) (splitString (builtins.readFile ./newsboat/youtube.txt)));\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/obs-studio/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  system-config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.obs-studio;\n  droidcam-port = 5201;\nin\n{\n  options.pinpox.programs.obs-studio.enable = mkEnableOption \"obs-studio\";\n\n  config = mkIf cfg.enable {\n\n    # assertions = [\n    #   {\n    #     assertion = (builtins.elem droidcam-port system-config.networking.firewall.allowedTCPPorts);\n    #     message = \"Port ${toString droidcam-port}/tcp is not open in the firewall, but required by droidcam\";\n    #   }\n    # ];\n\n    home.packages = [\n      pkgs.uxplay # AirPlay Unix mirroring server\n\n      pkgs.slurp\n      pkgs.xdg-desktop-portal\n      pkgs.xdg-desktop-portal-gtk\n    ];\n\n    programs.obs-studio = {\n      enable = true;\n      plugins = with pkgs.obs-studio-plugins; [\n        obs-pipewire-audio-capture\n        # droidcam-obs\n        wlrobs\n        # obs-vintage-filter\n        # obs-teleport\n        obs-backgroundremoval\n        input-overlay\n      ];\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/pandoc/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n\n  cfg = config.pinpox.programs.pandoc;\nin\n{\n  options.pinpox.programs.pandoc.enable = mkEnableOption \"pandoc config\";\n\n  config = mkIf cfg.enable {\n\n    programs.pandoc = {\n      enable = true;\n      citationStyles = [ ];\n\n      # templates = {\n      # \"default.latex\" = path/to/your/template;\n      # };\n\n      defaults = {\n        metadata = {\n          author = \"Pablo Ovelleiro Corral\";\n        };\n        # pdf-engine = \"xelatex\";\n        # citeproc = true;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/pi-mono/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.pi;\n\n  # Generate models.json from the declarative provider config\n  modelsJson = pkgs.writeText \"models.json\" (\n    builtins.toJSON { providers = cfg.providers; }\n  );\nin\n{\n  options.pinpox.programs.pi = {\n\n    enable = mkEnableOption \"pi coding agent\";\n\n    package = mkOption {\n      type = types.package;\n      default = pkgs.pi;\n      description = \"The pi package to install.\";\n    };\n\n    extensions = mkOption {\n      type = types.attrsOf types.path;\n      default = { };\n      description = ''\n        Extension name to source path mappings. Each entry is symlinked into\n        `~/.pi/agent/extensions/<name>`. Sources can be local paths, fetchGit\n        results, or built packages.\n\n        Pi auto-discovers extensions from `~/.pi/agent/extensions/` — both\n        single `.ts` files and directories with an `index.ts` entry point.\n\n        Note: Extensions that require `npm install` (i.e. have runtime\n        dependencies in package.json) need to be built first or installed\n        via `pi install` instead.\n      '';\n      example = literalExpression ''\n        {\n          my-extension = ./extensions/my-extension.ts;\n        }\n      '';\n    };\n\n    providers = mkOption {\n      type = types.attrsOf types.attrs;\n      default = { };\n      description = ''\n        Model provider configurations, keyed by provider name. All providers\n        are merged and written to `~/.pi/agent/models.json` as a read-only\n        symlink. Pi only reads this file, never writes to it.\n\n        Can be set from multiple modules (e.g. machine-specific configs)\n        and Nix will merge them.\n      '';\n      example = literalExpression ''\n        {\n          ollama = {\n            baseUrl = \"http://100.96.100.103:11434/v1\";\n            api = \"openai-completions\";\n            apiKey = \"dummy\";\n            compat = {\n              supportsDeveloperRole = false;\n              supportsReasoningEffort = false;\n            };\n            models = [\n              { id = \"llama3.3:latest\"; contextWindow = 128000; maxTokens = 32000; }\n            ];\n          };\n        }\n      '';\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    home.packages = [ cfg.package ];\n\n    home.file = mkMerge [\n      # Symlink extensions into the auto-discovery directory\n      (mapAttrs' (name: src: {\n        name = \".pi/agent/extensions/${name}\";\n        value.source = src;\n      }) cfg.extensions)\n\n      # models.json as read-only symlink (only when providers are configured)\n      (mkIf (cfg.providers != { }) {\n        \".pi/agent/models.json\".source = modelsJson;\n      })\n    ];\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/rio/config/rio/config.toml",
    "content": "# Modifier to line-height. Default is `1.0`\nline-height = 1.06\n\n# Environment variables\nenv-vars = [\"TERM=xterm-256color\"]\n\n# Don't confirm before exiting Rio\nconfirm-before-quit = false\n\n[adaptive-theme]\nlight = \"wildcharm-light\"\ndark = \"wildcharm-dark\"\n\n# light = \"pinpox-light\"\n# dark = \"pinpox-dark\"\n\n# Hide the cursor while typing\n# hide-cursor-when-typing = false\n\n# Ignore theme selection foreground color\n# ignore-selection-foreground-color = false\n\n# Startup directory\n#\n# Directory the shell is started in. If this is unset the working\n# directory of the parent process will be used.\n#\n# This configuration only has effect if use-fork is disabled.\n#\n# Example:\n# working-dir = \"/Users/raphael/Documents/\"\n\n\n# Use fork\n#\n# Defaults for POSIX-based systems (Windows is not configurable):\n# MacOS: spawn processes\n# Linux/BSD: fork processes\n#\n# Example:\n# use-fork = false\n\n# Cursor\n#\n# shape - Default cursor shape is 'block'\n# Other available options are: 'underline', 'beam' or 'hidden'\n#\n# blinking - Whether the cursor blinks. The default is false\n#\n# blinking-interval - Cursor update on milliseconds interval\n#\n# [cursor]\n# shape = 'block'\n# blinking = false\n# blinking-interval = 800\n\n# Editor\n#\n# Default editor on Linux and MacOS is \"vi\",\n# on Windows it is \"notepad\".\n#\n# Whenever the key binding `OpenConfigEditor` is triggered it will\n# use the value of the editor along with the rio configuration path.\n[editor]\nprogram = \"nvim\"\n# args = []\n\n# Window configuration\n#\n# • width - define the initial window width.\n#   Default: 600\n#\n# • height - define the initial window height.\n#   Default: 400\n#\n# • mode - define how the window will be created\n#     - \"Windowed\" (default) is based on width and height\n#     - \"Maximized\" window is created with maximized\n#     - \"Fullscreen\" window is created with fullscreen\n#\n# • opacity - Set window opacity\n#\n# • blur - Set blur on the window background. Changing this config requires restarting Rio to take effect.\n#\n# • decorations - Set window decorations, options: \"Enabled\", \"Disabled\", \"Transparent\", \"Buttonless\"\n#\n# • colorspace - Set the color space for the window\n#     - \"srgb\" (default on non-macOS)\n#     - \"display-p3\" (default on macOS)\n#     - \"rec2020\"\n#\n# Example:\n[window]\n# width = 600\n# height = 400\n# mode = \"windowed\"\nopacity = 0.95\nblur = true\n# decorations = \"enabled\"\n# colorspace = \"display-p3\"\n\n# Renderer\n#\n# • Performance: Set WGPU rendering performance\n#   - High: Adapter that has the highest performance. This is often a discrete GPU.\n#   - Low: Adapter that uses the least possible power. This is often an integrated GPU.\n#\n# • Backend: Set WGPU rendering backend\n#   - Automatic: Leave Sugarloaf/WGPU to decide\n#   - GL: Supported on Linux/Android, and Windows and macOS/iOS via ANGLE\n#   - Vulkan: Supported on Windows, Linux/Android\n#   - DX12: Supported on Windows 10\n#   - Metal: Supported on macOS/iOS\n#\n# • disable-unfocused-render: This property disable renderer processes while Rio is unfocused.\n#\n# • level: Configure renderer level\n#   - Available options: 0 and 1.\n#       Higher the level more rendering features and computations\n#       will be done like enable font ligatures or emoji support.\n#       For more information please check the docs.\n#\n# • filters: A list of paths to RetroArch slang shaders. Might not work with OpenGL.\n#\n# Example:\n# [renderer]\n# performance = \"high\"\n# backend = \"automatic\"\n# disable-unfocused-render = false\n# level = 1\n# filters = []\n\n# Keyboard\n#\n# use-kitty-keyboard-protocol - Enable Kitty Keyboard protocol\n#\n# disable-ctlseqs-alt - Disable ctlseqs with ALT keys\n#   - For example: Terminal.app does not deal with ctlseqs with ALT keys\n#\n# ime-cursor-positioning - Enable IME cursor positioning\n#   - When enabled, the IME input popup will appear at the cursor position\n#   - Default is true\n#\n# Example:\n# [keyboard]\n# use-kitty-keyboard-protocol = false\n# disable-ctlseqs-alt = false\n# ime-cursor-positioning = true\n\n# Fonts\n#\n# Configure fonts used by the terminal\n#\n# Note: You can set different font families but Rio terminal\n# will always look for regular font bounds whene\n#\n# You can also set family on root to overwrite all fonts.\n#\n# You can also specify extra fonts to load\n# [fonts]\n# extras = [{ family = \"Microsoft JhengHei\" }]\n#\n# In case you want to specify any font feature:\n# [fonts]\n# features = [\"ss02\", \"ss03\", \"ss05\", \"ss19\"]\n#\n# Note: Font features do not have support to live reload on configuration,\n# so to reflect your changes, you will need to close and reopen Rio.\n#\n# You can also map the specified Unicode codepoints to a particular font.\n# symbol-map = [\n#   { start = \"2297\", end = \"2299\", font-family = \"Cascadia Code NF\" }\n# ]\n\n[fonts]\n# You can also disable font hinting. Font hinting is enabled by default.\n# hinting = false\nsize = 16.6\nfamily = \"Berkeley Mono\"\n\n[fonts.emoji]\nfamily = \"Noto Emoji\"\n\n[fonts.regular]\nstyle = \"Normal\"\n\n[fonts.bold]\nstyle = \"Normal\"\nweight = 800\n\n[fonts.italic]\nstyle = \"Italic\"\n\n[fonts.bold-italic]\nstyle = \"Italic\"\nweight = 800\n\n# [hints]\n# # Characters used for hint labels\n# alphabet = \"jfkdls;ahgurieowpq\"\n#\n# # URL hint example\n# [[hints.rules]]\n# regex = \"(https://|http://)[^\\u{0000}-\\u{001F}\\u{007F}-\\u{009F}<>\\\"\\\\s{-}\\\\^⟨⟩`\\\\\\\\]+\"\n# hyperlinks = true\n# post-processing = true\n# persist = false\n#\n# [hints.rules.action]\n# command = \"xdg-open\"  # Linux/BSD\n# # command = \"open\"    # macOS\n# # command = { program = \"cmd\", args = [\"/c\", \"start\", \"\"] }  # Windows\n#\n# [hints.rules.binding]\n# key = \"O\"\n# mods = [\"Control\", \"Shift\"]\n\n# Scroll\n#\n# You can change how many lines are scrolled each time by setting this option.\n#\n# Scroll calculation for canonical mode will be based on `lines = (accumulated scroll * multiplier / divider)`,\n# If you want a quicker scroll, keep increasing the multiplier.\n# If you want to reduce scroll speed you will need to increase the divider.\n# You can use both properties also to find the best scroll for you.\n#\n# Multiplier default is 3.0.\n# Divider default is 1.0.\n# Example:\n# [scroll]\n# multiplier = 3.0\n# divider = 1.0\n\n# Navigation\n#\n# \"mode\" - Define navigation mode\n#   • NativeTab (MacOS only)\n#   • Bookmark\n#   • BottomTab\n#   • TopTab\n#   • Plain\n#\n# \"hide-if-single\" - Hide navigation UI if is single.\n# \"clickable\" - Enable click on tabs to switch.\n# \"use-current-path\" - Use same path whenever a new tab is created (Note: requires `use-fork` to be set to false).\n# \"color-automation\" - Set a specific color for the tab whenever a specific program is running, or in a specific directory.\n#\n# Example:\n# [navigation]\n# mode = \"bookmark\"\n# clickable = false\n# hide-if-single = true\n# use-current-path = false\n# color-automation = []\n\n# Shell\n#\n# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`.\n# Entries in `shell.args` are passed unmodified as arguments to the shell.\n#\n# Default:\n#   - (macOS) user login shell\n#   - (Linux/BSD) user login shell\n#   - (Windows) powershell\n#\n# Example 1 using fish shell from bin path:\n#\n# [shell]\n# program = \"/bin/fish\"\n# args = [\"--login\"]\n#\n# Example 2 for Windows using powershell\n#\n# [shell]\n# program = \"pwsh\"\n# args = []\n#\n# Example 3 for Windows using powershell with login\n#\n# [shell]\n# program = \"pwsh\"\n# args = [\"-l\"]\n#\n# Example 4 for MacOS with tmux installed by homebrew\n#\n# [shell]\n# program = \"/opt/homebrew/bin/tmux\"\n# args = [\"new-session\", \"-c\", \"/var/www\"]\n\n# Colors\n#\n# Colors definition will overwrite any property in theme\n# (considering if theme folder does exists and is being used)\n#\n# Example:\n# [colors]\n# background = '#0F0D0E'\n# foreground = '#F9F4DA'\n# cursor = '#F38BA3'\n# tabs = '#443d40'\n# tabs-active = '#F38BA3'\n# green = '#0BA95B'\n# red = '#ED203D'\n# blue = '#12B5E5'\n# yellow = '#FCBA28'\n\n# Bindings\n#\n# Create custom Key bindings for Rio terminal\n# More information in: https://raphamorim.io/rio/docs/key-bindings\n#\n# Example:\n# [bindings]\n# keys = [\n#   { key = \"q\", with = \"super\", action = \"Quit\" },\n#   # Bytes[27, 91, 53, 126] is equivalent to \"\\x1b[5~\"\n#   { key = \"home\", with = \"super | shift\", bytes = [27, 91, 53, 126] }\n# ]\n\n# Platform\n#\n# Rio now allows you to have different configurations per OS\n# You can write ovewrite properties like `Shell`, `Navigation`\n# and `Window`.\n#\n# Example:\n# [shell]\n# # default (in this case will be used only on MacOS)\n# program = \"/bin/fish\"\n# args = [\"--login\"]\n#\n# [platform]\n# # Microsoft Windows overwrite\n# windows.shell.program = \"pwsh\"\n# windows.shell.args = [\"-l\"]\n#\n# # Linux overwrite\n# linux.shell.program = \"tmux\"\n# linux.shell.args = [\"new-session\", \"-c\", \"/var/www\"]\n\n# Log level\n#\n# This property enables log level filter and file. The default level is \"OFF\" and the logs are not logged to a file as default.\n#\n# Example:\n# [developer]\n# log-level = \"OFF\"\n# enable-log-file = false\n\n"
  },
  {
    "path": "home-manager/modules/rio/config/rio/themes/pinpox-dark.toml",
    "content": "[colors]\nbackground       = '#24273a'\nblack            = '#24273a'\nblue             = '#8aadf4'\ncursor           = '#e8f0ff'\ncyan             = '#8bd5ca'\nforeground       = '#e8f0ff'\ngreen            = '#a6da95'\nmagenta          = '#cba6f7'\nred              = '#ed8796'\ntabs             = '#363a4f'\ntabs-active      = '#494d64'\nwhite            = '#e8f0ff'\nyellow           = '#eed49f'\ndim-black        = '#1e2030'\ndim-blue         = '#7891d0'\ndim-cyan         = '#73b9a8'\ndim-foreground   = '#b0b7d0'\ndim-green        = '#8cb77d'\ndim-magenta      = '#ad92d5'\ndim-red          = '#c9707d'\ndim-white        = '#b0b7d0'\ndim-yellow       = '#d1bc87'\nlight-black      = '#5b6078'\nlight-blue       = '#74c7ec'\nlight-cyan       = '#aee2da'\nlight-foreground = '#747c9e'\nlight-green      = '#68f288'\nlight-magenta    = '#f5bde6'\nlight-red        = '#ff5370'\nlight-white      = '#747c9e'\nlight-yellow     = '#fab387'\n"
  },
  {
    "path": "home-manager/modules/rio/config/rio/themes/pinpox-light.toml",
    "content": "[colors]\nbackground       = '#e8f0ff'\nblack            = '#e8f0ff'\nblue             = '#1e66f5'\ncursor           = '#24273a'\ncyan             = '#179299'\nforeground       = '#24273a'\ngreen            = '#40a02b'\nmagenta          = '#8839ef'\nred              = '#d20f39'\ntabs             = '#e1e4e8'\ntabs-active      = '#ccd0da'\nwhite            = '#24273a'\nyellow           = '#df8e1d'\ndim-black        = '#dce0e8'\ndim-blue         = '#1752c9'\ndim-cyan         = '#127680'\ndim-foreground   = '#1c1f2e'\ndim-green        = '#357f23'\ndim-magenta      = '#722fc3'\ndim-red          = '#a90c2e'\ndim-white        = '#1c1f2e'\ndim-yellow       = '#b57418'\nlight-black      = '#5b6078'\nlight-blue       = '#267ff5'\nlight-cyan       = '#1da6ad'\nlight-foreground = '#2c2f3d'\nlight-green      = '#4db332'\nlight-magenta    = '#9c46f5'\nlight-red        = '#e42144'\nlight-white      = '#363a4f'\nlight-yellow     = '#f29d23'\n"
  },
  {
    "path": "home-manager/modules/rio/config/rio/themes/wildcharm-dark.toml",
    "content": "[colors]\nbackground       = '#000000'\nblack            = '#000000'\nblue             = '#0087d7'\ncursor           = '#ffffff'\ncyan             = '#00afaf'\nforeground       = '#d0d0d0'\ngreen            = '#00af5f'\nmagenta          = '#d787d7'\nred              = '#d7005f'\ntabs             = '#1c1c1c'\ntabs-active      = '#303030'\nwhite            = '#d0d0d0'\nyellow           = '#d78700'\ndim-black        = '#262626'\ndim-blue         = '#005faf'\ndim-cyan         = '#008787'\ndim-foreground   = '#8a8a8a'\ndim-green        = '#008700'\ndim-magenta      = '#875f87'\ndim-red          = '#af005f'\ndim-white        = '#8a8a8a'\ndim-yellow       = '#af8700'\nlight-black      = '#767676'\nlight-blue       = '#00afff'\nlight-cyan       = '#00d7d7'\nlight-foreground = '#ffffff'\nlight-green      = '#00d75f'\nlight-magenta    = '#ff87ff'\nlight-red        = '#ff5f87'\nlight-white      = '#ffffff'\nlight-yellow     = '#ffaf00'\n"
  },
  {
    "path": "home-manager/modules/rio/config/rio/themes/wildcharm-light.toml",
    "content": "[colors]\nbackground       = '#ffffff'\nblack            = '#000000'\nblue             = '#005faf'\ncursor           = '#000000'\ncyan             = '#008787'\nforeground       = '#000000'\ngreen            = '#008700'\nmagenta          = '#870087'\nred              = '#af0000'\ntabs             = '#e4e4e4'\ntabs-active      = '#d0d0d0'\nwhite            = '#ffffff'\nyellow           = '#af5f00'\ndim-black        = '#262626'\ndim-blue         = '#00305f'\ndim-cyan         = '#005f5f'\ndim-foreground   = '#585858'\ndim-green        = '#005f00'\ndim-magenta      = '#5f005f'\ndim-red          = '#870000'\ndim-white        = '#d0d0d0'\ndim-yellow       = '#875f00'\nlight-black      = '#585858'\nlight-blue       = '#0087d7'\nlight-cyan       = '#00afaf'\nlight-foreground = '#262626'\nlight-green      = '#00af5f'\nlight-magenta    = '#af00af'\nlight-red        = '#d70000'\nlight-white      = '#ffffff'\nlight-yellow     = '#d78700'\n"
  },
  {
    "path": "home-manager/modules/rio/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.rio;\nin\n{\n  options.pinpox.programs.rio.enable = mkEnableOption \"rio terminal emulator\";\n\n  config = mkIf cfg.enable {\n    home.packages = with pkgs; [ rio ];\n    xdg.enable = true;\n    xdg.configFile.rio.source = ./config/rio;\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/river/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.river;\n  start-river =\n    pkgs.writeShellScriptBin \"start-river\" # sh\n      ''\n        export WLR_DRM_NO_MODIFIERS=1\n        dbus-launch --sh-syntax --exit-with-session ${pkgs.river-classic}/bin/river\n      '';\n\n  screenshot-region =\n    pkgs.writeShellScriptBin \"screenshot-region\" # sh\n      ''\n        ${pkgs.slurp}/bin/slurp | ${pkgs.grim}/bin/grim -g - - | ${pkgs.wl-clipboard}/bin/wl-copy -t image/png\n      '';\n\n  screenshot-region-file =\n    pkgs.writeShellScriptBin \"screenshot-region-file\" # sh\n      ''\n        ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" $(date +'%s_grim.png')\n      '';\nin\n{\n  options.pinpox.programs.river.enable = mkEnableOption \"river window manager\";\n\n  config = mkIf cfg.enable {\n\n    # Sets --indicator for network-manager-applet, which makes it work in river\n    xsession.preferStatusNotifierItems = true;\n\n    # Install these packages for my user\n    home.packages = with pkgs; [\n      river-classic\n      river-luatile\n      # way-displays\n      waybar\n      wl-clipboard\n      wlr-randr\n      start-river\n      screenshot-region\n      screenshot-region-satty\n      screenshot-region-file\n    ];\n\n    xdg = {\n      enable = true;\n      configFile = {\n\n        # River configuration files\n        river-config = {\n          target = \"river/init\";\n          source = ./river-config;\n          executable = true;\n        };\n\n        river-config-extra = {\n          target = \"river/init_exta\";\n          text = # sh\n            ''\n              riverctl map-switch normal lid close spawn ${pkgs.swaylock}/bin/swaylock\n              # riverctl map normal Super F12 spawn '${pkgs.slurp}/bin/slurp | ${pkgs.grim}/bin/grim -g - - | ${pkgs.wl-clipboard}/bin/wl-copy -t image/png'\n              # riverctl map normal Super F12 spawn ${screenshot-region}\n              riverctl map normal Super p spawn \"${pkgs.tofi}/bin/tofi-run\"\n\n              ${pkgs.mako}/bin/mako &\n              waybar\n              # ${pkgs.wlr-randr}/bin/wlr-randr --output eDP-1 --mode 1920x1080 --pos 0,0 \\\n              # --output DP-1 --mode 2560x1440 --pos 4480,0 \\\n              # --output DP-2 --mode 2560x1440@164.54 --pos 1920,0\n\n              # wlr-randr --output eDP-1 --on --mode 1920x1080 --pos 0,0 --output DP-1  --on --mode 2560x1440 --pos 4480,0 --output DP-2  --on --mode 2560x1440  --pos 1920,0\n\n            '';\n          executable = true;\n        };\n\n        # river-luatile layouts\n        luatile-layout = {\n          target = \"river-luatile/layout.lua\";\n          source = ./layout.lua;\n        };\n\n        luatile-json = {\n          target = \"river-luatile/json.lua\";\n          source = ./json.lua;\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/river/json.lua",
    "content": "\n--\n-- json.lua\n--\n-- Copyright (c) 2020 rxi\n--\n-- Permission is hereby granted, free of charge, to any person obtaining a copy of\n-- this software and associated documentation files (the \"Software\"), to deal in\n-- the Software without restriction, including without limitation the rights to\n-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\n-- of the Software, and to permit persons to whom the Software is furnished to do\n-- so, subject to the following conditions:\n--\n-- The above copyright notice and this permission notice shall be included in all\n-- copies or substantial portions of the Software.\n--\n-- THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n-- SOFTWARE.\n--\n\nlocal json = { _version = \"0.1.2\" }\n\n-------------------------------------------------------------------------------\n-- Encode\n-------------------------------------------------------------------------------\n\nlocal encode\n\nlocal escape_char_map = {\n  [ \"\\\\\" ] = \"\\\\\",\n  [ \"\\\"\" ] = \"\\\"\",\n  [ \"\\b\" ] = \"b\",\n  [ \"\\f\" ] = \"f\",\n  [ \"\\n\" ] = \"n\",\n  [ \"\\r\" ] = \"r\",\n  [ \"\\t\" ] = \"t\",\n}\n\nlocal escape_char_map_inv = { [ \"/\" ] = \"/\" }\nfor k, v in pairs(escape_char_map) do\n  escape_char_map_inv[v] = k\nend\n\n\nlocal function escape_char(c)\n  return \"\\\\\" .. (escape_char_map[c] or string.format(\"u%04x\", c:byte()))\nend\n\n\nlocal function encode_nil(val)\n  return \"null\"\nend\n\n\nlocal function encode_table(val, stack)\n  local res = {}\n  stack = stack or {}\n\n  -- Circular reference?\n  if stack[val] then error(\"circular reference\") end\n\n  stack[val] = true\n\n  if rawget(val, 1) ~= nil or next(val) == nil then\n    -- Treat as array -- check keys are valid and it is not sparse\n    local n = 0\n    for k in pairs(val) do\n      if type(k) ~= \"number\" then\n        error(\"invalid table: mixed or invalid key types\")\n      end\n      n = n + 1\n    end\n    if n ~= #val then\n      error(\"invalid table: sparse array\")\n    end\n    -- Encode\n    for i, v in ipairs(val) do\n      table.insert(res, encode(v, stack))\n    end\n    stack[val] = nil\n    return \"[\" .. table.concat(res, \",\") .. \"]\"\n\n  else\n    -- Treat as an object\n    for k, v in pairs(val) do\n      if type(k) ~= \"string\" then\n        error(\"invalid table: mixed or invalid key types\")\n      end\n      table.insert(res, encode(k, stack) .. \":\" .. encode(v, stack))\n    end\n    stack[val] = nil\n    return \"{\" .. table.concat(res, \",\") .. \"}\"\n  end\nend\n\n\nlocal function encode_string(val)\n  return '\"' .. val:gsub('[%z\\1-\\31\\\\\"]', escape_char) .. '\"'\nend\n\n\nlocal function encode_number(val)\n  -- Check for NaN, -inf and inf\n  if val ~= val or val <= -math.huge or val >= math.huge then\n    error(\"unexpected number value '\" .. tostring(val) .. \"'\")\n  end\n  return string.format(\"%.14g\", val)\nend\n\n\nlocal type_func_map = {\n  [ \"nil\"     ] = encode_nil,\n  [ \"table\"   ] = encode_table,\n  [ \"string\"  ] = encode_string,\n  [ \"number\"  ] = encode_number,\n  [ \"boolean\" ] = tostring,\n}\n\n\nencode = function(val, stack)\n  local t = type(val)\n  local f = type_func_map[t]\n  if f then\n    return f(val, stack)\n  end\n  error(\"unexpected type '\" .. t .. \"'\")\nend\n\n\nfunction json.encode(val)\n  return ( encode(val) )\nend\n\n\n-------------------------------------------------------------------------------\n-- Decode\n-------------------------------------------------------------------------------\n\nlocal parse\n\nlocal function create_set(...)\n  local res = {}\n  for i = 1, select(\"#\", ...) do\n    res[ select(i, ...) ] = true\n  end\n  return res\nend\n\nlocal space_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\")\nlocal delim_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\", \"]\", \"}\", \",\")\nlocal escape_chars  = create_set(\"\\\\\", \"/\", '\"', \"b\", \"f\", \"n\", \"r\", \"t\", \"u\")\nlocal literals      = create_set(\"true\", \"false\", \"null\")\n\nlocal literal_map = {\n  [ \"true\"  ] = true,\n  [ \"false\" ] = false,\n  [ \"null\"  ] = nil,\n}\n\n\nlocal function next_char(str, idx, set, negate)\n  for i = idx, #str do\n    if set[str:sub(i, i)] ~= negate then\n      return i\n    end\n  end\n  return #str + 1\nend\n\n\nlocal function decode_error(str, idx, msg)\n  local line_count = 1\n  local col_count = 1\n  for i = 1, idx - 1 do\n    col_count = col_count + 1\n    if str:sub(i, i) == \"\\n\" then\n      line_count = line_count + 1\n      col_count = 1\n    end\n  end\n  error( string.format(\"%s at line %d col %d\", msg, line_count, col_count) )\nend\n\n\nlocal function codepoint_to_utf8(n)\n  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa\n  local f = math.floor\n  if n <= 0x7f then\n    return string.char(n)\n  elseif n <= 0x7ff then\n    return string.char(f(n / 64) + 192, n % 64 + 128)\n  elseif n <= 0xffff then\n    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)\n  elseif n <= 0x10ffff then\n    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,\n                       f(n % 4096 / 64) + 128, n % 64 + 128)\n  end\n  error( string.format(\"invalid unicode codepoint '%x'\", n) )\nend\n\n\nlocal function parse_unicode_escape(s)\n  local n1 = tonumber( s:sub(1, 4),  16 )\n  local n2 = tonumber( s:sub(7, 10), 16 )\n   -- Surrogate pair?\n  if n2 then\n    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)\n  else\n    return codepoint_to_utf8(n1)\n  end\nend\n\n\nlocal function parse_string(str, i)\n  local res = \"\"\n  local j = i + 1\n  local k = j\n\n  while j <= #str do\n    local x = str:byte(j)\n\n    if x < 32 then\n      decode_error(str, j, \"control character in string\")\n\n    elseif x == 92 then -- `\\`: Escape\n      res = res .. str:sub(k, j - 1)\n      j = j + 1\n      local c = str:sub(j, j)\n      if c == \"u\" then\n        local hex = str:match(\"^[dD][89aAbB]%x%x\\\\u%x%x%x%x\", j + 1)\n                 or str:match(\"^%x%x%x%x\", j + 1)\n                 or decode_error(str, j - 1, \"invalid unicode escape in string\")\n        res = res .. parse_unicode_escape(hex)\n        j = j + #hex\n      else\n        if not escape_chars[c] then\n          decode_error(str, j - 1, \"invalid escape char '\" .. c .. \"' in string\")\n        end\n        res = res .. escape_char_map_inv[c]\n      end\n      k = j + 1\n\n    elseif x == 34 then -- `\"`: End of string\n      res = res .. str:sub(k, j - 1)\n      return res, j + 1\n    end\n\n    j = j + 1\n  end\n\n  decode_error(str, i, \"expected closing quote for string\")\nend\n\n\nlocal function parse_number(str, i)\n  local x = next_char(str, i, delim_chars)\n  local s = str:sub(i, x - 1)\n  local n = tonumber(s)\n  if not n then\n    decode_error(str, i, \"invalid number '\" .. s .. \"'\")\n  end\n  return n, x\nend\n\n\nlocal function parse_literal(str, i)\n  local x = next_char(str, i, delim_chars)\n  local word = str:sub(i, x - 1)\n  if not literals[word] then\n    decode_error(str, i, \"invalid literal '\" .. word .. \"'\")\n  end\n  return literal_map[word], x\nend\n\n\nlocal function parse_array(str, i)\n  local res = {}\n  local n = 1\n  i = i + 1\n  while 1 do\n    local x\n    i = next_char(str, i, space_chars, true)\n    -- Empty / end of array?\n    if str:sub(i, i) == \"]\" then\n      i = i + 1\n      break\n    end\n    -- Read token\n    x, i = parse(str, i)\n    res[n] = x\n    n = n + 1\n    -- Next token\n    i = next_char(str, i, space_chars, true)\n    local chr = str:sub(i, i)\n    i = i + 1\n    if chr == \"]\" then break end\n    if chr ~= \",\" then decode_error(str, i, \"expected ']' or ','\") end\n  end\n  return res, i\nend\n\n\nlocal function parse_object(str, i)\n  local res = {}\n  i = i + 1\n  while 1 do\n    local key, val\n    i = next_char(str, i, space_chars, true)\n    -- Empty / end of object?\n    if str:sub(i, i) == \"}\" then\n      i = i + 1\n      break\n    end\n    -- Read key\n    if str:sub(i, i) ~= '\"' then\n      decode_error(str, i, \"expected string for key\")\n    end\n    key, i = parse(str, i)\n    -- Read ':' delimiter\n    i = next_char(str, i, space_chars, true)\n    if str:sub(i, i) ~= \":\" then\n      decode_error(str, i, \"expected ':' after key\")\n    end\n    i = next_char(str, i + 1, space_chars, true)\n    -- Read value\n    val, i = parse(str, i)\n    -- Set\n    res[key] = val\n    -- Next token\n    i = next_char(str, i, space_chars, true)\n    local chr = str:sub(i, i)\n    i = i + 1\n    if chr == \"}\" then break end\n    if chr ~= \",\" then decode_error(str, i, \"expected '}' or ','\") end\n  end\n  return res, i\nend\n\n\nlocal char_func_map = {\n  [ '\"' ] = parse_string,\n  [ \"0\" ] = parse_number,\n  [ \"1\" ] = parse_number,\n  [ \"2\" ] = parse_number,\n  [ \"3\" ] = parse_number,\n  [ \"4\" ] = parse_number,\n  [ \"5\" ] = parse_number,\n  [ \"6\" ] = parse_number,\n  [ \"7\" ] = parse_number,\n  [ \"8\" ] = parse_number,\n  [ \"9\" ] = parse_number,\n  [ \"-\" ] = parse_number,\n  [ \"t\" ] = parse_literal,\n  [ \"f\" ] = parse_literal,\n  [ \"n\" ] = parse_literal,\n  [ \"[\" ] = parse_array,\n  [ \"{\" ] = parse_object,\n}\n\n\nparse = function(str, idx)\n  local chr = str:sub(idx, idx)\n  local f = char_func_map[chr]\n  if f then\n    return f(str, idx)\n  end\n  decode_error(str, idx, \"unexpected character '\" .. chr .. \"'\")\nend\n\n\nfunction json.decode(str)\n  if type(str) ~= \"string\" then\n    error(\"expected argument of type string, got \" .. type(str))\n  end\n  local res, idx = parse(str, next_char(str, 1, space_chars, true))\n  idx = next_char(str, idx, space_chars, true)\n  if idx <= #str then\n    decode_error(str, idx, \"trailing garbage\")\n  end\n  return res\nend\n\n\nreturn json\n"
  },
  {
    "path": "home-manager/modules/river/layout.lua",
    "content": "package.path = package.path .. \";/home/pinpox/.config/river-luatile/?.lua\"\njson = require \"json\"\n\nprint(\"river-luatile started!\")\n-- You can define your global state here\nmain_ratio = 0.65\ngaps = 10\nsmart_gaps = false\ncurrent_layout = \"rivertile\"\n\noutput_layouts = { }\n\n-- The most important function - the actual layout generator\n--\n-- The argument is a table with:\n--  * Focused tags\n--  * Window count\n--  * Output width\n--  * Output height\n--\n-- The return value must be a table with exactly `count` entries. Each entry is a table with four\n-- numbers:\n--  * X coordinate\n--  * Y coordinate\n--  * Window width\n--  * Window height\n\nargumunts = {}\n\nfunction handle_metadata(args)\n\treturn { name = output_layouts[args.output] }\nend\n\n-- We choose from one of the existing layouts defined in the table further down\nfunction handle_layout(args)\n\t-- print(args.output)\n\targuments = args\n\n\t-- Default to rivertile as layout for all outputs\n\tif output_layouts[args.output] == nil then\n\t\toutput_layouts[args.output] = \"rivertile\"\n\tend\n\treturn layouts[output_layouts[args.output]](args)\nend\n\n-- This example is a simplified version of `rivertile`\nfunction handle_layout_rivertile(args)\n\n\tprint(\"handle_layout() reached!\")\n\t-- print(json.encode(args))\n\tlocal retval = {}\n\tif args.count == 1 then\n\t\tif smart_gaps then\n\t\t\ttable.insert(retval, { 0, 0, args.width, args.height })\n\t\telse\n\t\t\ttable.insert(retval, { gaps, gaps, args.width - gaps * 2, args.height - gaps * 2 })\n\t\tend\n\telseif args.count > 1 then\n\t\tlocal main_w = (args.width - gaps * 3) * main_ratio\n\t\tlocal side_w = (args.width - gaps * 3) - main_w\n\t\tlocal main_h = args.height - gaps * 2\n\t\tlocal side_h = (args.height - gaps) / (args.count - 1) - gaps\n\t\ttable.insert(retval, {\n\t\t\tgaps,\n\t\t\tgaps,\n\t\t\tmain_w,\n\t\t\tmain_h,\n\t\t})\n\t\tfor i = 0, (args.count - 2) do\n\t\t\ttable.insert(retval, {\n\t\t\t\tmain_w + gaps * 2,\n\t\t\t\tgaps + i * (side_h + gaps),\n\t\t\t\tside_w,\n\t\t\t\tside_h,\n\t\t\t})\n\t\tend\n\tend\n\t-- print(json.encode(retval))\n\treturn retval\nend\n\n-- Monocle layout: Show just one big window, but not fullscreen\nfunction handle_layout_monocle(args)\n\tlocal retval = {}\n\n\toffset = 20\n\tgap = 5\n\n\tfor i = 0, (args.count -1) do\n\t\ttable.insert(retval, {\n\t\t\tgap + i*offset,\n\t\t\tgap + i*offset,\n\t\t\t(args.width - gap *2) -  (args.count -1) * offset,\n\t\t\t(args.height - gap *2) - (args.count -1) * offset,\n\t\t})\n\tend\n\treturn retval\nend\n\n\n-- IMPORTANT: User commands send via `riverctl send-layout-cmd` are treated as lua code.\n-- Active tags are stored in `CMD_TAGS` global variable.\n\n-- Here is an example of a function that can be mapped to some key\n-- Run with `riverctl send-layout-cmd luatile \"toggle_gaps()\"`\nlocal gaps_alt = 0\nfunction toggle_gaps()\n\tprint(\"toggle_gaps() reached!\")\n\tlocal tmp = gaps\n\tgaps = gaps_alt\n\tgaps_alt = tmp\nend\n\n-- Change output to a specific layout\nfunction layout_switch(layout_name)\n\tif  layouts[layout_name] ~= nil then\n\t\tcurrent_layout = layout_name\n\tend\nend\n\n-- Cycle layout of an output\nfunction layout_cycle()\n\tprint(\"CYCLING LAYOUTS\")\n\n\tcurrent_layout = output_layouts[CMD_OUTPUT]\n\n\t-- TODO this could be nicer with a cycle\n\tif current_layout == \"rivertile\" then\n\t\tcurrent_layout = \"monocle\"\n\telseif current_layout == \"monocle\" then\n\t\tcurrent_layout = \"rivertile\"\n\tend\n\n\toutput_layouts[CMD_OUTPUT] = current_layout\n\nend\n\n-- Add all layouts here that should be supported\nlayouts = {\n\trivertile = handle_layout_rivertile,\n\tmonocle = handle_layout_monocle,\n\t-- grid = handle_layout_grid,\n}\n\n\n"
  },
  {
    "path": "home-manager/modules/river/river-config",
    "content": "#!/usr/bin/env bash\n\n# This is the example configuration file for river.\n#\n# If you wish to edit this, you will probably want to copy it to\n# $XDG_CONFIG_HOME/river/init or $HOME/.config/river/init first.\n#\n# See the river(1), riverctl(1), and rivertile(1) man pages for complete\n# documentation.\n\n\n# Set keyboard layout\n# riverctl keyboard-layout -variant colemak us\nriverctl keyboard-layout -variant colemak -options \"caps:swapescape\" us\n\n# Super+Return to start an instance of foot (https://codeberg.org/dnkl/foot)\nriverctl map normal Super Return spawn foot\n\n\n# Super+Shif+Q to close the focused view\nriverctl map normal Super+Shift Q close\n\n# Super+Shift+E to exit river\nriverctl map normal Super+Shift E exit\n\n# Focus should follow the mouse\nriverctl focus-follows-cursor normal\n# riverctl focus-follows-cursor disabled\n\n# Super+Tab and Super+Shift+Tab to focus the next/previous view in the layout\n# stack\nriverctl map normal Super Tab focus-view next\nriverctl map normal Super+Shift Tab focus-view previous\n\n# TODO focus-view has not implemeted left/down/right/left (yet)\n# riverctl map normal Super H focus-view left\n# riverctl map normal Super J focus-view down\n# riverctl map normal Super K focus-view up\n# riverctl map normal Super L focus-view right\n\n# Super+Shift+J and Super+Shift+K to swap the focused view with the next/previous\n# view in the layout stack\nriverctl map normal Super+Shift J swap next\nriverctl map normal Super+Shift K swap previous\n\n# Super+H and Super+L to decrease/increase the main ratio of rivertile(1)\nriverctl map normal Super+Shift H send-layout-cmd rivertile \"main-ratio -0.05\"\nriverctl map normal Super+Shift L send-layout-cmd rivertile \"main-ratio +0.05\"\n\n# Super+Period and Super+Comma to focus the next/previous output\nriverctl map normal Super Period focus-output right\nriverctl map normal Super Comma focus-output left\n\n# Super+Shift+{Period,Comma} to send the focused view to the next/previous output\n# TODO use -current-tags once PR has reached release\n# https://github.com/riverwm/river/commit/b3698150708bec276454c0ff7c707d9dab446b1e\n# riverctl map normal Super+Shift Period send-to-output -current-tags right\n# riverctl map normal Super+Shift Comma send-to-output -current-tags left\nriverctl map normal Super+Shift Period send-to-output right\nriverctl map normal Super+Shift Comma send-to-output left\n\n# Super+Return to bump the focused view to the top of the layout stack\nriverctl map normal Super+Shift Return zoom\n\n# Super+Shift++ and Super+Shift+- to increment/decrement the main count of rivertile(1)\nriverctl map normal Super+Shift + send-layout-cmd rivertile \"main-count +1\"\nriverctl map normal Super+Shift - send-layout-cmd rivertile \"main-count -1\"\n\n# Super+Alt+{H,J,K,L} to move views\nriverctl map normal Super+Alt H move left 100\nriverctl map normal Super+Alt J move down 100\nriverctl map normal Super+Alt K move up 100\nriverctl map normal Super+Alt L move right 100\n\n# Super+Alt+Control+{H,J,K,L} to snap views to screen edges\nriverctl map normal Super+Alt+Control H snap left\nriverctl map normal Super+Alt+Control J snap down\nriverctl map normal Super+Alt+Control K snap up\nriverctl map normal Super+Alt+Control L snap right\n\n# Super+Alt+Shift+{H,J,K,L} to resize views\nriverctl map normal Super+Alt+Shift H resize horizontal -100\nriverctl map normal Super+Alt+Shift J resize vertical 100\nriverctl map normal Super+Alt+Shift K resize vertical -100\nriverctl map normal Super+Alt+Shift L resize horizontal 100\n\n# Super + Left Mouse Button to move views\nriverctl map-pointer normal Super BTN_LEFT move-view\n\n# Super + Right Mouse Button to resize views\nriverctl map-pointer normal Super BTN_RIGHT resize-view\n\n# Super + Middle Mouse Button to toggle float\nriverctl map-pointer normal Super BTN_MIDDLE toggle-float\n\nfor i in $(seq 1 9)\ndo\n    tags=$((1 << ($i - 1)))\n\n    # Super+[1-9] to focus tag [0-8]\n    riverctl map normal Super $i set-focused-tags $tags\n\n    # Super+Shift+[1-9] to tag focused view with tag [0-8]\n    riverctl map normal Super+Shift $i set-view-tags $tags\n\n    # Super+Control+[1-9] to toggle focus of tag [0-8]\n    riverctl map normal Super+Control $i toggle-focused-tags $tags\n\n    # Super+Shift+Control+[1-9] to toggle tag [0-8] of focused view\n    riverctl map normal Super+Shift+Control $i toggle-view-tags $tags\ndone\n\n# Super+0 to focus all tags\n# Super+Shift+0 to tag focused view with all tags\nall_tags=$(((1 << 32) - 1))\nriverctl map normal Super 0 set-focused-tags $all_tags\nriverctl map normal Super+Shift 0 set-view-tags $all_tags\n\n# Super+Shift+Space to toggle float\nriverctl map normal Super+Shift Space toggle-float\n\n# Super+Space to cycle layouts\nriverctl map normal Super Space send-layout-cmd luatile 'layout_cycle()'\n\n# Super+F to toggle fullscreen\nriverctl map normal Super F toggle-fullscreen\n\n# Super+{Up,Right,Down,Left} to change layout orientation\nriverctl map normal Super Up    send-layout-cmd rivertile \"main-location top\"\nriverctl map normal Super Right send-layout-cmd rivertile \"main-location right\"\nriverctl map normal Super Down  send-layout-cmd rivertile \"main-location bottom\"\nriverctl map normal Super Left  send-layout-cmd rivertile \"main-location left\"\n\n# Declare a passthrough mode. This mode has only a single mapping to return to\n# normal mode. This makes it useful for testing a nested wayland compositor\nriverctl declare-mode passthrough\n\n# Super+F11 to enter passthrough mode\nriverctl map normal Super F11 enter-mode passthrough\n\n# Super+F11 to return to normal mode\nriverctl map passthrough Super F11 enter-mode normal\n\n# Various media key mapping examples for both normal and locked mode which do\n# not have a modifier\nfor mode in normal locked\ndo\n    # Eject the optical drive (well if you still have one that is)\n    # riverctl map $mode None XF86Eject spawn 'eject -T'\n\n    # Control pulse audio volume with pamixer (https://github.com/cdemoulins/pamixer)\n    riverctl map $mode None XF86AudioRaiseVolume  spawn 'pamixer -i 5'\n    riverctl map $mode None XF86AudioLowerVolume  spawn 'pamixer -d 5'\n    riverctl map $mode None XF86AudioMute         spawn 'pamixer --toggle-mute'\n\n    riverctl map $mode Super F12 spawn 'amixer set Capture toggle' # Mute microphone\n\n    # Control MPRIS aware media players with playerctl (https://github.com/altdesktop/playerctl)\n    riverctl map $mode None XF86AudioMedia spawn 'playerctl play-pause'\n    riverctl map $mode None XF86AudioPlay  spawn 'playerctl play-pause'\n    riverctl map $mode None XF86AudioPrev  spawn 'playerctl previous'\n    riverctl map $mode None XF86AudioNext  spawn 'playerctl next'\n\ndone\n\n# Set background and border color\nriverctl background-color 0x #002b36\nriverctl border-color-focused 0x418fdd\nriverctl border-color-unfocused 0x586e75\n\n# Set keyboard repeat rate\nriverctl set-repeat 50 300\n\n# Warp cursor when changing focus with keyboard\nriverctl set-cursor-warp on-output-change\n\n# Use lswt to get IDs of windows\n\n# Make all views with an app-id that starts with \"float\" and title \"foo\" start floating.\nriverctl rule-add float -app-id 'float*' -title 'foo'\n\n# Float firefox screenshare indicator\nriverctl rule-add float -title 'Firefox — Sharing Indicator'\n\n# Make all views with app-id \"bar\" and any title use client-side decorations\nriverctl rule-add csd -app-id \"bar\"\n\n# Make specific applications use server-side decorations\nriverctl rule-add ssd -app-id firefox\nriverctl rule-add ssd -app-id pavucontrol\nriverctl rule-add ssd -app-id thunderbird\n\n# Needed to make xdg-desktop-portal-wlr work (screensharing)\ndbus-update-activation-environment --systemd WAYLAND_DISPLAY WAYLAND_DESKTOP\n\n# Set the default layout generator to be river-luatile and start it.\n# River will send the process group of the init executable SIGTERM on exit.\nriverctl default-layout luatile\n# riverctl default-layout rivertile\n# rivertile -view-padding 6 -outer-padding 6 &\nriver-luatile &\n\n# Read nixos-generated config part\n/home/pinpox/.config/river/init_exta\n\n\n\n"
  },
  {
    "path": "home-manager/modules/shell/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.shell;\n  colors = config.pinpox.colors;\nin\n{\n\n  options.pinpox.defaults.shell = {\n    enable = mkEnableOption \"shell defaults\";\n    abbrev-aliases = mkOption {\n      type =\n        with types;\n        listOf (submodule {\n          options = {\n            alias = mkOption { type = str; };\n            command = mkOption { type = str; };\n            global = mkOption {\n              type = bool;\n              default = false;\n              description = \"Expand alias everywhere, not only at the beginning of a line.\";\n            };\n            recursive = mkOption {\n              type = bool;\n              default = false;\n              description = \"Expand aliases recursively\";\n            };\n            eval = mkOption {\n              type = bool;\n              default = false;\n              description = \"Evaluate subshells on expansion\";\n            };\n          };\n        });\n\n      example = [\n        {\n          alias = \"nfu\";\n          command = \"nix flake update --commit-lock-file\";\n        }\n        {\n          global = true;\n          alias = \"G\";\n          command = \"| rg -i\";\n        }\n      ];\n\n      description = ''\n        Aliases for abbrev-allias ZSH plugin\n        https://github.com/momo-lab/zsh-abbrev-alias\n      '';\n    };\n  };\n\n  imports = [\n    ./starship.nix\n    ./zsh.nix\n    # ./fish.nix\n  ];\n\n  config = mkIf cfg.enable {\n\n    programs.direnv = {\n      enable = true;\n      enableZshIntegration = true;\n      nix-direnv.enable = true;\n      # https://direnv.net/man/direnv.toml.1.html\n      # config = {};\n    };\n\n    pinpox.defaults.shell.abbrev-aliases = [\n\n      # Aliases expanded only at beginning of lines\n      {\n        alias = \"flakeinit\";\n        command = \"nix flake init -t github:pinpox/nixos\";\n      }\n      {\n        alias = \"g\";\n        command = \"git\";\n      }\n      {\n        alias = \"o\";\n        command = \"xdg-open\";\n      }\n      {\n        alias = \"q\";\n        command = \"exit\";\n      }\n      {\n        alias = \"snvim\";\n        command = \"sudo -E nvim\";\n      }\n      {\n        alias = \"v\";\n        command = \"nvim\";\n      }\n      {\n        alias = \"nfu\";\n        command = \"nix flake update --commit-lock-file\";\n      }\n      # {\n      #   alias = \"yt-dlp-mp4\";\n      #   command = \"nix run nixpkgs#yt-dlp -- -S res,ext:mp4:m4a --recode mp4 \";\n      # }\n      {\n        alias = \"rm-orig\";\n        command = ''find . -name \"*.orig\" -type f -delete'';\n      }\n      # Global aliases, get expanded everywhere\n      {\n        global = true;\n        alias = \"G\";\n        command = \"| rg -i\";\n      }\n      {\n        global = true;\n        alias = \"P\";\n        command = \"| paste\";\n      }\n    ];\n\n    programs.fzf = {\n      enable = true;\n      enableZshIntegration = true;\n      defaultOptions = [\n        \"--height 40%\"\n        \"--layout=reverse\"\n        \"--border\"\n        \"--inline-info\"\n        \"--color 'fg:#${colors.White}'\" # Text\n        \"--color 'bg:#${colors.Black}'\" # Background\n        \"--color 'preview-fg:#${colors.White}'\" # Preview window text\n        \"--color 'preview-bg:#${colors.Black}'\" # Preview window background\n        \"--color 'hl:#${colors.Yellow}'\" # Highlighted substrings\n        \"--color 'fg+:#${colors.Blue}'\" # Text (current line)\n        \"--color 'bg+:#${colors.BrightBlack}'\" # Background (current line)\n        \"--color 'gutter:#${colors.BrightBlack}'\" # Gutter on the left (defaults to bg+)\n        \"--color 'hl+:#${colors.Magenta}'\" # Highlighted substrings (current line)\n        \"--color 'info:#${colors.Magenta}'\" # Info line (match counters)\n        \"--color 'border:#${colors.Blue}'\" # Border around the window (--border and --preview)\n        \"--color 'prompt:#${colors.White}'\" # Prompt\n        \"--color 'pointer:#${colors.Magenta}'\" # Pointer to the current line\n        \"--color 'marker:#${colors.Magenta}'\" # Multi-select marker\n        \"--color 'spinner:#${colors.Magenta}'\" # Streaming input indicator\n        \"--color 'header:#${colors.White}'\" # Header\n      ];\n    };\n\n    programs.dircolors = {\n      enable = true;\n      enableZshIntegration = true;\n    };\n\n    programs.pazi = {\n      enable = true;\n      enableZshIntegration = true;\n    };\n\n    programs.htop = {\n      enable = true;\n      settings.tree_view = true;\n    };\n\n    programs.jq.enable = true;\n\n    programs.bat = {\n      enable = true;\n      # TODO: This should pick up the correct colors for the generated theme. Otherwise\n      # it is possible to generate a custom bat theme to ~/.config/bat/config\n      config = {\n        theme = \"base16\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/shell/fish.nix",
    "content": "{ pkgs, promterm, ... }:\n{\n\n  programs = {\n\n    fzf.enableFishIntegration = true;\n    dircolors.enableFishIntegration = true;\n    pazi.enableFishIntegration = true;\n  };\n\n  programs.fish = {\n    enable = true;\n    functions = {\n      gitignore = \"curl -sL https://www.gitignore.io/api/$argv\";\n      fish_command_not_found = \"echo Did not find command $argv[1]\";\n\n      # Create and change to a directory\n      take = ''mkdir -p -- \"$1\" && cd -- \"$1\"'';\n\n      # Create and change to a new temporary directory\n      ttake = \"cd $(mktemp -d)\";\n\n      # Use `line 10 /etc/hosts` to get 10th line of file\n      line = ''awk \"NR == $1\" \"$2\"'';\n    };\n\n    plugins = [\n\n      {\n\n        # https://github.com/gazorby/fifc\n\n        name = \"fifc\";\n        src = pkgs.fetchFromGitHub {\n          owner = \"gazorby\";\n          repo = \"fifc\";\n          rev = \"a01650cd432becdc6e36feeff5e8d657bd7ee84a\";\n          sha256 = \"sha256-Ynb0Yd5EMoz7tXwqF8NNKqCGbzTZn/CwLsZRQXIAVp4=\";\n        };\n      }\n\n    ];\n\n    shellAbbrs = {\n\n      o = \"xdg-open\";\n      q = \"exit\";\n      snvim = \"sudo -E nvim\";\n      v = \"nvim\";\n\n      # Global aliases, get expanded everywhere\n      # abbrev-alias -g G = \"| rg -i\"\n      # abbrev-alias - g P=\"| tb\"\n      #TODO\n    };\n    shellAliases = rec {\n\n      # Eza ls replacement\n      ls = \"${pkgs.eza}/bin/eza --group-directories-first\";\n      l = \"${ls} -lbF --git --icons\";\n      ll = \"${l} -G\";\n      la = \"${ls} -lbhHigmuSa@ --time-style=long-iso --git --color-scale --icons\";\n      lt = \"${ls} --tree --level=2 --icons\";\n\n      # Git\n      gs = \"${pkgs.git}/bin/git status\";\n\n      # Pastebin (termbin.com)\n      tb = \"${pkgs.netcat-gnu}/bin/nc termbin.com 9999\";\n\n      # Frequendly used folders\n      cdn = \"cd ~/code/github.com/pinpox/nixos\";\n      cdnh = \"cd ~/code/github.com/pinpox/nixos-home\";\n\n      # Other\n      pt = \"${promterm.defaultPackage.x86_64-linux}/bin/promterm 'https://vpn.prometheus.pablo.tools/api/v1/alerts'\";\n      lsblk = \"lsblk -o name,mountpoint,label,size,type,uuid\";\n      c = \"${pkgs.bat}/bin/bat -n --decorations never\";\n      cc = \"${pkgs.clang}/bin/clang -Wall -Wextra -pedantic -std=c99 -Wshadow -Weverything\";\n      qr = \"${pkgs.qrencode}/bin/qrencode -t utf8 -o-\";\n      top = \"${pkgs.htop}/bin/htop\";\n      weather = \"${pkgs.curl}/bin/curl -4 http://wttr.in/Koeln\";\n      #radio = \"${\n      #pkgs.mpv}/bin/mpv http://lassul.us:8000/radio.ogg\";\n\n      zzz = \"systemctl suspend\";\n\n      serve = \"${pkgs.miniserve}/bin/miniserve\";\n\n      za = \"${./zellij-chooser}\";\n\n      upterm = \"${pkgs.upterm}/bin/upterm host --server ssh://upterm.thalheim.io:2323 --force-command 'zellij attach pair-programming' -- zellij attach --create pair-programming\";\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/shell/starship.nix",
    "content": "{ ... }:\n{\n  programs.starship = {\n    enable = false;\n    enableBashIntegration = true;\n    enableZshIntegration = true;\n    settings = {\n\n      character = {\n        success_symbol = \"[»](bold green)\";\n        error_symbol = \"[×](bold red) \";\n      };\n\n      aws = {\n        disabled = true;\n      };\n\n      python = {\n        disabled = true;\n      };\n\n      nix_shell = {\n        symbol = \"❄  \";\n      };\n\n      git_status = {\n\n        ahead = \"↑\";\n        behind = \"↓\";\n        diverged = \"↕\";\n        modified = \"!\";\n        staged = \"±\";\n        renamed = \"→\";\n      };\n\n      directory = {\n        truncate_to_repo = false;\n        fish_style_pwd_dir_length = 2;\n\n        substitutions = {\n          \"~/code/github.com/pinpox/nixos\" = \"<pinpox/nixos>\";\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/shell/zellij-chooser",
    "content": "#!/usr/bin/env bash\n\nZJ_SESSIONS=$(zellij list-sessions -n)\nNO_SESSIONS=$(echo \"${ZJ_SESSIONS}\" | wc -l)\n\nif [ \"${NO_SESSIONS}\" -ge 2 ]; then\n    zellij attach \"$(echo \"${ZJ_SESSIONS}\" | fzf | cut -d' ' -f1)\"\nelse\n   zellij attach -c\nfi\n"
  },
  {
    "path": "home-manager/modules/shell/zsh.nix",
    "content": "{\n  flake-inputs,\n  pkgs,\n  promterm,\n  lib,\n  config,\n  ...\n}:\n{\n\n  programs.zsh = {\n    enable = true;\n    autosuggestion = {\n      enable = true;\n      highlight = \"fg=8\";\n    };\n    enableCompletion = true;\n    autocd = true;\n    dotDir = \"${config.xdg.configHome}/zsh\";\n    sessionVariables = {\n      RPS1 = \"\"; # Disable the right side prompt that \"walters\" theme introduces\n      ZDOTDIR = \"/home/pinpox/.config/zsh\";\n      # Override prezto's default LESS which includes -z-4 (negative scroll\n      # window size), rejected by less >= 691\n      LESS = \"-g -i -M -R -S -w -X -z4\";\n    };\n\n    initContent =\n      let\n        abbrevs = lib.concatStrings (\n          map (\n            a:\n            let\n              opt = lib.strings.optionalString;\n            in\n            ''\n              abbrev-alias ${opt a.global \"-g \"}${opt a.eval \"-e \"}${opt a.recursive \"-r \"}${a.alias}=\"${a.command}\"\n            ''\n          ) config.pinpox.defaults.shell.abbrev-aliases\n        );\n\n        functions = ''\n          function \"=\"() { printf \"%s\\n\" \"$@\" | ${pkgs.bc}/bin/bc }\n\n          function ai() {\n            echo \"$@\" | $##{pkgs.shell-gpt}/bin/sgpt\n          }\n\n          function aip() {\n            wl-paste | $##{pkgs.shell-gpt}/bin/sgpt\n          }\n\n          # Create a temporary, detached worktree of the current git repo.\n          # Great for quick hot-fixes.\n          twork () {\n              local wtpath=\"$(mktemp -d)\"\n              git worktree add --detach $wtpath\n              cd $wtpath\n          }\n\n          # Jump to a code project with FZF using Ctrl+j\n          fzf_cd_widget() {\n            local base=\"/home/pinpox/code\"\n            local rel full\n\n            rel=$(\n              find \"$base\" -maxdepth 3 -type d \\\n                | grep -E '(/.*){6}' \\\n                | sed \"s|^$base/||\" \\\n                | fzf --preview \"BASE=$base sh -c '${pkgs.eza}/bin/eza \\\n                  --group-directories-first --tree --level=2 --icons \\\"\\$BASE/\\$0\\\"' {}\"\n            ) || return\n\n            full=\"$base/$rel\"\n            cd \"$full\" || return\n\n            if [[ -n \"$ZLE_NAME\" ]]; then\n              zle reset-prompt\n            fi\n          }\n\n          zle -N fzf_cd_widget\n          bindkey '^J' fzf_cd_widget\n\n${lib.optionalString (pkgs.stdenv.hostPlatform.system == \"x86_64-linux\") ''\n          # Power profile function using ryzenadj (x86_64 only)\n          power-profile() {\n          if [ $# -ne 4 ]; then\n            echo \"Usage: power-profile <name> <stapm> <fast> <slow>\"\n            echo \"Example: power-profile PERFORMANCE 30000 35000 35000\"\n            return 1\n          fi\n\n          local name=$1\n          local stapm=$2 # Sustained power limit\n          local fast=$3 # Short burst power limit (instant peak response)\n          local slow=$4 # Slow burst power limit (~5-10s)\n\n          sudo ${lib.getExe pkgs.ryzenadj} --stapm-limit=$stapm --fast-limit=$fast --slow-limit=$slow && \\\n            echo \"Power profile set to: $name (''${stapm}mW/''${fast}mW/''${slow}mW)\"\n          }\n        ''}\n\n        '';\n      in\n      lib.mkMerge [\n        (lib.mkOrder 550 ((builtins.readFile ./zshrc) + ''\n          unsetopt nomatch\n        ''))\n        abbrevs\n        (builtins.readFile ./zshrc-extra)\n        (builtins.readFile ./zshrc-coffee)\n        functions\n      ];\n\n    history = {\n      expireDuplicatesFirst = true;\n      ignoreSpace = false;\n      save = 15000;\n      share = true;\n    };\n\n    dirHashes = {\n      # Allows addressing directorys by shortname, e.g. `cd ~notes`\n      docs = \"$HOME/Documents\";\n      notes = \"$HOME/Notes\";\n      downloads = \"$HOME/Downloads\";\n      nix-config = \"/home/pinpox/code/github.com/pinpox/nixos\";\n      clan = \"$HOME/code/git.clan.lol/clan/clan-core\";\n      clan-infra = \"$HOME/code/git.clan.lol/clan/clan-infra\";\n    };\n\n    shellAliases = rec {\n\n      remote-review = ''nixpkgs-review pr --build-args=\"--builders 'ssh://pinpox@build-box.nix-community.org'\"'';\n\n      # eza ls replacement\n      ls = \"${pkgs.eza}/bin/eza --group-directories-first\";\n      l = \"${ls} -lbF --git --icons\";\n      ll = \"${l} -G\";\n      la = \"${ls} -lbhHigmuSa@ --time-style=long-iso --git --color-scale --icons\";\n      lt = \"${ls} --tree --level=2 --icons\";\n\n      nb = \"nix build --no-link --print-out-paths -L\";\n      ne = \"nix eval --strict --json\";\n\n      # Git\n      gs = \"${pkgs.git}/bin/git status\";\n\n      # Pastebin (termbin.com)\n      tb = \"${pkgs.netcat-gnu}/bin/nc termbin.com 9999\";\n\n      # Frequendly used folders\n      cdn = \"cd ~/code/github.com/pinpox/nixos\";\n      cdnh = \"cd ~/code/github.com/pinpox/nixos-home\";\n\n      # Other\n      lsblk = \"lsblk -o name,mountpoint,label,size,type,uuid\";\n      c = \"${pkgs.bat}/bin/bat -n --decorations never\";\n      cc = \"${pkgs.clang}/bin/clang -Wall -Wextra -pedantic -std=c99 -Wshadow -Weverything\";\n      qr = \"${pkgs.qrencode}/bin/qrencode -t utf8 -o-\";\n      top = \"${pkgs.htop}/bin/htop\";\n      weather = \"${pkgs.curl}/bin/curl -4 http://wttr.in/Koeln\";\n      # radio = \"${\n        #pkgs.mpv}/bin/mpv http://lassul.us:8000/radio.ogg\";\n\n      # ${pkgs.yubikey-manager}/bin/ykman oath accounts code | \\\n      yotp = ''\n        ${pkgs.fzf}/bin/fzf | awk '{print $2}' | ${pkgs.xclip}/bin/xclip -sel clip\n      '';\n\n      zzz = \"systemctl suspend\";\n\n      picohsm-add-to-agent = \"ssh-add -e ${pkgs.opensc}/lib/opensc-pkcs11.so 2>/dev/null; SSH_ASKPASS=${lib.getExe pkgs.noctalia-askpass} SSH_ASKPASS_REQUIRE=prefer ssh-add -s ${pkgs.opensc}/lib/opensc-pkcs11.so\";\n\n      serve = \"${pkgs.miniserve}/bin/miniserve\";\n\n      za = \"${./zellij-chooser}\";\n\n      upterm = \"${pkgs.upterm}/bin/upterm host --server ssh://upterm.thalheim.io:2323 --force-command 'zellij attach pair-programming' -- zellij attach --create pair-programming\";\n    } // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == \"x86_64-linux\") {\n      # x86_64-only aliases\n      gif = \"${flake-inputs.gif-searcher.packages.x86_64-linux.default}/bin/show-gif\";\n      gifi = \"${flake-inputs.gif-searcher.packages.x86_64-linux.gif-infinite}/bin/show-gif\";\n      pt = \"${promterm.defaultPackage.x86_64-linux}/bin/promterm 'https://vpn.prometheus.pablo.tools/api/v1/alerts'\";\n\n      # Power profile aliases (requires ryzenadj)\n      power-performance = \"power-profile PERFORMANCE 30000 35000 35000\";\n      power-balanced = \"power-profile BALANCED 25000 33000 33000\";\n      power-saver = \"power-profile POWER-SAVER 26000 30000 15000\";\n      power-ultra-saver = \"power-profile ULTRA-POWER-SAVER 10000 20000 10000\";\n    };\n\n    prezto = {\n      enable = true;\n      # prompt.theme = \"pure\";\n\n      # Case insensitive completion\n      caseSensitive = true;\n\n      # Autoconvert .... to ../..\n      editor.dotExpansion = true;\n\n      # Prezto modules to load\n      # pmodules = [ \"utility\" \"editor\" \"directory\" \"completion\"];\n      pmodules = [\n        \"utility\"\n        \"editor\"\n        \"directory\"\n        # \"prompt\"\n      ];\n\n      terminal.autoTitle = true;\n    };\n\n    plugins = [\n      {\n        name = \"zsh-forgit\";\n        src = pkgs.zsh-forgit;\n        file = \"share/zsh/zsh-forgit/forgit.plugin.zsh\";\n      }\n      {\n        name = \"fast-syntax-highlighting\";\n        file = \"fast-syntax-highlighting.plugin.zsh\";\n        src = \"${pkgs.zsh-fast-syntax-highlighting}/share/zsh/site-functions\";\n      }\n      {\n        name = \"zsh-nix-shell\";\n        file = \"nix-shell.plugin.zsh\";\n        src = \"${pkgs.zsh-nix-shell}/share/zsh-nix-shell\";\n      }\n      {\n        name = \"zsh-abbrev-alias\";\n        file = \"abbrev-alias.plugin.zsh\";\n        src = \"${pkgs.zsh-abbrev-alias}/share/zsh-abbrev-alias\";\n      }\n      {\n        name = \"zsh-colored-man-pages\";\n        file = \"colored-man-pages.plugin.zsh\";\n        src = \"${pkgs.zsh-colored-man-pages}/share/zsh-colored-man-pages\";\n      }\n      {\n        name = \"zsh-fzf-tab\";\n        file = \"fzf-tab.plugin.zsh\";\n        src = \"${pkgs.zsh-fzf-tab}/share/fzf-tab\";\n      }\n      {\n        name = \"zsh-async\";\n        file = \"async.zsh\";\n        src = \"${pkgs.zsh-async}/share/zsh-async\";\n      }\n    ];\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/shell/zshrc",
    "content": "# zsh-prompt.zsh - Nushell-style prompt for zsh\n#\n# Set HOST_COLOR before sourcing this file, e.g.:\n#   HOST_COLOR=\"magenta\"\n#   HOST_COLOR=\"#ff79c6\"\n#   source /path/to/zsh-prompt.zsh\n#\n# Supports named colors (red, green, magenta, ...), 256-palette\n# numbers (0-255), and hex colors (#rrggbb).\n\nHOST_COLOR=\"${HOST_COLOR:-default}\"\n\nzmodload zsh/datetime\nsetopt PROMPT_SUBST\n\n# Short hostname locally, user@hostname over SSH\nif [[ -n \"$SSH_CONNECTION\" ]]; then\n  _PROMPT_HOST=\"${USER}@${HOST}\"\nelse\n  _PROMPT_HOST=\"${HOST%%.*}\"\nfi\n\n# Derive HOST_COLOR from hostname hash if not explicitly set.\n# Maps hash bytes into the 20-180 range for readable white text on top.\nif [[ -z \"$HOST_COLOR\" || \"$HOST_COLOR\" == \"default\" ]]; then\n  _hc_hash=$(printf '%s' \"$HOST\" | md5sum)\n  _hc_r=$(( 16#${_hc_hash[1,2]} % 161 + 20 ))\n  _hc_g=$(( 16#${_hc_hash[3,4]} % 161 + 20 ))\n  _hc_b=$(( 16#${_hc_hash[5,6]} % 161 + 20 ))\n  HOST_COLOR=\"$(printf '#%02x%02x%02x' $_hc_r $_hc_g $_hc_b)\"\n  unset _hc_r _hc_g _hc_b _hc_hash\nfi\n\n# Resolve HOST_COLOR into raw ANSI escape sequences so that hex\n# colors (#rrggbb) work alongside named/256 colors.\nif [[ \"$HOST_COLOR\" == \"#\"* ]]; then\n  _hc_hex=\"${HOST_COLOR#\\#}\"\n  _hc_r=$((16#${_hc_hex[1,2]}))\n  _hc_g=$((16#${_hc_hex[3,4]}))\n  _hc_b=$((16#${_hc_hex[5,6]}))\n  _prompt_hc_fg=$'\\e[38;2;'\"${_hc_r};${_hc_g};${_hc_b}\"'m'\n  _prompt_hc_bg=$'\\e[48;2;'\"${_hc_r};${_hc_g};${_hc_b}\"'m'\nelse\n  _prompt_hc_fg=\"$(print -Pn \"%F{${HOST_COLOR}}\")\"\n  _prompt_hc_bg=\"$(print -Pn \"%K{${HOST_COLOR}}\")\"\nfi\nunset _hc_hex _hc_r _hc_g _hc_b\n\n# Vi mode indicator\n_vi_mode_indicator=\" \"\n\nfunction zle-keymap-select {\n  case $KEYMAP in\n    vicmd) _vi_mode_indicator=\"> \" ;;\n    viins|main) _vi_mode_indicator=\" \" ;;\n  esac\n  zle reset-prompt\n}\nzle -N zle-keymap-select\n\nfunction zle-line-init {\n  _vi_mode_indicator=\" \"\n}\nzle -N zle-line-init\n\n# Command duration tracking\n_prompt_cmd_start=0\n\n_prompt_preexec() {\n  _prompt_cmd_start=$EPOCHREALTIME\n}\n\n_prompt_format_duration() {\n  local -i ms=$1\n  if (( ms < 1000 )); then\n    printf '%dms' $ms\n  elif (( ms < 60000 )); then\n    local -i sec=$(( ms / 1000 ))\n    local -i rem=$(( ms % 1000 ))\n    if (( rem > 0 )); then\n      printf '%dsec %dms' $sec $rem\n    else\n      printf '%dsec' $sec\n    fi\n  elif (( ms < 3600000 )); then\n    local -i min=$(( ms / 60000 ))\n    local -i sec=$(( (ms % 60000) / 1000 ))\n    if (( sec > 0 )); then\n      printf '%dmin %dsec' $min $sec\n    else\n      printf '%dmin' $min\n    fi\n  else\n    local -i hr=$(( ms / 3600000 ))\n    local -i min=$(( (ms % 3600000) / 60000 ))\n    if (( min > 0 )); then\n      printf '%dhr %dmin' $hr $min\n    else\n      printf '%dhr' $hr\n    fi\n  fi\n}\n\n# Computed prompt segments (rebuilt each precmd)\n_prompt_segments=\"\"\n\n_prompt_precmd() {\n  local exit_code=$?\n\n  # Duration\n  local duration=\"\"\n  if (( _prompt_cmd_start > 0 )); then\n    local -i elapsed_ms=$(( (EPOCHREALTIME - _prompt_cmd_start) * 1000 ))\n    duration=\"$(_prompt_format_duration $elapsed_ms)\"\n    _prompt_cmd_start=0\n  fi\n\n  # JJ branches (skip on gitbutler/* git branches)\n  local jj_branches=\"\"\n  local git_branch\n  git_branch=$(command git symbolic-ref --short HEAD 2>/dev/null)\n  if [[ \"$git_branch\" != gitbutler/* ]]; then\n    local jj_out\n    jj_out=$(PAGER=cat command jj log -r 'trunk()..@ & bookmarks()' -T 'bookmarks.join(\"\\n\") ++ \"\\n\"' --no-graph --ignore-working-copy 2>/dev/null)\n    if [[ $? -eq 0 && -n \"$jj_out\" ]]; then\n      jj_out=\"${jj_out//$'\\n'/ }\"\n      jj_out=\"${jj_out%% }\"\n      jj_out=\"${jj_out## }\"\n      [[ -n \"$jj_out\" ]] && jj_branches=\"$jj_out\"\n    fi\n  fi\n\n  # Build segments\n  # Path: bold underline, red for root\n  if (( EUID == 0 )); then\n    _prompt_segments=\"%U%B%F{red}%~%f%b%u\"\n  else\n    _prompt_segments=\"%U%B%~%b%u\"\n  fi\n\n  # Nix shell name\n  [[ -n \"$name\" ]] && _prompt_segments+=\" %F{red}${name}%f\"\n\n  # Duration (dark gray italic)\n  [[ -n \"$duration\" ]] && \\\n    _prompt_segments+=$' %F{240}%{\\e[3m%}'\"${duration}\"$'%{\\e[23m%}%f'\n\n  # JJ branches (magenta)\n  [[ -n \"$jj_branches\" ]] && \\\n    _prompt_segments+=\" %F{magenta}${jj_branches}%f\"\n\n  # Last exit code (red bold)\n  (( exit_code != 0 )) && \\\n    _prompt_segments+=\" %F{red}%B×${exit_code}%b%f\"\n\n  # Background jobs (yellow)\n  local njobs=${(Mw)#jobstates}\n  (( njobs > 0 )) && \\\n    _prompt_segments+=\" %F{yellow}[${njobs}]%f\"\n}\n\nprecmd_functions+=(_prompt_precmd)\npreexec_functions+=(_prompt_preexec)\n\nPROMPT=$'\\n''%{${_prompt_hc_fg}%}╭%{${_prompt_hc_bg}%}%F{white}%B ${_PROMPT_HOST} %b%k%f ${_prompt_segments}'$'\\n''%{${_prompt_hc_fg}%}╰▶ %f${_vi_mode_indicator}'\n# Show nix-shell packages in right prompt\nif [[ -n \"$IN_NIX_SHELL\" ]]; then\n  RPROMPT=\"%F{blue}${IN_NIX_SHELL} ${NIX_SHELL_PACKAGES}%f\"\nelse\n  RPROMPT=\"\"\nfi\n"
  },
  {
    "path": "home-manager/modules/shell/zshrc-coffee",
    "content": "function coffee {\n\t# Hide cursor\n\ttput civis\n\n\t# Restore cursor on exit\n\ttrap 'tput cnorm; clear; return 0' EXIT INT TERM\n\n\tlocal frame1='         )  (\n        (   ) )\n         ) ( (\n       _______)_\n    .-'\"'\"'---------|\n   ( C|/\\/\\/\\/\\/|\n    '\"'\"'-./\\/\\/\\/\\/|\n      '\"'\"'_________'\"'\"'\n       '\"'\"'-------'\"'\"'\n\n   pinpox went for coffee.\n   brb.\n'\n\n\tlocal frame2='         (  )\n        )   ( (\n         ( ) )\n       _______(_\n    .-'\"'\"'---------|\n   ( C|/\\/\\/\\/\\/|\n    '\"'\"'-./\\/\\/\\/\\/|\n      '\"'\"'_________'\"'\"'\n       '\"'\"'-------'\"'\"'\n\n   pinpox went for coffee.\n   brb.\n'\n\n\twhile true; do\n\t\tclear\n\t\techo \"$frame1\"\n\t\tsleep 0.5\n\t\tclear\n\t\techo \"$frame2\"\n\t\tsleep 0.5\n\tdone\n}\n"
  },
  {
    "path": "home-manager/modules/shell/zshrc-extra",
    "content": "# Create and change to a directory\ntake () { mkdir -p -- \"$1\" && cd -- \"$1\"; }\n\n# Create and change to a new temporary directory\nttake () { cd $(mktemp -d) }\n\n# Use `line 10 /etc/hosts` to get 10th line of file\nline () { awk \"NR == $1\" \"$2\" }\n\n\n# Bind up and down keys to history matching partial input\nbindkey \"$terminfo[kcuu1]\" history-search-backward\nbindkey \"$terminfo[kcud1]\" history-search-forward\n\n# Init h (https://github.com/zimbatm/h)\neval \"$(h --setup ~/code)\"\n\n# fzf-compete files with ctrl+t\nbindkey -s \"^T\" 'pazi_cd --pipe=\"fzf\"^M'\n\n# Make tab-completion case-insensitive\nzstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'\n\n# fzf-tab: https://github.com/Aloxaf/fzf-tab?tab=readme-ov-file#configure\n\n# set list-colors to enable filename colorizing\nzstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}\n# preview directory's content with eza when completing cd\nzstyle ':fzf-tab:complete:cd:*' fzf-preview 'eza -1 --color=always $realpath'\n\n# Wastebin\n# command | paste\nfunction paste() {\n\tjq -Rns '{text: inputs}' | curl  -s -H 'Content-Type: application/json' \\\n\t\t--data-binary @- https://paste.0cx.de | jq -r '. | \"https://paste.0cx.de\\(.path)\"'\n\t}\n\n# Self-hosted transfer.sh sharing. Expects ~/.netrc with crendentials in this format:\n# machine transfer.0cx.de login my-super-user password super-secret-password\n# transfer file.txt\ntransfer () {\n\tif [ $# -eq 0 ]\n\tthen\n\t\techo \"No arguments specified.\\nUsage:\\n  transfer <file|directory>\\n  ... | transfer <file_name>\" >&2\n\t\treturn 1\n\tfi\n\tif tty -s\n\tthen\n\t\tfile=\"$1\"\n\t\tfile_name=$(basename \"$file\")\n\t\tif [ ! -e \"$file\" ]\n\t\tthen\n\t\t\techo \"$file: No such file or directory\" >&2\n\t\t\treturn 1\n\t\tfi\n\t\tif [ -d \"$file\" ]\n\t\tthen\n\t\t\tfile_name=\"$file_name.zip\" ,\n\t\t\t(\n\t\t\tcd \"$file\" && zip -r -q - .\n\t\t\t) | curl -n --progress-bar --upload-file \"-\" \"https://transfer.0cx.de/$file_name\" | tee /dev/null,\n\t\telse\n\t\t\tcat \"$file\" | curl -n --progress-bar --upload-file \"-\" \"https://transfer.0cx.de/$file_name\" | tee /dev/null\n\t\tfi\n\telse\n\t\tfile_name=$1\n\t\tcurl -n --progress-bar --upload-file \"-\" \"https://transfer.0cx.de/$file_name\" | tee /dev/null\n\tfi\n}\n\nfunction delta_sidebyside {\n  if [[ COLUMNS -ge 140 ]]; then\n    export DELTA_FEATURES='side-by-side'\n  else\n    export DELTA_FEATURES=''\n  fi\n}\ntrap delta_sidebyside WINCH\n\n# Checkout a Gitea PR by number, setting up SSH remote and upstream tracking\ntea-pr() {\n  local pr=\"$1\"\n  [[ -z \"$pr\" ]] && { echo \"Usage: tea-pr <PR_NUMBER>\"; return 1; }\n\n  local head\n  head=$(tea pr view \"$pr\" --output json --fields index,head \\\n    | jq -r \".[] | select(.index==\\\"${pr}\\\") | .head\")\n\n  if [[ -z \"$head\" ]]; then\n    echo \"Error: PR #${pr} not found\"\n    return 1\n  fi\n\n  local owner branch\n  if [[ \"$head\" == *:* ]]; then\n    owner=\"${head%%:*}\"\n    branch=\"${head#*:}\"\n  else\n    owner=\"\"\n    branch=\"$head\"\n  fi\n\n  if [[ -z \"$owner\" ]]; then\n    git fetch origin \"$branch\"\n    git checkout -b \"pr-${pr}\" \"origin/${branch}\"\n  else\n    local remote=\"pulls/${owner}\"\n    git remote add \"$remote\" \"gitea@git.clan.lol:${owner}/clan-core.git\" 2>/dev/null\n    git fetch \"$remote\" \"$branch\"\n    git checkout -b \"pr-${pr}\" \"${remote}/${branch}\"\n    git branch --set-upstream-to=\"${remote}/${branch}\"\n  fi\n  echo \"PR #${pr}: on branch pr-${pr}\"\n}\n\nautoload -z edit-command-line\nzle -N edit-command-line\n\n# Allow jumping between prompts in foot with Ctrl+Shift+x/z\nprecmd() {\n    print -Pn \"\\e]133;A\\e\\\\\"\n}\n"
  },
  {
    "path": "home-manager/modules/ssh/default.nix",
    "content": "{\n  config,\n  lib,\n  ...\n}:\n\nwith lib;\n\nlet\n  cfg = config.pinpox.programs.ssh;\nin\n{\n  options.pinpox.programs.ssh.enable = mkEnableOption \"SSH configuration\";\n\n  config = mkIf cfg.enable {\n    programs.ssh = {\n      enable = true;\n\n      enableDefaultConfig = false;\n\n      matchBlocks = {\n\n        \"*\" = {\n          extraOptions = {\n            ForwardAgent = \"no\";\n            ServerAliveInterval = \"0\";\n            ServerAliveCountMax = \"3\";\n            Compression = \"no\";\n            AddKeysToAgent = \"yes\";\n            HashKnownHosts = \"no\";\n            UserKnownHostsFile = \"~/.ssh/known_hosts\";\n            ControlMaster = \"no\";\n            ControlPath = \"~/.ssh/master-%r@%n:%p\";\n            ControlPersist = \"no\";\n            PKCS11Provider = \"/run/current-system/sw/lib/opensc-pkcs11.so\";\n            # CertificateFile = \"~/.ssh/cert.pub\";\n            CertificateFile = \"${./ssh-key-cert.pub}\";\n          };\n        };\n\n        \"ap-oben\" = {\n          hostname = \"UAP-ProOBEN.lan\";\n          user = \"pinpox\";\n          extraOptions = {\n            PubkeyAcceptedAlgorithms = \"+ssh-rsa\";\n            HostkeyAlgorithms = \"+ssh-rsa\";\n          };\n        };\n        \"ap-mitte\" = {\n          hostname = \"UAP-ProMITTE.lan\";\n          user = \"pinpox\";\n          extraOptions = {\n            PubkeyAcceptedAlgorithms = \"+ssh-rsa\";\n            HostkeyAlgorithms = \"+ssh-rsa\";\n          };\n        };\n        \"ap-unten\" = {\n          hostname = \"UAP-ProUNTEN.lan\";\n          user = \"pinpox\";\n          extraOptions = {\n            PubkeyAcceptedAlgorithms = \"+ssh-rsa\";\n            HostkeyAlgorithms = \"+ssh-rsa\";\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/ssh/ssh-key-cert.pub",
    "content": "ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg+QuBDYto1/9245C9ZBOC3u0xdyrn4jJ8ZDIlhiH06N0AAAAIbmlzdHAyNTYAAABBBCdOfrnazSXp7ZmHcePXSd4leP3Qafr4fmDr3w+AxwRChSn1zzLPjV8CvD/PdMU7jQA0HS/1ItREurmZCKS/ZnQAAAAAAAAAAAAAAAEAAAAOcGlucG94QHBpY29oc20AAAASAAAABnBpbnBveAAAAARyb290AAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIAPcS8NMzwYLvKFOXeTZwX/W6ua0zIzs4zA0PW0xz62iAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDKiqLPktLn4KmCn5moBjxxLmEeAX18MIytub9xwVwqfWfTLXr1COR3UXQ5A1i4rZzaVS9G/G6LnTNLfU/2NJcH ssh-key\n"
  },
  {
    "path": "home-manager/modules/ssh/ssh-key.pub",
    "content": "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCdOfrnazSXp7ZmHcePXSd4leP3Qafr4fmDr3w+AxwRChSn1zzLPjV8CvD/PdMU7jQA0HS/1ItREurmZCKS/ZnQ= ssh-key\n"
  },
  {
    "path": "home-manager/modules/sway/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  wl-harmonograph,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.sway;\n\n  start-sway =\n    pkgs.writeShellScriptBin \"start-sway\" # sh\n      ''\n        export WLR_DRM_NO_MODIFIERS=1\n        # dbus-launch --sh-syntax --exit-with-session ${pkgs.sway}/bin/sway\n        ${pkgs.sway}/bin/sway --unsupported-gpu\n      '';\n\n  # Wrapper that ensures only one noctalia-shell instance is running, so we can\n  # use exec_always and pick up new versions on update\n  start-noctalia = pkgs.writeShellApplication {\n    name = \"start-noctalia\";\n    runtimeInputs = [\n      pkgs.procps\n      pkgs.noctalia-shell\n      pkgs.himalaya\n      pkgs.python3\n    ];\n    text = ''\n      # Kill any running instance (matches the quickshell process loading\n      # noctalia-shell's shell.qml). Ignore failure if none is running.\n      pkill -f 'quickshell.*noctalia-shell' || true\n\n      # Wait for the old process(es) to actually exit so the new instance\n      # can claim the IPC socket cleanly.\n      for _ in $(seq 1 50); do\n        pgrep -f 'quickshell.*noctalia-shell' >/dev/null || break\n        sleep 0.1\n      done\n\n      exec noctalia-shell\n    '';\n  };\nin\n{\n  options.pinpox.programs.sway = {\n    enable = mkEnableOption \"sway window manager\";\n    keyboardVariant = mkOption {\n      type = types.str;\n      default = \"colemak\";\n      description = \"Keyboard variant for sway input (empty string for standard US layout)\";\n    };\n  };\n\n  config = mkIf cfg.enable (\n    let\n      c = config.pinpox.colors;\n\n      # Dark theme color scheme (current)\n      darkColors = ''\n        client.focused #${c.BrightBlue} #${c.Blue} #${c.Black} #${c.BrightBlue} #${c.Blue}\n        client.focused_inactive #${c.BrightWhite} #${c.BrightBlack} #${c.BrightWhite} #${c.BrightBlack} #${c.BrightBlack}\n        client.unfocused #${c.BrightBlack} #${c.Black} #${c.BrightBlack} #${c.BrightBlack} #${c.Black}\n        client.urgent #${c.BrightRed} #${c.Red} #${c.White} #${c.Red} #${c.Red}\n      '';\n\n      # Light theme color scheme (inverted contrast)\n      lightColors = ''\n        client.focused #${c.Blue} #${c.BrightBlue} #${c.Black} #${c.Blue} #${c.BrightBlue}\n        client.focused_inactive #${c.BrightBlack} #${c.White} #${c.Black} #${c.BrightBlack} #${c.BrightBlack}\n        client.unfocused #${c.White} #${c.BrightWhite} #${c.BrightBlack} #${c.White} #${c.BrightWhite}\n        client.urgent #${c.Red} #${c.BrightRed} #${c.Black} #${c.Red} #${c.BrightRed}\n      '';\n\n      # Script to update Sway colors based on theme preference\n      swayThemeSwitcher = pkgs.writeShellScript \"sway-theme-switcher\" ''\n        theme=\"$1\"\n\n        if [[ \"$theme\" == \"prefer-light\" ]]; then\n          ${pkgs.sway}/bin/swaymsg '${lightColors}'\n        else\n          ${pkgs.sway}/bin/swaymsg '${darkColors}'\n        fi\n      '';\n    in\n    {\n      # Animated harmonograph wallpaper service\n      systemd.user.services.wl-harmonograph =\n        let\n          wl-harmonograph-pkg = wl-harmonograph.packages.x86_64-linux.default;\n          fg = builtins.concatStringsSep \",\" [\n            c.Red\n            c.Green\n            c.Blue\n            c.Yellow\n            c.Cyan\n            c.Magenta\n            c.BrightBlue\n          ];\n        in\n        {\n          Unit = {\n            Description = \"Animated harmonograph wallpaper\";\n            PartOf = [ \"sway-session.target\" ];\n            After = [ \"sway-session.target\" \"kanshi.service\" ];\n            Wants = [ \"kanshi.service\" ];\n          };\n          Service = {\n            ExecStart = \"${wl-harmonograph-pkg}/bin/wl-harmonograph\";\n            Environment = [\n              \"HARMONOGRAPH_FG=${fg}\"\n              \"HARMONOGRAPH_BG=${c.Black},${c.BrightBlack}\"\n            ];\n            Restart = \"on-failure\";\n            RestartSec = 2;\n          };\n          Install = {\n            WantedBy = [ \"sway-session.target\" ];\n          };\n        };\n\n      # Enable theme switcher and register Sway's theme script\n      pinpox.services.theme-switcher = {\n        scripts = [ \"${swayThemeSwitcher}\" ];\n      };\n\n      xdg.portal.config.sway = {\n        # Use xdg-desktop-portal-gtk for every portal interface...\n        default = [ \"gtk\" ];\n        # ... except for the ScreenCast, Screenshot and Secret\n        \"org.freedesktop.impl.portal.ScreenCast\" = \"wlr\";\n        \"org.freedesktop.impl.portal.Screenshot\" = \"wlr\";\n        # ignore inhibit bc gtk portal always returns as success,\n        # despite sway/the wlr portal not having an implementation,\n        # stopping firefox from using wayland idle-inhibit\n        \"org.freedesktop.impl.portal.Inhibit\" = \"none\";\n      };\n\n      # laucher\n      programs.tofi = {\n        enable = true;\n        settings = {\n          width = \"100%\";\n          height = \"100%\";\n          border-width = \"0\";\n          outline-width = \"0\";\n          padding-left = \"35%\";\n          padding-top = \"35%\";\n          result-spacing = \"25\";\n          num-results = \"5\";\n          font = config.pinpox.font.normal.family;\n          background-color = \"#${c.Black}A0\";\n          text-color = \"#${c.White}\";\n          selection-color = \"#${c.BrightBlue}\";\n          selection-match-color = \"#${c.Magenta}\";\n          input-color = \"#${c.BrightWhite}\";\n          prompt-text = \"\\\"\\\"\";\n          placeholder-text = \"yes?\";\n          placeholder-color = \"#${c.Yellow}\";\n        };\n      };\n\n      home.packages = with pkgs; [\n        wl-clipboard\n        wlr-randr\n        start-sway\n        font-awesome\n        line-awesome\n      ];\n\n      wayland.windowManager.sway = {\n        enable = true;\n        config = rec {\n\n          seat = {\n            \"*\" = {\n              xcursor_theme = \"${config.gtk.cursorTheme.name} ${toString config.gtk.cursorTheme.size}\";\n            };\n          };\n\n          keybindings = lib.mkOptionDefault {\n\n            # Terminal\n            \"${modifier}+Return\" = \"exec rio\";\n\n            # Launcher\n            \"${modifier}+p\" = ''\n              exec ${lib.getExe pkgs.noctalia-shell} ipc call launcher toggle\n            '';\n\n            # OpenCrow chat panel\n            \"${modifier}+a\" = ''\n              exec ${lib.getExe pkgs.noctalia-shell} ipc call plugin:opencrow-chat toggle\n            '';\n\n            # Url\n            \"${modifier}+Shift+p\" = ''\n              exec ${pkgs.firefox}/bin/firefox --new-window $(cat ~/.local/share/tofi-bookmarks | ${pkgs.tofi}/bin/tofi)\n            '';\n\n            # Toggle microphone mute\n            \"${modifier}+m\" =\n              let\n                mic-toggle =\n                  pkgs.writeShellScriptBin \"mic-toggle\" # sh\n                    ''\n                      source=$(${pkgs.pulseaudio}/bin/pactl get-default-source)\n                      ${pkgs.pulseaudio}/bin/pactl set-source-mute \"$source\" toggle\n                    '';\n              in\n              \"exec ${mic-toggle}/bin/mic-toggle\";\n\n            # Cycle in tabbed with win+tab\n            \"${modifier}+Shift+Tab\" = \"focus prev\";\n            \"${modifier}+Tab\" = \"focus next\";\n\n            # Screen lock\n            \"${modifier}+Shift+l\" = \"exec ${pkgs.swaylock}/bin/swaylock\";\n\n            # Scratchpad\n            \"${modifier}+u\" =\n              \"exec swaymsg '[app_id=\\\"dropdown\\\"] scratchpad show; [app_id=\\\"dropdown\\\"] resize set width 100 ppt height 100 ppt; [app_id=\\\"dropdown\\\"] move container to position 0 px 0 px'\";\n\n            # Screen brightness\n            \"XF86MonBrightnessUp\" = \"exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%+\";\n            \"XF86MonBrightnessDown\" = \"exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-\";\n\n            # Volume key\n            \"XF86AudioMute\" = \"exec ${pkgs.pamixer}/bin/pamixer --toggle-mute\";\n            \"XF86AudioLowerVolume\" = \"exec ${pkgs.pamixer}/bin/pamixer -d 5\";\n            \"XF86AudioRaiseVolume\" = \"exec ${pkgs.pamixer}/bin/pamixer -i 5\";\n\n            # Media keys\n            \"XF86AudioPlay\" = \"exec ${pkgs.playerctl}/bin/playerctl play-pause\";\n            \"XF86AudioNext\" = \"exec ${pkgs.playerctl}/bin/playerctl next\";\n            \"XF86AudioPrev\" = \"exec ${pkgs.playerctl}/bin/playerctl previous\";\n\n            # \"Airplane\" button\n            # \"XF86RFKill\" =\n\n            # \"Gear\" button\n            # \"XF86AudioMedia\" =\n\n            # Screenshots\n            \"Print\" = \"exec screenshot-region\";\n            \"Shift+Print\" = \"exec screenshot-region-file\";\n\n          };\n\n          modifier = \"Mod4\"; # Win key\n          terminal = \"rio\";\n          floating.modifier = \"Mod4\";\n\n          startup = [\n\n            {\n              command =\n                let\n                  init-dropdown = pkgs.writeShellScript \"init-dropdown\" ''\n                    # Start the terminal\n                    ${pkgs.rio}/bin/rio --app-id=dropdown &\n\n                    # Wait for it to be moved to scratchpad by window rules\n                    sleep 0.5\n\n                    # Fix position while it's in scratchpad (this ensures correct position on first show)\n                    ${pkgs.sway}/bin/swaymsg '[app_id=\"dropdown\"] scratchpad show'\n                    ${pkgs.sway}/bin/swaymsg '[app_id=\"dropdown\"] resize set width 100 ppt height 100 ppt' \n                    ${pkgs.sway}/bin/swaymsg '[app_id=\"dropdown\"] move container to position 0 px 0 px'\n                    ${pkgs.sway}/bin/swaymsg '[app_id=\"dropdown\"] move scratchpad'\n                  '';\n                in\n                \"${init-dropdown}\";\n              always = true;\n            }\n            {\n              command = lib.getExe start-noctalia;\n              always = true;\n            }\n          ];\n\n          # Application/window specific rules\n          window.commands = [\n            {\n              command = \"floating enable, border pixel 0\";\n              criteria.class = \"^Audacious$\";\n            }\n            {\n              command = \"floating enable\";\n              criteria.title = \"Firefox — Sharing Indicator\";\n            }\n            {\n              command = \"floating enable, border pixel 0, move scratchpad\";\n              criteria.app_id = \"dropdown\";\n            }\n          ];\n\n          input = {\n            \"*\" = {\n              xkb_layout = \"us\";\n            }\n            // lib.optionalAttrs (cfg.keyboardVariant != \"\") {\n              xkb_variant = cfg.keyboardVariant;\n            };\n          };\n\n          focus.wrapping = \"workspace\";\n\n          colors =\n            let\n              c = config.pinpox.colors;\n            in\n            {\n\n              focused = {\n                background = \"#${c.Blue}\";\n                border = \"#${c.BrightBlue}\";\n                childBorder = \"#${c.Blue}\";\n                indicator = \"#${c.BrightBlue}\";\n                text = \"#${c.Black}\";\n              };\n\n              focusedInactive = {\n                background = \"#${c.BrightWhite}\";\n                border = \"#${c.BrightBlack}\";\n                childBorder = \"#${c.BrightWhite}\";\n                indicator = \"#${c.BrightBlack}\";\n                text = \"#${c.White}\";\n              };\n\n              unfocused = {\n                background = \"#${c.Black}\";\n                border = \"#${c.BrightBlack}\";\n                childBorder = \"#${c.Black}\";\n                indicator = \"#${c.BrightBlack}\";\n                text = \"#${c.BrightBlack}\";\n              };\n\n              urgent = {\n                background = \"#${c.Red}\";\n                border = \"#${c.Black}\";\n                childBorder = \"#${c.Red}\";\n                indicator = \"#${c.Red}\";\n                text = \"#${c.White}\";\n              };\n            };\n\n          bars = [\n          ];\n\n          fonts = {\n            names = [ \"Berkeley Mono\" ];\n            size = 11.0;\n          };\n\n          workspaceAutoBackAndForth = true;\n\n          # Default to tabbed layout\n          workspaceLayout = \"tabbed\";\n\n          gaps = {\n            smartGaps = true;\n            smartBorders = \"on\";\n            bottom = 3;\n            top = 3;\n            horizontal = 3;\n            vertical = 3;\n            inner = 3;\n            left = 3;\n            right = 3;\n            outer = 3;\n          };\n        };\n      };\n    }\n  );\n}\n"
  },
  {
    "path": "home-manager/modules/swaylock/default.nix",
    "content": "{ lib, config, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.programs.swaylock;\nin\n{\n  options.pinpox.programs.swaylock.enable = mkEnableOption \"swaylock screenlocker\";\n\n  config = mkIf cfg.enable {\n\n    programs.swaylock = {\n      enable = true;\n      settings = {\n\n        #  Turn the screen into the given color instead of white.\n        color = \"${config.pinpox.colors.Black}\";\n        # Sets the indicator to show even if idle.\n        indicator-idle-visible = false;\n        # Sets the indicator radius.\n        indicator-radius = 100;\n        # Sets the color of the line between the inside and ring.\n        line-color = \"${config.pinpox.colors.White}\";\n        # Show current count of failed authentication attempts.\n        show-failed-attempts = true;\n        # Sets the color of the ring of the indicator.\n        ring-color = \"${config.pinpox.colors.Green}\";\n        # Sets the color of the text.\n        text-color = \"${config.pinpox.colors.White}\";\n        # Sets the font of the text.\n        font = \"Berkeley Mono\";\n        # Sets a fixed font size for the indicator text.\n        font-size = 24;\n        # Sets the color of the inside of the indicator when invalid.\n        inside-wrong-color = \"${config.pinpox.colors.Red}\";\n        # When an empty password is provided, do not validate it.\n        ignore-empty-password = true;\n        # Sets the color of backspace highlight segments.\n        bs-hl-color = \"${config.pinpox.colors.Magenta}\";\n        # Sets the color of the layout text.\n        layout-text-color = \"${config.pinpox.colors.White}\";\n      };\n    };\n  };\n}\n#  -i, --image [[<output>]:]<path>  Display the given image, optionally only on the given output.\n#  -k, --show-keyboard-layout       Display the current xkb layout while typing.\n#  -K, --hide-keyboard-layout       Hide the current xkb layout while typing.\n#  -L, --disable-caps-lock-text     Disable the Caps Lock text.\n#  -l, --indicator-caps-lock        Show the current Caps Lock state also on the indicator.\n#  -s, --scaling <mode>             Image scaling mode: stretch, fill, fit, center, tile, solid_color.\n#  -t, --tiling                     Same as --scaling=tile.\n#  -u, --no-unlock-indicator        Disable the unlock indicator.\n#  --indicator-thickness <thick>    Sets the indicator thickness.\n#  --indicator-x-position <x>       Sets the horizontal position of the indicator.\n#  --indicator-y-position <y>       Sets the vertical position of the indicator.\n#  --caps-lock-bs-hl-color <color>  Sets the color of backspace highlight segments when Caps Lock is active.\n#  --caps-lock-key-hl-color <color> Sets the color of the key press highlight segments when Caps Lock is active.\n#  --inside-color <color>           Sets the color of the inside of the indicator.\n#  --inside-clear-color <color>     Sets the color of the inside of the indicator when cleared.\n#  --inside-caps-lock-color <color> Sets the color of the inside of the indicator when Caps Lock is active.\n#  --inside-ver-color <color>       Sets the color of the inside of the indicator when verifying.\n#  --key-hl-color <color>           Sets the color of the key press highlight segments.\n#  --layout-bg-color <color>        Sets the background color of the box containing the layout text.\n#  --layout-border-color <color>    Sets the color of the border of the box containing the layout text.\n#  --ring-clear-color <color>       Sets the color of the ring of the indicator when cleared.\n#  --ring-caps-lock-color <color>   Sets the color of the ring of the indicator when Caps Lock is active.\n#  --ring-ver-color <color>         Sets the color of the ring of the indicator when verifying.\n#  --ring-wrong-color <color>       Sets the color of the ring of the indicator when invalid.\n#  --separator-color <color>        Sets the color of the lines that separate highlight segments.\n#  --text-clear-color <color>       Sets the color of the text when cleared.\n#  --text-caps-lock-color <color>   Sets the color of the text when Caps Lock is active.\n#  --text-ver-color <color>         Sets the color of the text when verifying.\n#  --text-wrong-color <color>       Sets the color of the text when invalid.\n"
  },
  {
    "path": "home-manager/modules/swaylock/style.css",
    "content": "* {\n  border: none;\n  border-radius: 0;\n  min-height: 0;\n  font-family: \"Berkeley Mono\";\n  font-size: 15px;\n}\n\nwindow#waybar {\n  /* background-color: rgba(43, 48, 59, 0.65); */\n  background-color: transparent;\n  color: white;\n}\n\n/* window#waybar.hidden { */\n/*     opacity: 0.2; */\n/* } */\n\n#tags button {\n  padding: 0px 5px;\n  margin: 3px 3px;\n  /* background-color: #161320; */\n  background-color: rgba(0, 0, 0, 0.2);\n  color: #d9e0ee;\n  /* Use box-shadow instead of border so the text isn't offset */\n  box-shadow: inset 0 -3px transparent;\n  /* Avoid rounded borders under each workspace name */\n  border: none;\n  border-radius: 3;\n}\n\n/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */\n#tags button:hover {\n  /* background: rgba(0, 0, 0, 0.2); */\n  box-shadow: inset 0 -3px #d9e0ee;\n}\n\n#tags button.occupied {\n  box-shadow: inset 0 -3px white;\n}\n\n#tags button.focused {\n  background-color: black;\n  box-shadow: inset 0 -3px green;\n}\n\n#tags button.urgent {\n  background-color: #f28fad;\n}\n\n#mode {\n  background-color: #64727d;\n  border-bottom: 3px solid #d9e0ee;\n}\n\n#clock,\n#battery,\n#cpu,\n#memory,\n#disk,\n#temperature,\n#backlight,\n#network,\n#pulseaudio,\n#custom-media,\n#tray,\n#mode,\n#idle_inhibitor,\n#mpd,\n#language,\n#idle_inhibitor {\n  padding: 5px 10px;\n  margin: 3px 3px;\n  color: #d9e0ee;\n  border-radius: 3;\n  /* background-color: #161320; */\n  background-color: black;\n}\n\n#window,\n#tags {\n  margin: 0 4px;\n}\n\n/* If workspaces is the leftmost module, omit left margin */\n.modules-left > widget:first-child > #tags {\n  margin-left: 9px;\n}\n\n/* If workspaces is the rightmost module, omit right margin */\n.modules-right > widget:last-child > #tags {\n  margin-right: 0;\n}\n\n#clock {\n  min-width: 45px;\n  margin-right: 11px;\n}\n\n#battery {\n  min-width: 55px;\n}\n\n@keyframes blink {\n  to {\n    background-color: #ffffff;\n    color: #000000;\n  }\n}\n\n#battery.critical:not(.charging) {\n  background-color: #f53c3c;\n  color: #d9e0ee;\n  animation-name: blink;\n  animation-duration: 0.5s;\n  animation-timing-function: linear;\n  animation-iteration-count: infinite;\n  animation-direction: alternate;\n}\n\nlabel:focus {\n  background-color: #d9e0ee;\n}\n\n#backlight {\n  min-width: 55px;\n}\n\n#network {\n  min-width: 150px;\n}\n\n#tray {\n  /* background-color: #161320; */\n  background-color: black;\n}\n\n#tray > .passive {\n  -gtk-icon-effect: dim;\n}\n\n#tray > .needs-attention {\n  -gtk-icon-effect: highlight;\n  background-color: #f28fad;\n}\n"
  },
  {
    "path": "home-manager/modules/taskwarrior/default.nix",
    "content": "{\n  lib,\n  config,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.taskwarrior;\nin\n{\n  options.pinpox.programs.taskwarrior.enable = mkEnableOption \"takswarrior configuration\";\n\n  config = mkIf cfg.enable {\n\n    programs.taskwarrior = {\n      package = pkgs.taskwarrior3;\n\n      # colorTheme\tEither one of the default provided theme as string, or a path to a theme configuration file. \tnull or string or path\n      # config\tKey-value configuration written to {file}`$XDG_CONFIG_HOME/task/taskrc`. \tattribute set of anything\n      enable = true;\n      # extraConfig\tAdditional content written at the end of {file}`$XDG_CONFIG_HOME/task/taskrc`. \tstrings concatenated with \"\\n\"\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/theme-switcher/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n\n  toggle-theme = pkgs.writeShellScriptBin \"toggle-theme\" (builtins.readFile ./toggle-theme.sh);\n\n  cfg = config.pinpox.services.theme-switcher;\n\n  # Build a script that calls all registered theme switch scripts\n  themeWatcherScript = pkgs.writeShellScript \"theme-watcher\" ''\n    update_theme() {\n      local scheme=$(${pkgs.dconf}/bin/dconf read /org/gnome/desktop/interface/color-scheme 2>/dev/null)\n\n      local theme=\"prefer-dark\"\n      if [[ \"$scheme\" == \"'prefer-light'\" ]]; then\n        theme=\"prefer-light\"\n      fi\n\n      ${concatStringsSep \"\\n\" (\n        map (script: ''\n          ${script} \"$theme\"\n        '') cfg.scripts\n      )}\n    }\n\n    # Set initial theme\n    update_theme\n\n    # Monitor dbus for changes to color-scheme\n    ${pkgs.dconf}/bin/dconf watch /org/gnome/desktop/interface/color-scheme | while read -r line; do\n      update_theme\n    done\n  '';\nin\n{\n  options.pinpox.services.theme-switcher = {\n    enable = mkEnableOption \"theme switcher service\";\n\n    scripts = mkOption {\n      type = types.listOf types.str;\n      default = [ ];\n      description = ''\n        List of scripts to call when theme changes. Each script will be called\n        with one argument: either \"prefer-light\" or \"prefer-dark\".\n      '';\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    home.packages = [ toggle-theme ];\n\n    systemd.user.services.theme-watcher = {\n      Unit = {\n        Description = \"Theme watcher - updates applications on dbus theme change\";\n        PartOf = [ \"graphical-session.target\" ];\n        After = [ \"graphical-session.target\" ];\n      };\n\n      Service = {\n        Type = \"simple\";\n        ExecStart = \"${themeWatcherScript}\";\n        Restart = \"on-failure\";\n        RestartSec = 3;\n      };\n\n      Install = {\n        WantedBy = [ \"graphical-session.target\" ];\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/theme-switcher/toggle-theme.sh",
    "content": "#!/usr/bin/env bash\n\n# Read current setting\ncurrent=$(dconf read /org/gnome/desktop/interface/color-scheme 2>/dev/null)\n\nif [[ $current == \"'prefer-dark'\" ]] || [[ $current == \"\" ]]; then\n  dconf write /org/gnome/desktop/interface/color-scheme \"'prefer-light'\"\n  echo \"Switched to light theme\"\nelse\n  dconf write /org/gnome/desktop/interface/color-scheme \"'prefer-dark'\"\n  echo \"Switched to dark theme\"\nfi\n\n# Verify the change\necho \"Current setting:\"\ndbus-send --session --print-reply \\\n  --dest=org.freedesktop.portal.Desktop \\\n  /org/freedesktop/portal/desktop \\\n  org.freedesktop.portal.Settings.Read \\\n  string:'org.freedesktop.appearance' \\\n  string:'color-scheme' 2>/dev/null | grep -A1 variant\n"
  },
  {
    "path": "home-manager/modules/tmux/default.nix",
    "content": "{\n  lib,\n  config,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.tmux;\nin\n{\n  options.pinpox.programs.tmux.enable = mkEnableOption \"tmux terminal mutliplexer\";\n\n  config = mkIf cfg.enable {\n    programs.tmux = {\n      enable = true;\n\n      # Set the prefix key. Overrules the \"shortcut\" option when set.\n      prefix = \"C-a\";\n\n      # Automatically spawn a session if trying to attach and none are running.\n      newSession = true;\n\n      # Base index for windows and panes.\n      baseIndex = 1;\n\n      # Use 24 hour clock.\n      clock24 = true;\n\n      # Maximum number of lines held in window history.\n      historyLimit = 8000;\n\n      # Less command delay\n      escapeTime = 20;\n\n      # Set the $TERM variable.\n      terminal = \"screen-256color\";\n\n      plugins = with pkgs.tmuxPlugins; [ tmux-fzf ];\n\n      extraConfig = builtins.readFile ./tmux.conf;\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/tmux/tmux.conf",
    "content": "# Set the emulator's title\nset -g set-titles on\nset -g set-titles-string \"#W\"\n\n# Forward focus events from emulator\nset -s focus-events on\n\n# Watch for activity\nsetw -g monitor-activity on\nset -g activity-action none\n\n# Copy to real clipboard\nset -g set-clipboard on\n\n\n####\n# Look and feel\n####\n\n# 24 bit colors\nset -sa terminal-overrides ',xterm-256color*:Tc'\n\n# Status bar\nset -g status-style fg=white,bg=black,default\nset -g window-status-current-style fg=red\nset -gw window-status-activity-style bg=colour162,none\n\n# Make inactive panes grey\nsetw -g window-style 'bg=#313547'\nsetw -g window-active-style 'bg=black'\nsetw -g pane-active-border-style \"\"\n\n# Pane border gray\nset -g pane-border-style fg=colour235\nset -g pane-active-border-style fg=yellow\n\n# Pane number display colors\nset -g display-panes-active-colour red\nset -g display-panes-colour blue\nset -g display-panes-time 500\n\n# Enable mouse support\nset -g mouse on\n\n\n####\n# Keys\n####\n# # Clients & Sessions\nbind-key r source-file $HOME/.config/tmux/tmux.conf\\; display \"Reloaded\"\n# # Windows & Panes\n# bind-key Space copy-mode\n# bind-key b break-pane\n# bind-key m join-pane -h\n# bind-key C-m join-pane\n# bind-key o last-pane\n# bind-key i last-window\n# bind-key c new-window -c \"#{pane_current_path}\"\n# bind-key -r C-h resize-pane -L\n# bind-key -r C-j resize-pane -D\n# bind-key -r C-k resize-pane -U\n# bind-key -r C-l resize-pane -R\n# bind-key . command-prompt \"select-pane -t '%%'\"\n\n# Select panes with hjkl instead of arrows\nunbind-key Left\nunbind-key Down\nunbind-key Up\nunbind-key Right\nbind-key h select-pane -L\nbind-key j select-pane -D\nbind-key k select-pane -U\nbind-key l select-pane -R\n\nbind-key - split-window -c \"#{pane_current_path}\"\nbind-key '|' split-window -h -c \"#{pane_current_path}\"\n\n# bind-key s swap-window\n# bind-key C-a send-prefix\n# bind-key a send-prefix\n# bind-key , select-pane -m\n# # Copy-mode\n# bind-key -T copy-mode-vi v send -X begin-selection\n# bind-key -T copy-mode-vi y send -X copy-selection\n#\n# set-option -g prefix C-a\n# # vi keys\n# setw -g mode-keys vi\n# set -g status-keys vi\n#\n\n#\n# Basic theme\n# https://github.com/jimeh/tmux-themepack/blob/master/basic.tmuxtheme\n\n# Right status\n#set -g status-right \"\\\n#  #[fg=colour231,bg=colour04]#{?client_prefix, ^A ,}#[default]\\\n#  #[fg=colour231,bg=colour09]#{?pane_in_mode, Copy ,}#[default]\\\n#  #[fg=colour002]#([ $(tmux show-option -qv key-table) = off ] && echo '(off) ')#[default]\\\n\n\n# https://man7.org/linux/man-pages/man1/tmux.1.html\n# #I: Index of window\n# #P: Index of pane\n# #W: Name of window\n# #F: Window flags with # escaped as ##\n# #S: Session name\nset -gF  display-panes-active-colour \"default\"\nset -gF  display-panes-colour \"default\"\nset -gF  message-command-style \"fg=default,bg=default\"\nset -gF  message-style \"fg=default,bg=default\"\nset -gF  status-interval \"1\"\nset -gF  status-justify \"centre\"\nset -gF  status-left \"#S #[fg=white]» #[fg=yellow]#I #[fg=cyan]#P\"\nset -gF  status-left-length \"40\"\nset -gF  status-left-style \"fg=green,bg=black\"\nset -gF  status-right \"\\\n#{?#(echo $IN_NIX_SHELL), ($IN_NIX_SHELL ❄ $NIX_SHELL_PACKAGES) ,} $USER@#H \\\n#[fg=white]« #[fg=yellow]%H:%M:%S #[fg=green] %Y-%m-%d\"\n# set -gF  status-right-length \"40\"\nset -gF  status-right-style \"fg=cyan,bg=black\"\nset -gF  status-style \"fg=cyan,bg=black\"\nset -gwF clock-mode-colour \"red\"\nset -gwF clock-mode-style \"24\"\nset -gwF mode-style \"fg=default,bg=red\"\nset -gwF pane-active-border-style \"fg=green,bg=default\"\nset -gwF pane-border-style \"fg=default,bg=default\"\nset -gwF window-status-activity-style \"fg=yellow,bg=black\"\n# set -gwF window-status-current-format \"#I:#W\"\nset -gwF window-status-current-style \"fg=black,bg=red\"\n# set -gwF window-status-format \"#I:#W#F\"\nset -gwF window-status-separator \" \"\n"
  },
  {
    "path": "home-manager/modules/waybar/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.waybar;\n\n  # source=$(${pkgs.pulseaudio}/bin/pactl get-default-source)\n  # writeShellApplication {\n  #   name = \"show-nixos-org\";\n  #\n  #   runtimeInputs = [\n  #     curl\n  #     w3m\n  #   ];\n  #\n  #   text = ''\n  #     curl -s 'https://nixos.org' | w3m -dump -T text/html\n  #   '';\n  # }\n\n  mic-exec-if =\n    # Returns 0 if mic is not muted and there are clients listening,\n    # 1 otherwise.\n    pkgs.writeShellScriptBin \"mic-exec-if\" # sh\n      ''\n        source=$(${pkgs.pulseaudio}/bin/pactl get-default-source)\n        mute_state=$(${pkgs.pulseaudio}/bin/pactl get-source-mute \"$source\" | awk '{print $2}')\n\n        if [ \"$mute_state\" = \"yes\" ]; then\n          exit 1\n        fi\n\n        clients=$(${pkgs.pulseaudio}/bin/pactl list source-outputs | grep \"application\\.name\" | uniq | awk -F'\"' '{print $2}' | tr '\\n' ',' | sed 's/,$/\\n/')\n        if [ -n \"$clients\" ]; then\n          exit 0\n        fi\n\n        exit 1\n      '';\n\n  mic-toggle =\n    # Toggle microphone on/off\n    pkgs.writeShellScriptBin \"mic-toggle\" # sh\n      ''\n        source=$(${pkgs.pulseaudio}/bin/pactl get-default-source)\n        ${pkgs.pulseaudio}/bin/pactl set-source-mute \"$source\" toggle\n      '';\n\n  mic-status-apps =\n    # List applications currently accessing the microphone\n    pkgs.writeShellScriptBin \"mic-status-apps\" # sh\n      ''\n        # List all input streams\n        clients=$(${pkgs.pulseaudio}/bin/pactl list source-outputs | grep \"application\\.name\" | uniq | awk -F'\"' '{print $2}' | tr '\\n' ',' | sed 's/,$/\\n/')\n\n        # Remove duplicates and format\n        if [ -n \"$clients\" ]; then\n            unique_clients=$(echo \"$clients\" | sort | uniq | paste -sd, -)\n            echo \" $unique_clients\"\n        fi\n      '';\nin\n{\n  options.pinpox.programs.waybar.enable = mkEnableOption \"waybar configuration\";\n\n  config = mkIf cfg.enable {\n\n    programs.waybar = {\n\n      enable = true;\n\n      style =\n        let\n          c = config.pinpox.colors;\n        in\n        ''\n\n            @define-color Black #${c.Black};\n            @define-color BrightBlack #${c.BrightBlack};\n            @define-color White #${c.White};\n            @define-color BrightWhite #${c.BrightWhite};\n            @define-color Yellow #${c.Yellow};\n            @define-color BrightYellow #${c.BrightYellow};\n            @define-color Green #${c.Green};\n            @define-color BrightGreen #${c.BrightGreen};\n            @define-color Cyan #${c.Cyan};\n            @define-color BrightCyan #${c.BrightCyan};\n            @define-color Blue #${c.Blue};\n            @define-color BrightBlue #${c.BrightBlue};\n            @define-color Magenta #${c.Magenta};\n            @define-color BrightMagenta #${c.BrightMagenta};\n            @define-color Red #${c.Red};\n            @define-color BrightRed #${c.BrightRed};\n\n          ${fileContents ./style.css}\n        '';\n\n      settings.mainbar = {\n        layer = \"top\";\n        position = \"bottom\";\n        # height = 20;\n\n        spacing = 4; # Gaps between modules (4px)\n        modules-left = [\n\n          \"sway/workspaces\"\n          \"sway/mode\"\n\n          # \"river/tags\"\n        ];\n        # modules-center = [\"river/mode\", \"river/window\"],\n        # modules-right = [\"idle_inhibitor\", \"backlight\",  \"cpu\",\"memory\", \"temperature\"],\n\n        modules-center = [\n          \"custom/mic\"\n          \"mpris\"\n        ];\n\n        modules-right = [\n          \"tray\"\n          \"network\"\n          \"pulseaudio\"\n          \"battery\"\n          \"clock\"\n        ];\n\n        mpris = {\n          player-icons = {\n            \"default\" = \"🎵\";\n            \"strawberry\" = \"🍓\";\n          };\n          format = \"{player_icon} {artist} - {title}\";\n          max-length = 40;\n          ignored-players = [\n            \"firefox\"\n            \"chromium\"\n          ];\n        };\n\n        # \"river/tags\" = {\n        #   \"num-tags\" = 9;\n        # };\n\n        idle_inhibitor = {\n          format = \"{icon}\";\n          format-icons = {\n            activated = \" \";\n            deactivated = \" \";\n          };\n        };\n\n        tray = {\n          # \"icon-size\": 21,\n          spacing = 10;\n        };\n        clock = {\n          format = \"{:%Y-%m-%d %H:%M}\";\n          tooltip-format = \"<big>{:%Y %B}</big>\\n<tt><small>{calendar}</small></tt>\";\n        };\n        #    \"cpu\": {\n        #        \"format\": \"{usage}% \",\n        #        \"tooltip\": false\n        #    },\n        #    \"memory\": {\n        #        \"format\": \"{}% \"\n        #    },\n        #    \"temperature\": {\n        #        // \"thermal-zone\": 2,\n        #        // \"hwmon-path\": \"/sys/class/hwmon/hwmon2/temp1_input\",\n        #        \"critical-threshold\": 80,\n        #        // \"format-critical\": \"{temperatureC}°C {icon}\",\n        #        \"format\": \"{temperatureC}°C {icon}\",\n        #        \"format-icons\": [\"\", \"\", \"\"]\n        #    },\n        #    \"backlight\": {\n        #        // \"device\": \"acpi_video1\",\n        #        \"format\": \"{percent}% {icon}\",\n        #        \"format-icons\": [\"\"]\n        #    },\n        battery = {\n          states = {\n            good = 95;\n            warning = 30;\n            critical = 15;\n          };\n          format = \"{capacity}% ▼\";\n          format-charging = \"{capacity}% ▲\";\n          format-plugged = \"{capacity}% ▲\";\n          format-alt = \"{time}\";\n          format-full = \"{capacity}% ▲\";\n        };\n        network = {\n          format-wifi = \"{essid} ({signalStrength}%)\";\n          format-ethernet = \"{ipaddr}/{cidr}\";\n          tooltip-format = \"{ifname} via {gwaddr}\";\n          format-linked = \"{ifname} (No IP)\";\n          format-disconnected = \"Disconnected ✕\";\n          format-alt = \"{ifname}: {ipaddr}/{cidr}\";\n        };\n\n\n        pulseaudio = {\n          scroll-step = 1; # %, can be a float\n          format = \"{volume}% <span color='#${config.pinpox.colors.Green}' rise='2000'>•</span> {format_source}\";\n          format-bluetooth = \"{volume}% <span color='#${config.pinpox.colors.Green}' rise='2000'>•</span> {format_source}\";\n          format-bluetooth-muted = \"✕ <span color='#${config.pinpox.colors.Green}' rise='2000'>•</span> {format_source}\";\n          format-muted = \"<span color='#${config.pinpox.colors.Red}'>{volume}%</span> <span color='#${config.pinpox.colors.Green}' rise='2000'>•</span> {format_source}\";\n          format-source = \"{volume}% <span color='#${config.pinpox.colors.Red}' rise='2000'>•</span>\";\n          format-source-muted = \"<span color='#${config.pinpox.colors.Red}'>{volume}%</span> <span color='#${config.pinpox.colors.Red}' rise='2000'>•</span>\";\n\n          \"on-click\" = \"${pkgs.pulseaudio}/bin/pactl set-sink-mute @DEFAULT_SINK@ toggle\";\n          \"on-click-right\" = \"${mic-toggle}/bin/mic-toggle\";\n        };\n\n        \"custom/mic\" = {\n          \"format\" = \"{}\";\n          \"max-length\" = 40;\n          \"tooltip\" = false;\n          \"interval\" = 1;\n          \"exec\" = \"${mic-status-apps}/bin/mic-status-apps\";\n          \"exec-if\" = \"${mic-exec-if}/bin/mic-exec-if\";\n          \"on-click\" = \"${mic-toggle}/bin/mic-toggle\";\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/waybar/style.css",
    "content": "/***************\n *   GENERAL   *\n ***************/\n\n* {\n  border: none;\n  border-radius: 0;\n  min-height: 0;\n  font-family: \"Berkeley Mono\", \"Font Awesome 6 Free\";\n  font-weight: 400;\n  font-size: 16px;\n}\n\nwindow#waybar {\n  background-color: transparent;\n  color: @White;\n  padding: 4px 0px;\n}\n\n#mode {\n  border-bottom: 3px solid @White;\n}\n\n#clock,\n#battery,\n#cpu,\n#memory,\n#disk,\n#temperature,\n#backlight,\n#network,\n#pulseaudio,\n#custom-media,\n#tray,\n#mode,\n#idle_inhibitor,\n#custom-mic,\n#workspaces button,\n#mpd,\n#language,\n#idle_inhibitor {\n  padding: 2px 8px;\n  margin: 4px 3px;\n  color: @White;\n  border-radius: 0px;\n  background-color: @Black;\n}\n\n#custom-mic,\n#mpris {\n  padding: 2px 8px;\n  margin: 3px 3px;\n  color: @White;\n  background-color: transparent;\n}\n\n#custom-mic {\n  color: @Red;\n}\n\n#window {\n  background-color: @Black;\n  color: @White;\n}\n\n/*********************\n *  WORKSPACES/TAGS  *\n *********************/\n\n#workspaces,\n#tags {\n  margin-left: 7px;\n}\n\n#workspaces button,\n#tags button {\n  background: @Black;\n  color: @White;\n  padding: 0px 0px;\n  margin: 4px 0px;\n}\n\n#workspaces button.focused,\n#tags button.focused {\n  background: @Blue;\n  color: @Black;\n}\n\n#workspaces button.urgent,\n#tags button.urgent {\n  background-color: @Red;\n  color: @Black;\n}\n\n#workspaces button:hover,\n#tags button:hover {\n  background: rgba(0, 0, 0, 0.2);\n  box-shadow: inherit;\n  text-shadow: inherit;\n}\n\n/* /1* If workspaces is the leftmost module, omit left margin *1/ */\n/* .modules-left > widget:first-child > #tags { */\n/* \tmargin-left: 9px; */\n/* } */\n\n/* .modules-right > widget:last-child > #tags { */\n/*\tmargin-right: 0; */\n/* } */\n\n/*********************\n *       CLOCK       *\n *********************/\n\n#clock {\n  min-width: 50px;\n  margin-right: 7px;\n}\n\n/*********************\n *      BATTERY      *\n *********************/\n\n#battery {\n}\n\n#pulseaudio {\n}\n\n@keyframes blink {\n  to {\n    background-color: @White;\n    color: @Black;\n  }\n}\n\n#battery.critical:not(.charging) {\n  background-color: @Red;\n  color: @White;\n  animation-name: blink;\n  animation-duration: 0.5s;\n  animation-timing-function: linear;\n  animation-iteration-count: infinite;\n  animation-direction: alternate;\n}\n\n/* label:focus { */\n/*\tbackground-color: @White; */\n/* } */\n\n/*********************\n *     BACKLIGHT     *\n *********************/\n\n#backlight {\n  min-width: 60px;\n}\n\n/* #network { */\n/*\tmin-width: 150px; */\n/* } */\n\n/*********************\n *       TRAY        *\n *********************/\n\n#tray {\n  background-color: @Black;\n}\n\n#tray > .passive {\n  -gtk-icon-effect: dim;\n}\n\n#tray > .needs-attention {\n  -gtk-icon-effect: highlight;\n  background-color: @Red;\n}\n"
  },
  {
    "path": "home-manager/modules/xdg/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.xdg;\nin\n{\n\n  options.pinpox.defaults.xdg = {\n    enable = mkEnableOption \"xdg defaults\";\n  };\n\n  config = mkIf cfg.enable {\n    xdg = {\n      enable = true;\n      configFile = { };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/zed/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\n\nwith lib;\n\nlet\n  cfg = config.pinpox.programs.zed;\n\nin\n{\n  options.pinpox.programs.zed = {\n    enable = mkEnableOption \"Zed editor configuration\";\n  };\n\n  config = mkIf cfg.enable {\n    services.gnome-keyring.enable = true;\n\n    # Add nixd (Nix language server) for better Nix support\n    home.packages = with pkgs; [\n      nixd\n    ];\n\n    programs.zed-editor = {\n      enable = true;\n      extensions = [ \"nix\" ];\n      userSettings = {\n        telemetry = {\n          metrics = false;\n          diagnostics = false;\n        };\n        vim_mode = true;\n        ui_font_size = 15;\n        buffer_font_size = 15;\n        buffer_font_family = \"Berkeley Mono\";\n        ui_font_family = \"Berkeley Mono\";\n        theme = {\n          mode = \"dark\";\n          light = \"Ayu Light\";\n          dark = \"One Dark\";\n        };\n        language_overrides = {\n          nix = {\n            language_server_id = \"nixd\";\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/zellij/default.nix",
    "content": "{ lib, config, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.programs.zellij;\nin\n{\n  options.pinpox.programs.zellij.enable = mkEnableOption \"zellij terminal mutliplexer\";\n\n  config = mkIf cfg.enable {\n    programs.zellij = {\n\n      enable = true;\n\n      # Don't auto-start zellij on new shells\n      enableZshIntegration = false;\n\n      settings = {\n\n        keybinds.unbind = \"Ctrl q\";\n\n        session_serialization = false;\n        show_startup_tips = false;\n        simplified_ui = true;\n\n        theme = \"custom\";\n        themes.custom = {\n          fg = \"#${config.pinpox.colors.White}\";\n          bg = \"#${config.pinpox.colors.BrightBlack}\";\n          black = \"#${config.pinpox.colors.Black}\";\n          red = \"#${config.pinpox.colors.Red}\";\n          green = \"#${config.pinpox.colors.Green}\";\n          yellow = \"#${config.pinpox.colors.BrightYellow}\";\n          blue = \"#${config.pinpox.colors.Blue}\";\n          magenta = \"#${config.pinpox.colors.Magenta}\";\n          cyan = \"#${config.pinpox.colors.Cyan}\";\n          white = \"#${config.pinpox.colors.White}\";\n          orange = \"#${config.pinpox.colors.Yellow}\";\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/zk/config.toml",
    "content": "# vim: filetype=dosini\n# zk configuration file\n#\n# Uncomment the properties you want to customize.\n\n# NOTE SETTINGS\n#\n# Defines the default options used when generating new notes.\n[note]\n\n# Language used when writing notes.\n# This is used to generate slugs or with date formats.\nlanguage = \"en\"\n\n# The default title used for new note, if no `--title` flag is provided.\ndefault-title = \"Untitled\"\n\n# Template used to generate a note's filename, without extension.\nfilename = \"{{format-date now '%Y-%m-%d-%H%M%S' }}\"\n\n# The file extension used for the notes.\nextension = \"md\"\n\n# Template used to generate a note's content.\n# If not an absolute path, it is relative to .zk/templates/\ntemplate = \"default.md\"\n\n# Configure random ID generation.\n\n# The charset used for random IDs. You can use:\n#   * letters: only letters from a to z.\n#   * numbers: 0 to 9\n#   * alphanum: letters + numbers\n#   * hex: hexadecimal, from a to f and 0 to 9\n#   * custom string: will use any character from the provided value\n#id-charset = \"alphanum\"\n\n# Length of the generated IDs.\n#id-length = 4\n\n# Letter case for the random IDs, among lower, upper or mixed.\n#id-case = \"lower\"\n\n\n# EXTRA VARIABLES\n#\n# A dictionary of variables you can use for any custom values when generating\n# new notes. They are accessible in templates with {{extra.<key>}}\n[extra]\n\n#key = \"value\"\n\n\n# GROUP OVERRIDES\n#\n# You can override global settings from [note] and [extra] for a particular\n# group of notes by declaring a [group.\"<name>\"] section.\n#\n# Specify the list of directories which will automatically belong to the group\n# with the optional `paths` property.\n#\n# Omitting `paths` is equivalent to providing a single path equal to the name of\n# the group. This can be useful to quickly declare a group by the name of the\n# directory it applies to.\n\n#[group.\"<NAME>\"]\n#paths = [\"<DIR1>\", \"<DIR2>\"]\n#[group.\"<NAME>\".note]\n#filename = \"{{date now}}\"\n#[group.\"<NAME>\".extra]\n#key = \"value\"\n\n[group.journal]\npaths = [\"journal\"]\n\n[group.journal.note]\nfilename = \"{{format-date now}}\"\ntemplate = \"journal.md\"\n\n# MARKDOWN SETTINGS\n[format.markdown]\n\n# Format used to generate links between notes.\n# Either \"wiki\", \"markdown\" or a custom template. Default is \"markdown\".\nlink-format = \"markdown\"\n# Indicates whether a link's path will be percent-encoded.\n# Defaults to true for \"markdown\" format and false for \"wiki\" format.\n#link-encode-path = true\n# Indicates whether a link's path file extension will be removed.\n# Defaults to true.\n#link-drop-extension = true\n\n# Enable support for #hashtags.\nhashtags = true\n# Enable support for :colon:separated:tags:.\ncolon-tags = true\n# Enable support for Bear's #multi-word tags#\n# Hashtags must be enabled for multi-word tags to work.\nmultiword-tags = false\n\n\n# EXTERNAL TOOLS\n[tool]\n\n# Default editor used to open notes. When not set, the EDITOR or VISUAL\n# environment variables are used.\neditor = \"nvim\"\n\n# Pager used to scroll through long output. If you want to disable paging\n# altogether, set it to an empty string \"\".\n#pager = \"less -FIRX\"\n\n# Command used to preview a note during interactive fzf mode.\n# Set it to an empty string \"\" to disable preview.\n\n# bat is a great tool to render Markdown document with syntax highlighting.\n#https://github.com/sharkdp/bat\nfzf-preview = \"bat -p --color always {-1}\"\n\n\n# LSP\n#\n#   Configure basic editor integration for LSP-compatible editors.\n#   See https://github.com/mickael-menu/zk/blob/main/docs/editors-integration.md\n#\n[lsp]\n\n[lsp.diagnostics]\n# Each diagnostic can have for value: none, hint, info, warning, error\n\n# Report titles of wiki-links as hints.\nwiki-title = \"hint\"\n# Warn for dead links between notes.\ndead-link = \"error\"\n\n\n# NAMED FILTERS\n#\n#    A named filter is a set of note filtering options used frequently together.\n#\n[filter]\n\n# Matches the notes created the last two weeks. For example:\n#    $ zk list recent --limit 15\n#    $ zk edit recent --interactive\nrecent = \"--sort created- --created-after 'last two weeks'\"\n\n\n# COMMAND ALIASES\n#\n#   Aliases are user commands called with `zk <alias> [<flags>] [<args>]`.\n#\n#   The alias will be executed with `$SHELL -c`, please refer to your shell's\n#   man page to see the available syntax. In most shells:\n#     * $@ can be used to expand all the provided flags and arguments\n#     * you can pipe commands together with the usual | character\n#\n[alias]\n# Here are a few aliases to get you started.\n\n# Shortcut to a command.\nls = \"zk list $@\"\n\n# Default flags for an existing command.\n#list = \"zk list --quiet $@\"\n\n# Edit the last modified note.\nedlast = \"zk edit --limit 1 --sort modified- $@\"\n\n# Edit the notes selected interactively among the notes created the last two weeks.\n# This alias doesn't take any argument, so we don't use $@.\nrecent = \"zk edit --sort created- --created-after 'last two weeks' --interactive\"\n\n# Print paths separated with colons for the notes found with the given\n# arguments. This can be useful to expand a complex search query into a flag\n# taking only paths. For example:\n#   zk list --link-to \"`zk path -m potatoe`\"\npath = \"zk list --quiet --format {{path}} --delimiter , $@\"\n\n# Show a random note.\n#lucky = \"zk list --quiet --format full --sort random --limit 1\"\n\n# Returns the Git history for the notes found with the given arguments.\n# Note the use of a pipe and the location of $@.\n#hist = \"zk list --format path --delimiter0 --quiet $@ | xargs -t -0 git log --patch --\"\n\n# Edit this configuration file.\nconf = '$EDITOR \"~/code/github.com/pinpox/nixos/home-manager/modules/zk/config.toml\"'\n\n# Edit journal\njournal = 'zk new --no-input \"$ZK_NOTEBOOK_DIR/journal\"'\n\n"
  },
  {
    "path": "home-manager/modules/zk/default.md",
    "content": "---\ntitle: {{title}}\ncreated: {{format-date now}}\nvisibility: private\nlanguage: en\ntags:\n  - {{slug title}}\n---\n\n{{content}}\n\n# References\n"
  },
  {
    "path": "home-manager/modules/zk/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.programs.zk;\nin\n{\n  options.pinpox.programs.zk.enable = mkEnableOption \"zk zettelkasten client\";\n\n  config = mkIf cfg.enable {\n    home.packages = with pkgs; [ zk ];\n\n    programs.zsh = {\n\n      sessionVariables.ZK_NOTEBOOK_DIR = \"/home/pinpox/Notes\";\n\n      shellAliases = {\n        # Edit notes\n        zke = \"${pkgs.zk}/bin/zk edit --interactive -x journal\";\n        zkn = \"${pkgs.zk}/bin/zk new inbox\";\n      };\n    };\n\n    xdg = {\n      enable = true;\n      configFile = {\n\n        # zk configuration file\n        zk_config = {\n          target = \"zk/config.toml\";\n          source = ./config.toml;\n        };\n\n        # Template for default notes\n        zk_template_default = {\n          target = \"zk/templates/default.md\";\n          source = ./default.md;\n        };\n\n        # Template for juornal/dairy notes\n        zk_template_journal = {\n          target = \"zk/templates/journal.md\";\n          source = ./journal.md;\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/modules/zk/journal.md",
    "content": "---\ntitle: {{title}}\ncreated: {{format-date now}}\nvisibility: private\nlanguage: de\ntags:\n  - {{slug title}}\n  - journal\n---\n\n# {{format-date now \"long\"}} - {{title}}\n\nWhat did I do today?\n{{content}}\n"
  },
  {
    "path": "home-manager/profiles/common.nix",
    "content": "{\n  lib,\n  pkgs,\n  nur,\n  flake-self,\n  ...\n}:\nwith lib;\n{\n  imports = [ ../colorscheme.nix ];\n\n  config = {\n\n    # Home-manager nixpkgs config\n    nixpkgs = {\n\n      # Allow \"unfree\" licenced packages\n      config = {\n        allowUnfree = true;\n      };\n\n      overlays = [\n        flake-self.overlays.default\n        nur.overlays.default\n      ];\n    };\n\n    # Extra arguments to pass to modules\n    _module.args = {\n      flake-inputs = flake-self.inputs;\n      pinpox-utils = import ../../utils { inherit pkgs; };\n    };\n\n    # Include man-pages\n    manual.manpages.enable = true;\n\n    # Environment variables\n    systemd.user.sessionVariables = {\n      ZDOTDIR = \"/home/pinpox/.config/zsh\";\n    };\n\n    home = {\n      # Install these packages for my user\n      packages = with pkgs; [\n        delta\n        eza\n        htop\n        machine-report\n        nixfmt\n        pkg-config\n        tealdeer\n        unzip\n      ];\n\n      sessionVariables = {\n        ZDOTDIR = \"/home/pinpox/.config/zsh\";\n      };\n\n      # This value determines the Home Manager release that your\n      # configuration is compatible with. This helps avoid breakage\n      # when a new Home Manager release introduces backwards\n      # incompatible changes.\n      #\n      # You can update Home Manager without changing this value. See\n      # the Home Manager release notes for a list of state version\n      # changes in each release.\n      stateVersion = \"25.05\";\n    };\n\n    # Let Home Manager install and manage itself.\n    programs.home-manager.enable = true;\n  };\n}\n"
  },
  {
    "path": "home-manager/profiles/desktop/default.nix",
    "content": "{\n  pkgs,\n  ...\n}:\nlet\n\n  screenshot-region =\n    pkgs.writeShellScriptBin \"screenshot-region\" # sh\n      ''\n        ${pkgs.slurp}/bin/slurp | ${pkgs.grim}/bin/grim -g - - | ${pkgs.wl-clipboard}/bin/wl-copy -t image/png\n      '';\n\n  screenshot-region-file =\n    pkgs.writeShellScriptBin \"screenshot-region-file\" # sh\n      ''\n        ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" $(date +'%s_grim.png')\n      '';\nin\n\n{\n\n  imports = [ ../common.nix ];\n\n  config = {\n\n    home.keyboard = {\n      variant = \"colemak\";\n      layout = \"us\";\n    };\n\n    pinpox = {\n\n      defaults = {\n        xdg.enable = true;\n        calendar.enable = false;\n        shell.enable = true;\n        gtk.enable = true;\n        fonts.enable = true;\n        credentials.enable = true;\n        email.enable = true;\n        git.enable = true;\n      };\n\n      services.theme-switcher.enable = true;\n\n      programs.ssh = {\n        enable = true;\n      };\n\n      programs = {\n        pi.enable = true;\n        games.enable = true;\n        obs-studio.enable = false;\n        pandoc.enable = true;\n        k9s.enable = false;\n        zed.enable = false;\n        helix.enable = false;\n        easyeffects.enable = false;\n        zellij.enable = true;\n        claude-code.enable = true;\n        chromium.enable = true;\n        firefox.enable = true;\n        tmux.enable = true;\n        zk.enable = true;\n        taskwarrior.enable = true;\n        go.enable = true;\n        foot.enable = true;\n        rio.enable = true;\n        sway.enable = true;\n        swaylock.enable = true;\n        # river.enable = true; # TODO: broken in nixpkgs (Zig build failure)\n        waybar.enable = true;\n        kanshi.enable = true;\n      };\n    };\n\n    # Install these packages for my user\n    home.packages = with pkgs; [\n      screenshot-region\n      screenshot-region-file\n      spotify\n      mpv\n      sysz\n      thunderbird-bin\n      deluge\n      (audacious.override { withPlugins = true; })\n      file-roller\n      imagemagick\n      swaynotificationcenter\n      tea\n      but\n      gitbutler\n      chafa\n      asciinema\n      duf\n      evince\n      eza\n      fd\n      gcc\n      # gimp\n      adwaita-icon-theme\n      gtk_engines\n      h # https://github.com/zimbatm/h\n      htop\n      iputils\n      libnotify\n      manix\n      matcha-gtk-theme\n      meld\n      ncdu\n      networkmanagerapplet\n      nextcloud-client\n      nix-index\n      nmap\n      papirus-icon-theme\n      pavucontrol\n      pkg-config\n      playerctl\n      pre-commit\n      signal-desktop\n      sqlite\n      tealdeer\n      unzip\n      viewnior\n      vlc\n      xarchiver\n      xdg-utils\n      # xfce-exo # thunar \"open terminal here\"\n      thunar-archive-plugin\n      thunar-volman\n      tumbler # thunar thumbnails\n      xfce4-volumed-pulse\n      xfconf # thunar save settings\n      # yubioath-desktop\n      # thunar\n      noto-fonts-color-emoji\n      (thunar.override {\n        thunarPlugins = with pkgs; [\n          thunar-volman\n          thunar-archive-plugin\n          thunar-media-tags-plugin\n        ];\n      })\n    ];\n\n    xdg = {\n      enable = true;\n      configFile = {\n        thunar_actions = {\n          target = \"Thunar/uca.xml\";\n          text = ''\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <actions>\n              <action>\n                <icon>utilities-terminal</icon>\n                <name>Open Terminal Here</name>\n                <unique-id>1604472351415438-1</unique-id>\n                <command>foot -D %f</command>\n                <description>Open terminal in current directory</description>\n                <patterns>*</patterns>\n                <startup-notify/>\n                <directories/>\n              </action>\n            </actions>\n          '';\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/profiles/mobile/default.nix",
    "content": "{\n  pkgs,\n  ...\n}:\n{\n\n  imports = [ ../common.nix ];\n\n  config = {\n\n    home.keyboard = {\n      # variant = \"colemak\";\n      layout = \"us\";\n    };\n\n    pinpox = {\n\n      defaults = {\n        xdg.enable = true;\n        calendar.enable = false;\n        shell.enable = true;\n        gtk.enable = true;\n        fonts.enable = false; # Disabled - noto-fonts-color-emoji can't cross-compile\n        credentials.enable = true;\n        git.enable = true;\n      };\n\n      services.theme-switcher.enable = true;\n\n      programs.ssh = {\n        enable = true;\n      };\n\n      programs = {\n        chromium.enable = true;\n        mpv.enable = true;\n        zellij.enable = true;\n        tmux.enable = true;\n        foot.enable = true;\n        rio.enable = true;\n        sway.enable = true;\n        sway.keyboardVariant = \"\"; # Use standard US QWERTY on mobile devices\n        swaylock.enable = true;\n        waybar.enable = true;\n        mako.enable = true;\n      };\n    };\n\n    # Install these packages for my user\n    home.packages = with pkgs; [\n      swaynotificationcenter\n      tea\n\n      chafa\n      duf\n      evince\n      eza\n      fd\n      gcc\n      adwaita-icon-theme\n      gtk_engines\n      htop\n      iputils\n      libnotify\n      ncdu\n      networkmanagerapplet\n      nix-index\n      pavucontrol\n      playerctl\n      sqlite\n      tealdeer\n      unzip\n      xdg-utils\n      thunar-archive-plugin\n      thunar-volman\n      tumbler # thunar thumbnails\n      xfconf # thunar save settings\n      (thunar.override {\n        thunarPlugins = with pkgs; [\n          thunar-volman\n          thunar-archive-plugin\n          thunar-media-tags-plugin\n        ];\n      })\n    ];\n\n    xdg = {\n      enable = true;\n      configFile = {\n        thunar_actions = {\n          target = \"Thunar/uca.xml\";\n          text = ''\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n            <actions>\n              <action>\n                <icon>utilities-terminal</icon>\n                <name>Open Terminal Here</name>\n                <unique-id>1604472351415438-1</unique-id>\n                <command>foot -D %f</command>\n                <description>Open terminal in current directory</description>\n                <patterns>*</patterns>\n                <startup-notify/>\n                <directories/>\n              </action>\n            </actions>\n          '';\n        };\n      };\n    };\n\n    services = {\n\n      # Applets, shown in tray\n\n      # Networking\n      network-manager-applet.enable = true;\n\n      # Bluetooth\n      blueman-applet.enable = true;\n\n      # Pulseaudio\n      pasystray.enable = true;\n\n    };\n  };\n}\n"
  },
  {
    "path": "home-manager/profiles/server/default.nix",
    "content": "{\n  pkgs,\n  ...\n}:\n{\n  imports = [ ../common.nix ];\n\n  config = {\n\n    # Install these packages for my user\n    home.packages = with pkgs; [\n      exa\n      htop\n      httpie\n      pkg-config\n      tealdeer\n      unzip\n    ];\n\n    pinpox = {\n      defaults = {\n        credentials.enable = true;\n        git.enable = true;\n        shell.enable = true;\n        xdg.enable = true;\n      };\n\n      programs = {\n        tmux.enable = true;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "images/configuration.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  modulesPath,\n  ...\n}:\nwith lib;\n{\n\n  imports = [ (modulesPath + \"/profiles/qemu-guest.nix\") ];\n\n  config = {\n\n    # Filesystems\n    fileSystems.\"/\" = {\n      device = \"/dev/disk/by-label/nixos\";\n      fsType = \"ext4\";\n      autoResize = true;\n    };\n\n    # Bootloader\n    boot.growPartition = true;\n    boot.kernelParams = [ \"console=ttyS0\" ];\n    boot.loader.grub.device = \"/dev/vda\";\n    boot.loader.timeout = 0;\n\n    # Locale settings\n    i18n.defaultLocale = \"en_US.UTF-8\";\n    console = {\n      font = \"Lat2-Terminus16\";\n      keyMap = \"colemak\";\n    };\n\n    # TODO set hostname\n    networking.hostName = \"my-nixos-host\";\n\n    # Openssh\n    programs.ssh.startAgent = false;\n    services.openssh = {\n      enable = true;\n      passwordAuthentication = false;\n      startWhenNeeded = true;\n      kbdInteractiveAuthentication = false;\n      permitRootLogin = \"yes\";\n    };\n\n    users = {\n      users.root = {\n        openssh.authorizedKeys.keyFiles = [\n          (pkgs.fetchurl {\n            url = \"https://github.com/pinpox.keys\";\n            sha256 = \"sha256-Cf/PSZemROU/Y0EEnr6A+FXE0M3+Kso5VqJgomGST/U=\";\n          })\n        ];\n      };\n    };\n\n    # Enable flakes\n    nix.package = pkgs.nixVersions.stable;\n\n    # Install some basic utilities\n    environment.systemPackages = [\n      pkgs.git\n      pkgs.ag\n      pkgs.htop\n    ];\n\n    # Let 'nixos-version --json' know about the Git revision\n    # of this flake.\n    # system.configurationRevision = pkgs.lib.mkIf (self ? rev) self.rev;\n  };\n}\n"
  },
  {
    "path": "images/raspi.nix",
    "content": "# # nix build '.#base-image'\n# raspi-image =\n#   let\n#     system = \"aarch64-linux\";\n#   in\n#   import \"${nixpkgs}/nixos/lib/make-disk-image.nix\" {\n#     pkgs = nixpkgs.legacyPackages.\"${system}\";\n#     lib = nixpkgs.lib;\n#     config =\n#       (nixpkgs.lib.nixosSystem {\n#         inherit system;\n#         modules = [ ./images/raspi.nix ];\n#       }).config;\n#     format = \"qcow2\";\n#     diskSize = 4096;\n#     name = \"raspi-image\";\n#   };\n#\n# base-image =\n#   let\n#     system = \"x86_64-linux\";\n#   in\n#   import \"${nixpkgs}/nixos/lib/make-disk-image.nix\" {\n#     pkgs = nixpkgs.legacyPackages.\"${system}\";\n#     lib = nixpkgs.lib;\n#     config =\n#       (nixpkgs.lib.nixosSystem {\n#         inherit system;\n#         modules = [ ./images/configuration.nix ];\n#       }).config;\n#     format = \"qcow2\";\n#     diskSize = 2048;\n#     name = \"base-image\";\n#   };\n#\n\n{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\n\n{\n\n  # Filesystems\n  fileSystems.\"/\" = {\n    device = \"/dev/disk/by-label/nixos\";\n    fsType = \"ext4\";\n    autoResize = true;\n  };\n  # This configuration worked on 09-03-2021 nixos-unstable @ commit 102eb68ceec\n  # The image used https://hydra.nixos.org/build/134720986\n\n  boot = {\n    kernelPackages = pkgs.linuxPackages_rpi4;\n    tmpOnTmpfs = true;\n    initrd.availableKernelModules = [\n      \"usbhid\"\n      \"usb_storage\"\n    ];\n    # ttyAMA0 is the serial console broken out to the GPIO\n    kernelParams = [\n      \"8250.nr_uarts=1\"\n      \"console=ttyAMA0,115200\"\n      \"console=tty1\"\n      # A lot GUI programs need this, nearly all wayland applications\n      \"cma=128M\"\n    ];\n  };\n\n  # Openssh\n  programs.ssh.startAgent = false;\n  services.openssh = {\n    enable = true;\n    passwordAuthentication = false;\n    startWhenNeeded = true;\n    kbdInteractiveAuthentication = false;\n    permitRootLogin = \"yes\";\n  };\n\n  boot.growPartition = true;\n\n  boot.loader.raspberryPi = {\n    enable = true;\n    version = 4;\n  };\n  boot.loader.grub.enable = false;\n\n  # Required for the Wireless firmware\n  hardware.enableRedistributableFirmware = true;\n\n  networking = {\n    hostName = \"nixos-raspi-4\"; # Define your hostname.\n    networkmanager = {\n      enable = true;\n    };\n  };\n\n  # Locale settings\n  i18n.defaultLocale = \"en_US.UTF-8\";\n  console = {\n    font = \"Lat2-Terminus16\";\n    keyMap = \"colemak\";\n  };\n\n  users = {\n    users.root = {\n      openssh.authorizedKeys.keyFiles = [\n        (pkgs.fetchurl {\n          url = \"https://github.com/pinpox.keys\";\n          sha256 = \"sha256-V0ek+L0axLt8v1sdyPXHfZgkbOxqwE3Zw8vOT2aNDcE=\";\n        })\n      ];\n    };\n  };\n\n  nix = {\n    autoOptimiseStore = true;\n    gc = {\n      automatic = true;\n      dates = \"weekly\";\n      options = \"--delete-older-than 30d\";\n    };\n    # Free up to 1GiB whenever there is less than 100MiB left.\n    extraOptions = ''\n      min-free = ${toString (100 * 1024 * 1024)}\n      max-free = ${toString (1024 * 1024 * 1024)}\n    '';\n  };\n  system.stateVersion = \"20.09\";\n}\n"
  },
  {
    "path": "inventory.json",
    "content": "{\n  \"machines\": {\n    \"fichte\": {\n      \"installedAt\": 1760275487\n    },\n    \"clementine\": {\n      \"installedAt\": 1772618310\n    },\n    \"traube\": {\n      \"installedAt\": 1773917352\n    }\n  }\n}"
  },
  {
    "path": "inventory.nix",
    "content": "{ self }:\n{\n  machines = {\n    kiwi.tags = [ \"desktop\" ];\n    tanne.tags = [ \"desktop\" ];\n    fichte.tags = [ \"desktop\" ];\n    kartoffel.tags = [ \"desktop\" ];\n    limette.tags = [ \"desktop\" ];\n    uconsole.tags = [ \"mobile\" ];\n\n    birne.tags = [ \"server\" ];\n    clementine.tags = [ \"server\" ];\n    kfbox.tags = [ \"server\" ];\n    porree.tags = [ \"server\" ];\n    traube.tags = [ \"server\" ];\n  };\n\n  meta.name = \"pinpox-clan\";\n\n  # My clan's top-level domain. All my service shall be accessible from within\n  # the clan at https://<service>.pin\n  meta.domain = \"pin\";\n\n  instances = {\n\n    # A service, which exports a endpoint: \"music\"\n    # The goal is to be able to access https://music.pin from everywhere in the\n    # clan and reach the navidrome server\n    navidrome = {\n      module.input = \"self\";\n      module.name = \"@pinpox/navidrome\";\n      roles.default.machines.kfbox = {\n        settings.host = \"music.0cx.de\";\n        # settings.host = \"music.pin\";\n      };\n    };\n\n    # nostr = {\n    #   module.input = \"clan-community\";\n    #   module.name = \"opencrow\";\n    #   roles.default.machines.kiwi = { };\n    #   roles.llm.machines.kiwi = { };\n    #   roles.nostr-relay.machines.kiwi = { };\n    # };\n\n    # nostr = {\n    #   module.input = \"clan-community\";\n    #   module.name = \"nostr\";\n    #   roles.relay.machines.kfbox = {\n    #     settings.host = \"nostr.0cx.de\";\n    #   };\n    #   roles.groups-relay.machines.kfbox = {\n    #     settings.host = \"groups.0cx.de\";\n    #     settings.relayName = \"0cx.de NIP-29 Groups\";\n    #     settings.relayDescription = \"NIP-29 group chat relay for 0cx.de\";\n    #   };\n    # };\n\n    thelounge = {\n      module.input = \"self\";\n      module.name = \"@pinpox/thelounge\";\n      roles.default.machines.kfbox = { };\n    };\n\n    # Collects all \"endpoint\" exports from all services and generates a file\n    # with CNAME entries.\n    # The dm-dns services has an export of type \"dataMesher\" which signals \"I\n    # want the file 'dns/cnames' to be distributed via data-mesher\".\n    dm-dns = {\n      module.name = \"dm-dns\";\n      roles.push.machines.kiwi = { };\n      roles.default.tags = [ \"all\" ];\n    };\n\n    # Also collects all \"endpoint\" exports from all services and uses them to\n    # set up PKI. Only generators are used, no step-ca or otherwise\n    # centralized service. The architecture is:\n    # - A clan-wide CA is created (shared generater with deploy = false)\n    # - Each host in the clan with the role additionally gets a Host CA, which\n    #   is signed by the Root CA (generator dependand on the root-ca, deployed\n    #   on each host)\n    # - Each endpoint gets a certificate, signed by the Host CA (generator\n    #   dependant on the Host CA)\n    # - All hosts trust the Clan-wide Root CA\n    # With this, every host can just visit the endpoint and is presented with a\n    # certificate that is automatically trusted, because there is a chain of\n    # trust up to the Root CA. If a host adds a new service/endpoint no\n    # re-deployment of other hosts is required.\n    pki = {\n      module.name = \"pki\";\n      roles.default.tags = [ \"all\" ];\n    };\n\n    # Pull-based NixOS deployment via data-mesher. Push machines send a flake\n    # ref, all machines rebuild themselves from it.\n    dm-pull-deploy = {\n      module.input = \"clan-community\";\n      module.name = \"dm-pull-deploy\";\n      roles.push.machines.kiwi.settings.gitUrl = \"https://github.com/pinpox/nixos.git\";\n      roles.default.tags = [ \"all\" ];\n\n      roles.default.machines.tanne.settings.action = \"build\";\n    };\n\n    # The actual data-mesher. It collects all exports of type \"dataMesher\" from\n    # all services and configures itself to distribute the files accordingly.\n    data-mesher = {\n      roles.bootstrap.tags = [ \"server\" ];\n      roles.default.tags = [ \"all\" ];\n      roles.default.settings.interfaces = [ \"ygg\" ];\n    };\n\n    internet = {\n      module.name = \"internet\";\n      roles.default.tags = [ \"server\" ];\n      roles.default.machines = {\n        kfbox.settings.host = \"46.38.242.17\";\n        porree.settings.host = \"94.16.108.229\";\n        clementine.settings.host = \"152.53.139.179\";\n      };\n    };\n\n    tor = {\n      module.name = \"tor\";\n\n      # Add all machines to tor\n      # Add smokeping to test if yggdrasil uses the best way\n\n      roles.client.machines.kiwi = { };\n      roles.client.machines.porree = { };\n\n      roles.server.machines = {\n\n        kiwi.settings = {\n          secretHostname = false;\n          portMapping = [\n            {\n              port = 6443;\n              target.port = 6443;\n            }\n            {\n              port = 6446;\n              target.port = 6446;\n            }\n          ];\n        };\n\n        porree.settings = {\n          secretHostname = false;\n          portMapping = [\n            {\n              port = 6443;\n              target.port = 6443;\n            }\n            {\n              port = 6446;\n              target.port = 6446;\n            }\n          ];\n        };\n      };\n    };\n\n    yggdrasil = {\n      module.name = \"yggdrasil\";\n      roles.default.tags = [ \"all\" ];\n    };\n\n    desktop = {\n      module.input = \"clan-community\";\n      module.name = \"desktop\";\n      roles.sway.tags.desktop = { };\n      roles.kde.machines.fichte = { };\n    };\n\n    user-root = {\n      module.name = \"users\";\n      roles.default.tags.all = { };\n      roles.default.settings = {\n        user = \"root\";\n        share = true;\n        # no identity = no IdP account\n      };\n      roles.default.extraModules = [ ./users/root.nix ];\n    };\n\n    user-pinpox = {\n      module.name = \"users\";\n      roles.default.tags.all = { };\n      roles.default.settings = {\n        user = \"pinpox\";\n        share = true;\n        identity.main = {\n          email = \"mail@pablo.tools\";\n          groups = [\n            \"admins\"\n            \"users\"\n            \"miniflux-users\"\n            \"opencloud-users\"\n            \"paperless-users\"\n          ];\n        };\n      };\n      roles.default.extraModules = [ ./users/pinpox.nix ];\n    };\n\n    # Identity-only user: IdP account but no Unix account\n    user-berber = {\n      module.name = \"users\";\n      roles.default.tags.all = { };\n      roles.default.settings = {\n        user = \"berber\";\n        systemUser = false;\n        identity.main = {\n          groups = [\n            \"users\"\n            \"opencloud-users\"\n            \"miniflux-users\"\n          ];\n        };\n      };\n    };\n\n    user-lislon = {\n      module.name = \"users\";\n      roles.default.machines.fichte = { };\n      roles.default.settings = {\n        user = \"lislon\";\n        share = true;\n        identity.main = { };\n      };\n    };\n\n    localsend = {\n      module.input = \"clan-community\";\n      module.name = \"localsend\";\n      roles.default.tags = [ \"desktop\" ];\n    };\n\n    machine-type = {\n      module.input = \"self\";\n      module.name = \"@pinpox/machine-type\";\n      roles.desktop.tags.desktop = { };\n      roles.server.tags.server = { };\n      roles.mobile.tags.mobile = { };\n    };\n\n    main = {\n      module.input = \"clan-community\";\n      module.name = \"authelia\";\n      roles.default.machines.porree.settings = {\n        publicHost = \"auth.pablo.tools\";\n        domain = \"pablo.tools\";\n\n        # Additional ACL rules (prepended before the auto-generated wildcard)\n        accessControlRules = [\n          {\n            domain = \"paper.pablo.tools\";\n            policy = \"one_factor\";\n            subject = \"group:paperless-users\";\n          }\n        ];\n\n        # Restrict these clients to pinpox only.\n        # Unlisted clients use the default: any authenticated user.\n        clientAccess = {\n          grafana = [ \"user:pinpox\" ];\n          prometheus = [ \"user:pinpox\" ];\n        };\n\n        # OIDC clients for non-clan-service consumers (miniflux, forgejo,\n        # opencloud are still NixOS modules, not clan services, so they\n        # can't export auth.client themselves yet).\n        extraClients = {\n          miniflux = {\n            redirect_uris = [ \"https://news.0cx.de/oauth2/oidc/callback\" ];\n            scopes = [\n              \"openid\"\n              \"profile\"\n              \"email\"\n            ];\n            token_endpoint_auth_method = \"client_secret_basic\";\n          };\n          forgejo = {\n            client_name = \"Forgejo\";\n            authorization_policy = \"two_factor\";\n            require_pkce = true;\n            pkce_challenge_method = \"S256\";\n            redirect_uris = [ \"https://git.pinpox.com/user/oauth2/authelia/callback\" ];\n            scopes = [\n              \"openid\"\n              \"email\"\n              \"profile\"\n              \"groups\"\n            ];\n            response_types = [ \"code\" ];\n            grant_types = [ \"authorization_code\" ];\n            token_endpoint_auth_method = \"client_secret_basic\";\n          };\n          opencloud = {\n            client_name = \"OpenCloud\";\n            public = true;\n            require_pkce = true;\n            pkce_challenge_method = \"S256\";\n            scopes = [\n              \"openid\"\n              \"offline_access\"\n              \"groups\"\n              \"profile\"\n              \"email\"\n            ];\n            redirect_uris = [\n              \"https://cloud.pablo.tools/\"\n              \"https://cloud.pablo.tools/oidc-callback.html\"\n              \"https://cloud.pablo.tools/oidc-silent-redirect.html\"\n            ];\n            response_types = [ \"code\" ];\n            grant_types = [\n              \"authorization_code\"\n              \"refresh_token\"\n            ];\n            token_endpoint_auth_method = \"none\";\n          };\n        };\n      };\n    };\n\n    punchcard1 = {\n      module.input = \"clan-community\";\n      module.name = \"punchcard\";\n      roles.default.machines.clementine.settings = {\n        publicHost = \"punchcard.megaclan3000.de\";\n        environmentFile = \"/run/secrets/punchcard/envfile\";\n      };\n      roles.default.extraModules = [\n        (\n          { pinpox-utils, ... }:\n          {\n            clan.core.vars.generators.\"punchcard\" = pinpox-utils.mkEnvGenerator [\n              \"OIDC_ISSUER_URL\"\n              \"OIDC_CLIENT_ID\"\n              \"OIDC_CLIENT_SECRET\"\n            ];\n          }\n        )\n      ];\n    };\n\n    punchcard2 = {\n      module.input = \"clan-community\";\n      module.name = \"punchcard\";\n      roles.default.machines.clementine.settings = {\n        publicHost = \"punchcard2.megaclan3000.de\";\n        port = 8100;\n        environmentFile = \"/run/secrets/punchcard2/envfile\";\n      };\n      roles.default.extraModules = [\n        (\n          { pinpox-utils, ... }:\n          {\n            clan.core.vars.generators.\"punchcard2\" = pinpox-utils.mkEnvGenerator [\n              \"OIDC_ISSUER_URL\"\n              \"OIDC_CLIENT_ID\"\n              \"OIDC_CLIENT_SECRET\"\n            ];\n          }\n        )\n      ];\n    };\n\n    monitoring = {\n      module.input = \"self\";\n      module.name = \"@pinpox/monitoring\";\n\n      # node-exporter on every host\n      # roles.node-exporter.tags.all = { };\n      #\n      # # Centralized monitoring server lives on porree\n      roles.prometheus.machines.porree.settings = {\n        blackboxTargets = [\n          \"https://pablo.tools\"\n          # \"https://megaclan3000.de\"\n          # \"https://build.lounge.rocks\"\n          # \"https://pass.pablo.tools\" # Vaultwarden\n          # \"https://pinpox.github.io/nixos/\"\n          # \"https://cache.lounge.rocks/nix-cache/nix-cache-info\"\n          # \"https://news.0cx.de\"\n          # \"https://git.0cx.de\" # Gitea\n          # \"https://irc.0cx.de\"\n        ];\n      };\n      # roles.loki.machines.porree = { };\n\n      roles.grafana.machines.porree.settings = {\n        oidc = {\n          enable = true;\n          issuer = \"https://auth.pablo.tools\";\n          clientId = \"grafana\";\n        };\n      };\n\n      # roles.blackbox.machines.porree = { };\n      # roles.alertmanager-irc-relay.machines.porree = { };\n    };\n\n    importer = {\n      module.name = \"importer\";\n      roles.default.tags.all = { };\n      # Import all modules from ./modules/<module-name> on all machines\n      roles.default.extraModules = (\n        map (m: ./modules + \"/${m}\") (\n          builtins.filter (m: m != \"opencrow\") (builtins.attrNames self.nixosModules)\n        )\n      );\n    };\n\n    zerotier = {\n      roles.controller.machines.clementine = { };\n      roles.peer.machines.kiwi = { };\n      # roles.peer.tags.all = { };\n    };\n\n    wg-star = {\n\n      module.input = \"clan-community\";\n      module.name = \"dm-wireguard-star\";\n\n      roles.controller.machines.porree.settings = {\n        endpoint = \"vpn.pablo.tools\";\n        listenPort = 51821;\n      };\n\n      roles.peer.machines.kiwi = { };\n      roles.peer.machines.kfbox = { };\n    };\n\n    wg-clan = {\n\n      module.input = \"clan-community\";\n      module.name = \"wireguard-star\";\n\n      roles.controller.machines.porree.settings = {\n        endpoint = \"vpn.pablo.tools:51820\";\n      };\n\n      roles.peer.machines = {\n        kartoffel = { };\n        birne.settings.allowedIPs = [\n          \"10.100.0.0/24\"\n          \"192.168.101.0/24\"\n        ];\n        kfbox = { };\n        uconsole = { };\n        clementine = { };\n        kiwi = { };\n        limette = { };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/birne/configuration.nix",
    "content": "# Configuration for birne\n{\n  config,\n  lib,\n  ...\n}:\n{\n  imports = [ ./hardware-configuration.nix ];\n\n  # clan.core.networking.targetHost = \"192.168.101.221\";\n\n  # The global useDHCP flag is deprecated, therefore explicitly set to false here.\n  # Per-interface useDHCP will be mandatory in the future, so this generated config\n  # replicates the default behaviour.\n  networking.useDHCP = false;\n  networking.interfaces.eno1.useDHCP = true;\n\n  # Host forwards incoming wg connections to the local network so we can reach LAN devices via wireguard. E.g. for retrieving stats directly from smart-home devices\n  boot.kernel.sysctl.\"net.ipv4.ip_forward\" = 1;\n\n  boot.supportedFilesystems = {\n    btrfs = true;\n    zfs = true;\n  };\n\n  networking.hostName = \"birne\";\n\n  pinpox = {\n\n    services = {\n      unifi-controller.enable = true;\n      minio.enable = true;\n      home-assistant.enable = false;\n    };\n\n    defaults = {\n      lvm-grub.enable = true;\n      environment.enable = true;\n      locale.enable = true;\n      nix.enable = true;\n      storagebox.enable = true;\n    };\n  };\n\n  networking.firewall = {\n    allowedUDPPorts = [ 3478 ];\n    allowedTCPPorts = [\n      80\n      443\n    ];\n  };\n}\n"
  },
  {
    "path": "machines/birne/hardware-configuration.nix",
    "content": "# Do not modify this file!  It was generated by ‘nixos-generate-config’\n# and may be overwritten by future invocations.  Please make changes\n# to /etc/nixos/configuration.nix instead.\n{\n  config,\n  lib,\n  pkgs,\n  modulesPath,\n  ...\n}:\n\n{\n  imports = [ (modulesPath + \"/installer/scan/not-detected.nix\") ];\n\n  boot.initrd.availableKernelModules = [\n    \"ahci\"\n    \"xhci_pci\"\n    \"usb_storage\"\n    \"usbhid\"\n    \"sd_mod\"\n  ];\n\n  boot.initrd.kernelModules = [ \"dm-snapshot\" ];\n  boot.kernelModules = [ \"kvm-intel\" ];\n  boot.extraModulePackages = [ ];\n\n  # ZFS support\n  boot.supportedFilesystems = [ \"zfs\" ];\n\n  # Needed for ZFS\n  # head -c4 /dev/urandom | od -A none -t x4\n  networking.hostId = \"887bde8c\";\n\n  # Efi partition (SSD)\n  fileSystems.\"/boot\" = {\n    device = \"/dev/disk/by-uuid/E45C-8185\";\n    fsType = \"vfat\";\n  };\n\n  # Root drive (SSD)\n  fileSystems.\"/\" = {\n    device = \"/dev/disk/by-uuid/74866c52-5077-44aa-afb2-88ce9e72ab47\";\n    fsType = \"ext4\";\n  };\n\n  # Swap partition\n  swapDevices = [ { device = \"/dev/disk/by-uuid/8551b399-6866-40e0-b8f5-266b5475ffa9\"; } ];\n\n  # Data drive for seafile\n  fileSystems.\"/mnt/data\" = {\n    device = \"/dev/disk/by-uuid/426645bc-dbf6-4c4d-b389-16bbb55d7a14\";\n    fsType = \"ext4\";\n  };\n\n  # Backup drive for restic\n  fileSystems.\"/mnt/backup\" = {\n    device = \"/dev/disk/by-uuid/9961fd1b-3162-474d-9e2e-7cb7d269cd0e\";\n    fsType = \"ext4\";\n  };\n\n  fileSystems.\"/mnt/backup-old\" = {\n    device = \"/dev/disk/by-uuid/a6a101de-0238-4b87-ada2-76653ce51cfc\";\n    fsType = \"ext4\";\n  };\n\n  powerManagement.cpuFreqGovernor = lib.mkDefault \"powersave\";\n}\n"
  },
  {
    "path": "machines/clementine/configuration.nix",
    "content": "{\n  mc3000,\n  trippy-track,\n  pinpox-utils,\n  ...\n}:\n{\n  imports = [ trippy-track.nixosModules.default ];\n\n  clan.core.networking.targetHost = \"152.53.139.179\";\n  networking.hostName = \"clementine\";\n\n  networking.interfaces.ens3 = {\n    ipv6.addresses = [\n      {\n        address = \"2a0a:4cc0:c0:f339::\";\n        prefixLength = 64;\n      }\n    ];\n  };\n\n  pinpox.services.twitch-first.enable = true;\n\n  clan.core.vars.generators.\"trippy-track\" = pinpox-utils.mkEnvGenerator [\n    \"OIDC_ISSUER_URL\"\n    \"OIDC_CLIENT_ID\"\n    \"OIDC_CLIENT_SECRET\"\n    \"OIDC_REDIRECT_URL\"\n  ];\n\n  services.trippy-track = {\n    enable = true;\n    port = 8090;\n    environmentFile = \"/run/secrets/trippy-track/envfile\";\n  };\n\n  services.qemuGuest.enable = true;\n\n  networking.firewall = {\n    enable = true;\n    allowPing = true;\n    allowedTCPPorts = [\n      80\n      443\n      22\n    ];\n  };\n\n  services.caddy = {\n    enable = true;\n    virtualHosts = {\n      \"megaclan3000.de\".extraConfig = ''\n        root * ${mc3000.packages.x86_64-linux.mc3000}\n        file_server\n        encode zstd gzip\n      '';\n      \"travel.pinpox.com\".extraConfig = ''\n        reverse_proxy localhost:8090\n      '';\n    };\n  };\n}\n"
  },
  {
    "path": "machines/clementine/disko.nix",
    "content": "# ---\n# schema = \"single-disk\"\n# [placeholders]\n# mainDisk = \"/dev/disk/by-path/pci-0000:00:10.0\" \n# ---\n# This file was automatically generated!\n# CHANGING this configuration requires wiping and reinstalling the machine\n{\n\n  boot.loader.grub.efiSupport = true;\n  boot.loader.grub.efiInstallAsRemovable = true;\n  boot.loader.grub.enable = true;\n  disko.devices = {\n    disk = {\n      main = {\n        name = \"main-9d6c201ebd034bc0800f78df376f4829\";\n        device = \"/dev/disk/by-path/pci-0000:00:10.0\";\n        type = \"disk\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n            \"boot\" = {\n              size = \"1M\";\n              type = \"EF02\"; # for grub MBR\n              priority = 1;\n            };\n            ESP = {\n              type = \"EF00\";\n              size = \"500M\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"umask=0077\" ];\n              };\n            };\n            root = {\n              size = \"100%\";\n              content = {\n                type = \"filesystem\";\n                format = \"ext4\";\n                mountpoint = \"/\";\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/clementine/facter.json",
    "content": "{\n  \"version\": 1,\n  \"system\": \"x86_64-linux\",\n  \"virtualisation\": \"kvm\",\n  \"hardware\": {\n    \"bios\": {\n      \"apm_info\": {\n        \"supported\": false,\n        \"enabled\": false,\n        \"version\": 0,\n        \"sub_version\": 0,\n        \"bios_flags\": 0\n      },\n      \"vbe_info\": {\n        \"version\": 0,\n        \"video_memory\": 0\n      },\n      \"pnp\": true,\n      \"pnp_id\": 0,\n      \"lba_support\": false,\n      \"low_memory_size\": 654336,\n      \"smbios_version\": 520\n    },\n    \"bridge\": [\n      {\n        \"index\": 9,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"bridge\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 1\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"ISA bridge\",\n          \"value\": 1\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"7000\",\n          \"value\": 28672\n        },\n        \"sub_device\": {\n          \"hex\": \"1100\",\n          \"value\": 4352\n        },\n        \"model\": \"Intel ISA bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:01.0\",\n        \"sysfs_bus_id\": \"0000:00:01.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 259,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 0,\n          \"prog_if\": 0\n        },\n        \"module_alias\": \"pci:v00008086d00007000sv00001AF4sd00001100bc06sc01i00\"\n      },\n      {\n        \"index\": 11,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"bridge\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Host bridge\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"1237\",\n          \"value\": 4663\n        },\n        \"sub_device\": {\n          \"hex\": \"1100\",\n          \"value\": 4352\n        },\n        \"revision\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"model\": \"Intel Host bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:00.0\",\n        \"sysfs_bus_id\": \"0000:00:00.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 259,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 0,\n          \"prog_if\": 0\n        },\n        \"module_alias\": \"pci:v00008086d00001237sv00001AF4sd00001100bc06sc00i00\"\n      },\n      {\n        \"index\": 12,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"bridge\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 1\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"Bridge\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"7113\",\n          \"value\": 28947\n        },\n        \"sub_device\": {\n          \"hex\": \"1100\",\n          \"value\": 4352\n        },\n        \"revision\": {\n          \"hex\": \"0003\",\n          \"value\": 3\n        },\n        \"model\": \"Intel Bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:01.3\",\n        \"sysfs_bus_id\": \"0000:00:01.3\",\n        \"resources\": [\n          {\n            \"type\": \"irq\",\n            \"base\": 9,\n            \"triggered\": 0,\n            \"enabled\": true\n          }\n        ],\n        \"detail\": {\n          \"function\": 3,\n          \"command\": 259,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 9,\n          \"prog_if\": 0\n        },\n        \"driver\": \"piix4_smbus\",\n        \"driver_module\": \"i2c_piix4\",\n        \"drivers\": [\n          \"piix4_smbus\"\n        ],\n        \"driver_modules\": [\n          \"i2c_piix4\"\n        ],\n        \"module_alias\": \"pci:v00008086d00007113sv00001AF4sd00001100bc06sc80i00\"\n      }\n    ],\n    \"cdrom\": [\n      {\n        \"index\": 21,\n        \"attached_to\": 14,\n        \"class_list\": [\n          \"cdrom\",\n          \"scsi\",\n          \"block_device\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0084\",\n          \"name\": \"SCSI\",\n          \"value\": 132\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0106\",\n          \"name\": \"Mass Storage Device\",\n          \"value\": 262\n        },\n        \"sub_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"CD-ROM\",\n          \"value\": 2\n        },\n        \"pci_interface\": {\n          \"hex\": \"0003\",\n          \"name\": \"DVD\",\n          \"value\": 3\n        },\n        \"vendor\": {\n          \"hex\": \"0000\",\n          \"name\": \"QEMU\",\n          \"value\": 0\n        },\n        \"device\": {\n          \"hex\": \"0000\",\n          \"name\": \"QEMU DVD-ROM\",\n          \"value\": 0\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"2.5+\",\n          \"value\": 0\n        },\n        \"model\": \"QEMU DVD-ROM\",\n        \"sysfs_id\": \"/class/block/sr0\",\n        \"sysfs_bus_id\": \"0:0:0:0\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:01.1/ata1/host0/target0:0:0/0:0:0:0\",\n        \"unix_device_name\": \"/dev/sr0\",\n        \"unix_device_number\": {\n          \"type\": 98,\n          \"major\": 11,\n          \"minor\": 0,\n          \"range\": 1\n        },\n        \"unix_device_names\": [\n          \"/dev/cdrom\",\n          \"/dev/disk/by-id/ata-QEMU_DVD-ROM_QM00001\",\n          \"/dev/disk/by-path/pci-0000:00:01.1-ata-1\",\n          \"/dev/disk/by-path/pci-0000:00:01.1-ata-1.0\",\n          \"/dev/sr0\"\n        ],\n        \"unix_device_name2\": \"/dev/sg0\",\n        \"unix_device_number2\": {\n          \"type\": 99,\n          \"major\": 21,\n          \"minor\": 0,\n          \"range\": 1\n        },\n        \"driver\": \"ata_piix\",\n        \"driver_module\": \"ata_piix\",\n        \"drivers\": [\n          \"ata_piix\",\n          \"sr\"\n        ],\n        \"driver_modules\": [\n          \"ata_piix\",\n          \"sr_mod\"\n        ]\n      }\n    ],\n    \"cpu\": [\n      {\n        \"architecture\": \"x86_64\",\n        \"vendor_name\": \"AuthenticAMD\",\n        \"family\": 25,\n        \"model\": 17,\n        \"stepping\": 0,\n        \"features\": [\n          \"fpu\",\n          \"vme\",\n          \"de\",\n          \"pse\",\n          \"tsc\",\n          \"msr\",\n          \"pae\",\n          \"mce\",\n          \"cx8\",\n          \"apic\",\n          \"sep\",\n          \"mtrr\",\n          \"pge\",\n          \"mca\",\n          \"cmov\",\n          \"pat\",\n          \"pse36\",\n          \"clflush\",\n          \"mmx\",\n          \"fxsr\",\n          \"sse\",\n          \"sse2\",\n          \"syscall\",\n          \"nx\",\n          \"mmxext\",\n          \"fxsr_opt\",\n          \"pdpe1gb\",\n          \"rdtscp\",\n          \"lm\",\n          \"rep_good\",\n          \"nopl\",\n          \"xtopology\",\n          \"cpuid\",\n          \"extd_apicid\",\n          \"tsc_known_freq\",\n          \"pni\",\n          \"pclmulqdq\",\n          \"ssse3\",\n          \"fma\",\n          \"cx16\",\n          \"pcid\",\n          \"sse4_1\",\n          \"sse4_2\",\n          \"x2apic\",\n          \"movbe\",\n          \"popcnt\",\n          \"tsc_deadline_timer\",\n          \"aes\",\n          \"xsave\",\n          \"avx\",\n          \"f16c\",\n          \"rdrand\",\n          \"hypervisor\",\n          \"lahf_lm\",\n          \"cmp_legacy\",\n          \"cr8_legacy\",\n          \"abm\",\n          \"sse4a\",\n          \"misalignsse\",\n          \"3dnowprefetch\",\n          \"osvw\",\n          \"topoext\",\n          \"perfctr_core\",\n          \"ssbd\",\n          \"ibrs\",\n          \"ibpb\",\n          \"stibp\",\n          \"ibrs_enhanced\",\n          \"vmmcall\",\n          \"fsgsbase\",\n          \"tsc_adjust\",\n          \"bmi1\",\n          \"avx2\",\n          \"smep\",\n          \"bmi2\",\n          \"erms\",\n          \"invpcid\",\n          \"avx512f\",\n          \"avx512dq\",\n          \"rdseed\",\n          \"adx\",\n          \"smap\",\n          \"avx512ifma\",\n          \"clflushopt\",\n          \"clwb\",\n          \"avx512cd\",\n          \"sha_ni\",\n          \"avx512bw\",\n          \"avx512vl\",\n          \"xsaveopt\",\n          \"xsavec\",\n          \"xgetbv1\",\n          \"xsaves\",\n          \"avx512_bf16\",\n          \"clzero\",\n          \"xsaveerptr\",\n          \"wbnoinvd\",\n          \"arat\",\n          \"avx512vbmi\",\n          \"umip\",\n          \"pku\",\n          \"ospke\",\n          \"avx512_vbmi2\",\n          \"gfni\",\n          \"vaes\",\n          \"vpclmulqdq\",\n          \"avx512_vnni\",\n          \"avx512_bitalg\",\n          \"avx512_vpopcntdq\",\n          \"la57\",\n          \"rdpid\",\n          \"fsrm\",\n          \"flush_l1d\",\n          \"arch_capabilities\"\n        ],\n        \"bugs\": [\n          \"sysret_ss_attrs\",\n          \"spectre_v1\",\n          \"spectre_v2\",\n          \"spec_store_bypass\",\n          \"srso\",\n          \"ibpb_no_ret\"\n        ],\n        \"bogo\": 4493.24,\n        \"cache\": 1024,\n        \"physical_id\": 0,\n        \"siblings\": 1,\n        \"cores\": 1,\n        \"fpu\": true,\n        \"fpu_exception\": true,\n        \"cpuid_level\": 13,\n        \"write_protect\": false,\n        \"tlb_size\": 1024,\n        \"clflush_size\": 64,\n        \"cache_alignment\": 64,\n        \"address_sizes\": {\n          \"physical\": \"0x28\",\n          \"virtual\": \"0x39\"\n        }\n      },\n      {\n        \"architecture\": \"x86_64\",\n        \"vendor_name\": \"AuthenticAMD\",\n        \"family\": 25,\n        \"model\": 17,\n        \"stepping\": 0,\n        \"features\": [\n          \"fpu\",\n          \"vme\",\n          \"de\",\n          \"pse\",\n          \"tsc\",\n          \"msr\",\n          \"pae\",\n          \"mce\",\n          \"cx8\",\n          \"apic\",\n          \"sep\",\n          \"mtrr\",\n          \"pge\",\n          \"mca\",\n          \"cmov\",\n          \"pat\",\n          \"pse36\",\n          \"clflush\",\n          \"mmx\",\n          \"fxsr\",\n          \"sse\",\n          \"sse2\",\n          \"syscall\",\n          \"nx\",\n          \"mmxext\",\n          \"fxsr_opt\",\n          \"pdpe1gb\",\n          \"rdtscp\",\n          \"lm\",\n          \"rep_good\",\n          \"nopl\",\n          \"xtopology\",\n          \"cpuid\",\n          \"extd_apicid\",\n          \"tsc_known_freq\",\n          \"pni\",\n          \"pclmulqdq\",\n          \"ssse3\",\n          \"fma\",\n          \"cx16\",\n          \"pcid\",\n          \"sse4_1\",\n          \"sse4_2\",\n          \"x2apic\",\n          \"movbe\",\n          \"popcnt\",\n          \"tsc_deadline_timer\",\n          \"aes\",\n          \"xsave\",\n          \"avx\",\n          \"f16c\",\n          \"rdrand\",\n          \"hypervisor\",\n          \"lahf_lm\",\n          \"cmp_legacy\",\n          \"cr8_legacy\",\n          \"abm\",\n          \"sse4a\",\n          \"misalignsse\",\n          \"3dnowprefetch\",\n          \"osvw\",\n          \"topoext\",\n          \"perfctr_core\",\n          \"ssbd\",\n          \"ibrs\",\n          \"ibpb\",\n          \"stibp\",\n          \"ibrs_enhanced\",\n          \"vmmcall\",\n          \"fsgsbase\",\n          \"tsc_adjust\",\n          \"bmi1\",\n          \"avx2\",\n          \"smep\",\n          \"bmi2\",\n          \"erms\",\n          \"invpcid\",\n          \"avx512f\",\n          \"avx512dq\",\n          \"rdseed\",\n          \"adx\",\n          \"smap\",\n          \"avx512ifma\",\n          \"clflushopt\",\n          \"clwb\",\n          \"avx512cd\",\n          \"sha_ni\",\n          \"avx512bw\",\n          \"avx512vl\",\n          \"xsaveopt\",\n          \"xsavec\",\n          \"xgetbv1\",\n          \"xsaves\",\n          \"avx512_bf16\",\n          \"clzero\",\n          \"xsaveerptr\",\n          \"wbnoinvd\",\n          \"arat\",\n          \"avx512vbmi\",\n          \"umip\",\n          \"pku\",\n          \"ospke\",\n          \"avx512_vbmi2\",\n          \"gfni\",\n          \"vaes\",\n          \"vpclmulqdq\",\n          \"avx512_vnni\",\n          \"avx512_bitalg\",\n          \"avx512_vpopcntdq\",\n          \"la57\",\n          \"rdpid\",\n          \"fsrm\",\n          \"flush_l1d\",\n          \"arch_capabilities\"\n        ],\n        \"bugs\": [\n          \"sysret_ss_attrs\",\n          \"spectre_v1\",\n          \"spectre_v2\",\n          \"spec_store_bypass\",\n          \"srso\",\n          \"ibpb_no_ret\"\n        ],\n        \"bogo\": 4493.24,\n        \"cache\": 1024,\n        \"physical_id\": 1,\n        \"siblings\": 1,\n        \"cores\": 1,\n        \"fpu\": true,\n        \"fpu_exception\": true,\n        \"cpuid_level\": 13,\n        \"write_protect\": false,\n        \"tlb_size\": 1024,\n        \"clflush_size\": 64,\n        \"cache_alignment\": 64,\n        \"address_sizes\": {\n          \"physical\": \"0x28\",\n          \"virtual\": \"0x39\"\n        }\n      }\n    ],\n    \"disk\": [\n      {\n        \"index\": 22,\n        \"attached_to\": 16,\n        \"class_list\": [\n          \"disk\",\n          \"block_device\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0106\",\n          \"name\": \"Mass Storage Device\",\n          \"value\": 262\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disk\",\n          \"value\": 0\n        },\n        \"model\": \"Disk\",\n        \"sysfs_id\": \"/class/block/vda\",\n        \"sysfs_bus_id\": \"virtio1\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:10.0/virtio1\",\n        \"unix_device_name\": \"/dev/vda\",\n        \"unix_device_number\": {\n          \"type\": 98,\n          \"major\": 253,\n          \"minor\": 0,\n          \"range\": 16\n        },\n        \"unix_device_names\": [\n          \"/dev/disk/by-path/pci-0000:00:10.0\",\n          \"/dev/disk/by-path/virtio-pci-0000:00:10.0\",\n          \"/dev/vda\"\n        ],\n        \"rom_id\": \"0x80\",\n        \"resources\": [\n          {\n            \"type\": \"disk_geo\",\n            \"cylinders\": 266305,\n            \"heads\": 16,\n            \"sectors\": 63,\n            \"size\": \"0x0\",\n            \"geo_type\": \"logical\"\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"sectors\",\n            \"value_1\": 268435456,\n            \"value_2\": 512\n          }\n        ],\n        \"driver\": \"virtio-pci\",\n        \"driver_module\": \"virtio_pci\",\n        \"drivers\": [\n          \"virtio-pci\",\n          \"virtio_blk\"\n        ],\n        \"driver_modules\": [\n          \"virtio_blk\",\n          \"virtio_pci\"\n        ]\n      }\n    ],\n    \"graphics_card\": [\n      {\n        \"index\": 15,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"graphics_card\",\n          \"pci\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 2\n        },\n        \"base_class\": {\n          \"hex\": \"0003\",\n          \"name\": \"Display controller\",\n          \"value\": 3\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"VGA compatible controller\",\n          \"value\": 0\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"VGA\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"1234\",\n          \"value\": 4660\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"1111\",\n          \"value\": 4369\n        },\n        \"sub_device\": {\n          \"hex\": \"1100\",\n          \"value\": 4352\n        },\n        \"revision\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"model\": \"VGA compatible controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:02.0\",\n        \"sysfs_bus_id\": \"0000:00:02.0\",\n        \"resources\": [\n          {\n            \"type\": \"mem\",\n            \"base\": 4261412864,\n            \"range\": 8388608,\n            \"enabled\": true,\n            \"access\": \"read_only\",\n            \"prefetch\": \"no\"\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 4273799168,\n            \"range\": 4096,\n            \"enabled\": true,\n            \"access\": \"read_write\",\n            \"prefetch\": \"no\"\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 786432,\n            \"range\": 131072,\n            \"enabled\": false,\n            \"access\": \"read_write\",\n            \"prefetch\": \"no\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 3,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"bochs-drm\",\n        \"driver_module\": \"bochs\",\n        \"drivers\": [\n          \"bochs-drm\"\n        ],\n        \"driver_modules\": [\n          \"bochs\"\n        ],\n        \"module_alias\": \"pci:v00001234d00001111sv00001AF4sd00001100bc03sc00i00\"\n      }\n    ],\n    \"hub\": [\n      {\n        \"index\": 23,\n        \"attached_to\": 7,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.14.10 uhci_hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0001\",\n          \"name\": \"UHCI Host Controller\",\n          \"value\": 1\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.14\",\n          \"value\": 0\n        },\n        \"serial\": \"0000:00:01.2\",\n        \"model\": \"Linux 6.14.10 uhci_hcd UHCI Host Controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:01.2/usb1/1-0:1.0\",\n        \"sysfs_bus_id\": \"1-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 12000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 0,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0001d0614dc09dsc00dp00ic09isc00ip00in00\"\n      }\n    ],\n    \"memory\": [\n      {\n        \"index\": 5,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"memory\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0101\",\n          \"name\": \"Internally Used Class\",\n          \"value\": 257\n        },\n        \"sub_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Main Memory\",\n          \"value\": 2\n        },\n        \"model\": \"Main Memory\",\n        \"resources\": [\n          {\n            \"type\": \"mem\",\n            \"base\": 0,\n            \"range\": 4107390976,\n            \"enabled\": true,\n            \"access\": \"read_write\",\n            \"prefetch\": \"unknown\"\n          },\n          {\n            \"type\": \"phys_mem\",\n            \"range\": 4026531840\n          }\n        ]\n      }\n    ],\n    \"monitor\": [\n      {\n        \"index\": 20,\n        \"attached_to\": 15,\n        \"class_list\": [\n          \"monitor\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0100\",\n          \"name\": \"Monitor\",\n          \"value\": 256\n        },\n        \"sub_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"LCD Monitor\",\n          \"value\": 2\n        },\n        \"vendor\": {\n          \"hex\": \"4914\",\n          \"value\": 18708\n        },\n        \"device\": {\n          \"hex\": \"1234\",\n          \"name\": \"QEMU Monitor\",\n          \"value\": 4660\n        },\n        \"serial\": \"0\",\n        \"model\": \"QEMU Monitor\",\n        \"resources\": [\n          {\n            \"type\": \"monitor\",\n            \"width\": 1024,\n            \"height\": 768,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"monitor\",\n            \"width\": 1280,\n            \"height\": 800,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"monitor\",\n            \"width\": 1600,\n            \"height\": 1200,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"monitor\",\n            \"width\": 1920,\n            \"height\": 1080,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"monitor\",\n            \"width\": 2048,\n            \"height\": 1152,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"monitor\",\n            \"width\": 640,\n            \"height\": 480,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"monitor\",\n            \"width\": 800,\n            \"height\": 600,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"mm\",\n            \"value_1\": 325,\n            \"value_2\": 203\n          }\n        ],\n        \"detail\": {\n          \"manufacture_year\": 2014,\n          \"manufacture_week\": 42,\n          \"vertical_sync\": {\n            \"min\": 50,\n            \"max\": 125\n          },\n          \"horizontal_sync\": {\n            \"min\": 30,\n            \"max\": 160\n          },\n          \"horizontal_sync_timings\": {\n            \"disp\": 1280,\n            \"sync_start\": 1600,\n            \"sync_end\": 1638,\n            \"total\": 1728\n          },\n          \"vertical_sync_timings\": {\n            \"disp\": 800,\n            \"sync_start\": 804,\n            \"sync_end\": 808,\n            \"total\": 828\n          },\n          \"clock\": 107300,\n          \"width\": 1280,\n          \"height\": 800,\n          \"width_millimetres\": 325,\n          \"height_millimetres\": 203,\n          \"horizontal_flag\": 45,\n          \"vertical_flag\": 45,\n          \"vendor\": \"\",\n          \"name\": \"QEMU Monitor\"\n        },\n        \"driver_info\": {\n          \"type\": \"display\",\n          \"width\": 2048,\n          \"height\": 1152,\n          \"vertical_sync\": {\n            \"min\": 50,\n            \"max\": 125\n          },\n          \"horizontal_sync\": {\n            \"min\": 30,\n            \"max\": 160\n          },\n          \"bandwidth\": 0,\n          \"horizontal_sync_timings\": {\n            \"disp\": 1280,\n            \"sync_start\": 1600,\n            \"sync_end\": 1638,\n            \"total\": 1728\n          },\n          \"vertical_sync_timings\": {\n            \"disp\": 800,\n            \"sync_start\": 804,\n            \"sync_end\": 808,\n            \"total\": 828\n          },\n          \"horizontal_flag\": 45,\n          \"vertical_flag\": 45\n        }\n      }\n    ],\n    \"mouse\": [\n      {\n        \"index\": 24,\n        \"attached_to\": 23,\n        \"class_list\": [\n          \"mouse\",\n          \"usb\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0105\",\n          \"name\": \"Mouse\",\n          \"value\": 261\n        },\n        \"sub_class\": {\n          \"hex\": \"0003\",\n          \"name\": \"USB Mouse\",\n          \"value\": 3\n        },\n        \"vendor\": {\n          \"hex\": \"0627\",\n          \"name\": \"QEMU\",\n          \"value\": 1575\n        },\n        \"device\": {\n          \"hex\": \"0001\",\n          \"name\": \"QEMU USB Tablet\",\n          \"value\": 1\n        },\n        \"serial\": \"28754-0000:00:01.2-1\",\n        \"compat_vendor\": \"Unknown\",\n        \"compat_device\": \"Generic USB Mouse\",\n        \"model\": \"QEMU USB Tablet\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:01.2/usb1/1-1/1-1:1.0\",\n        \"sysfs_bus_id\": \"1-1:1.0\",\n        \"unix_device_name\": \"/dev/input/mice\",\n        \"unix_device_number\": {\n          \"type\": 99,\n          \"major\": 13,\n          \"minor\": 63,\n          \"range\": 1\n        },\n        \"unix_device_names\": [\n          \"/dev/input/mice\"\n        ],\n        \"unix_device_name2\": \"/dev/input/mouse0\",\n        \"unix_device_number2\": {\n          \"type\": 99,\n          \"major\": 13,\n          \"minor\": 32,\n          \"range\": 1\n        },\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 12000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 0,\n          \"interface_class\": {\n            \"hex\": \"0003\",\n            \"name\": \"hid\",\n            \"value\": 3\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"usbhid\",\n        \"driver_module\": \"usbhid\",\n        \"drivers\": [\n          \"usbhid\"\n        ],\n        \"driver_modules\": [\n          \"usbhid\"\n        ],\n        \"driver_info\": {\n          \"type\": \"mouse\",\n          \"db_entry_0\": [\n            \"explorerps/2\",\n            \"exps2\"\n          ],\n          \"xf86\": \"explorerps/2\",\n          \"gpm\": \"exps2\",\n          \"buttons\": -1,\n          \"wheels\": -1\n        },\n        \"module_alias\": \"usb:v0627p0001d0000dc00dsc00dp00ic03isc00ip00in00\"\n      }\n    ],\n    \"network_controller\": [\n      {\n        \"index\": 18,\n        \"attached_to\": 13,\n        \"class_list\": [\n          \"network_controller\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"008f\",\n          \"name\": \"Virtio\",\n          \"value\": 143\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Ethernet controller\",\n          \"value\": 0\n        },\n        \"vendor\": \"Virtio\",\n        \"device\": \"Ethernet Card 0\",\n        \"model\": \"Virtio Ethernet Card 0\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:03.0/virtio0\",\n        \"sysfs_bus_id\": \"virtio0\",\n        \"unix_device_name\": \"ens3\",\n        \"unix_device_names\": [\n          \"ens3\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 50\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 50\n          }\n        ],\n        \"driver\": \"virtio_net\",\n        \"driver_module\": \"virtio_net\",\n        \"drivers\": [\n          \"virtio_net\"\n        ],\n        \"driver_modules\": [\n          \"virtio_net\"\n        ],\n        \"module_alias\": \"virtio:d00000001v00001AF4\"\n      }\n    ],\n    \"network_interface\": [\n      {\n        \"index\": 25,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"network_interface\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Loopback\",\n          \"value\": 0\n        },\n        \"model\": \"Loopback network interface\",\n        \"sysfs_id\": \"/class/net/lo\",\n        \"unix_device_name\": \"lo\",\n        \"unix_device_names\": [\n          \"lo\"\n        ]\n      },\n      {\n        \"index\": 26,\n        \"attached_to\": 18,\n        \"class_list\": [\n          \"network_interface\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Ethernet\",\n          \"value\": 1\n        },\n        \"model\": \"Ethernet network interface\",\n        \"sysfs_id\": \"/class/net/ens3\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:03.0/virtio0\",\n        \"unix_device_name\": \"ens3\",\n        \"unix_device_names\": [\n          \"ens3\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 50\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 50\n          }\n        ],\n        \"driver\": \"virtio_net\",\n        \"driver_module\": \"virtio_net\",\n        \"drivers\": [\n          \"virtio_net\"\n        ],\n        \"driver_modules\": [\n          \"virtio_net\"\n        ]\n      }\n    ],\n    \"pci\": [\n      {\n        \"index\": 6,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"unknown\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 28\n        },\n        \"base_class\": {\n          \"hex\": \"0007\",\n          \"name\": \"Communication controller\",\n          \"value\": 7\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"Communication controller\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"1003\",\n          \"value\": 4099\n        },\n        \"sub_device\": {\n          \"hex\": \"0003\",\n          \"value\": 3\n        },\n        \"model\": \"Communication controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1c.0\",\n        \"sysfs_bus_id\": \"0000:00:1c.0\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 49344,\n            \"range\": 64,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"irq\",\n            \"base\": 11,\n            \"triggered\": 0,\n            \"enabled\": true\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 4273811456,\n            \"range\": 4096,\n            \"enabled\": true,\n            \"access\": \"read_write\",\n            \"prefetch\": \"no\"\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 824633753600,\n            \"range\": 16384,\n            \"enabled\": true,\n            \"access\": \"read_only\",\n            \"prefetch\": \"no\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1287,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 11,\n          \"prog_if\": 0\n        },\n        \"driver\": \"virtio-pci\",\n        \"driver_module\": \"virtio_pci\",\n        \"drivers\": [\n          \"virtio-pci\"\n        ],\n        \"driver_modules\": [\n          \"virtio_pci\"\n        ],\n        \"module_alias\": \"pci:v00001AF4d00001003sv00001AF4sd00000003bc07sc80i00\"\n      },\n      {\n        \"index\": 8,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"unknown\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 16\n        },\n        \"base_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Mass storage controller\",\n          \"value\": 1\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"SCSI storage controller\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"1001\",\n          \"value\": 4097\n        },\n        \"sub_device\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"model\": \"SCSI storage controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:10.0\",\n        \"sysfs_bus_id\": \"0000:00:10.0\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 49152,\n            \"range\": 128,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"irq\",\n            \"base\": 11,\n            \"triggered\": 0,\n            \"enabled\": true\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 4273807360,\n            \"range\": 4096,\n            \"enabled\": true,\n            \"access\": \"read_write\",\n            \"prefetch\": \"no\"\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 824633737216,\n            \"range\": 16384,\n            \"enabled\": true,\n            \"access\": \"read_only\",\n            \"prefetch\": \"no\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1287,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 11,\n          \"prog_if\": 0\n        },\n        \"driver\": \"virtio-pci\",\n        \"driver_module\": \"virtio_pci\",\n        \"drivers\": [\n          \"virtio-pci\"\n        ],\n        \"driver_modules\": [\n          \"virtio_pci\"\n        ],\n        \"module_alias\": \"pci:v00001AF4d00001001sv00001AF4sd00000002bc01sc00i00\"\n      },\n      {\n        \"index\": 10,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"unknown\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 30\n        },\n        \"base_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"sub_class\": {\n          \"hex\": \"00ff\",\n          \"value\": 255\n        },\n        \"vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"1002\",\n          \"value\": 4098\n        },\n        \"sub_device\": {\n          \"hex\": \"0005\",\n          \"value\": 5\n        },\n        \"model\": \"Unclassified device\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1e.0\",\n        \"sysfs_bus_id\": \"0000:00:1e.0\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 49408,\n            \"range\": 64,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"irq\",\n            \"base\": 10,\n            \"triggered\": 0,\n            \"enabled\": true\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 824633769984,\n            \"range\": 16384,\n            \"enabled\": true,\n            \"access\": \"read_only\",\n            \"prefetch\": \"no\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 263,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 10,\n          \"prog_if\": 0\n        },\n        \"driver\": \"virtio-pci\",\n        \"driver_module\": \"virtio_pci\",\n        \"drivers\": [\n          \"virtio-pci\"\n        ],\n        \"driver_modules\": [\n          \"virtio_pci\"\n        ],\n        \"module_alias\": \"pci:v00001AF4d00001002sv00001AF4sd00000005bc00scFFi00\"\n      },\n      {\n        \"index\": 13,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"unknown\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 3\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Ethernet controller\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"1000\",\n          \"value\": 4096\n        },\n        \"sub_device\": {\n          \"hex\": \"0001\",\n          \"value\": 1\n        },\n        \"model\": \"Ethernet controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:03.0\",\n        \"sysfs_bus_id\": \"0000:00:03.0\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 49280,\n            \"range\": 64,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"irq\",\n            \"base\": 10,\n            \"triggered\": 0,\n            \"enabled\": true\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 4273471488,\n            \"range\": 262144,\n            \"enabled\": false,\n            \"access\": \"read_only\",\n            \"prefetch\": \"no\"\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 4273803264,\n            \"range\": 4096,\n            \"enabled\": true,\n            \"access\": \"read_write\",\n            \"prefetch\": \"no\"\n          },\n          {\n            \"type\": \"mem\",\n            \"base\": 824633720832,\n            \"range\": 16384,\n            \"enabled\": true,\n            \"access\": \"read_only\",\n            \"prefetch\": \"no\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1287,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 10,\n          \"prog_if\": 0\n        },\n        \"driver\": \"virtio-pci\",\n        \"driver_module\": \"virtio_pci\",\n        \"drivers\": [\n          \"virtio-pci\"\n        ],\n        \"driver_modules\": [\n          \"virtio_pci\"\n        ],\n        \"module_alias\": \"pci:v00001AF4d00001000sv00001AF4sd00000001bc02sc00i00\"\n      }\n    ],\n    \"storage_controller\": [\n      {\n        \"index\": 14,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"storage_controller\",\n          \"pci\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 1\n        },\n        \"base_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Mass storage controller\",\n          \"value\": 1\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"IDE interface\",\n          \"value\": 1\n        },\n        \"pci_interface\": {\n          \"hex\": \"0080\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"7010\",\n          \"value\": 28688\n        },\n        \"sub_device\": {\n          \"hex\": \"1100\",\n          \"value\": 4352\n        },\n        \"model\": \"Intel IDE interface\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:01.1\",\n        \"sysfs_bus_id\": \"0000:00:01.1\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 1014,\n            \"range\": 1,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"io\",\n            \"base\": 368,\n            \"range\": 8,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"io\",\n            \"base\": 49504,\n            \"range\": 16,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"io\",\n            \"base\": 496,\n            \"range\": 8,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"io\",\n            \"base\": 886,\n            \"range\": 1,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 1,\n          \"command\": 263,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 0,\n          \"prog_if\": 128\n        },\n        \"driver\": \"ata_piix\",\n        \"driver_module\": \"ata_piix\",\n        \"drivers\": [\n          \"ata_piix\"\n        ],\n        \"driver_modules\": [\n          \"ata_piix\"\n        ],\n        \"module_alias\": \"pci:v00008086d00007010sv00001AF4sd00001100bc01sc01i80\"\n      },\n      {\n        \"index\": 16,\n        \"attached_to\": 8,\n        \"class_list\": [\n          \"storage_controller\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"008f\",\n          \"name\": \"Virtio\",\n          \"value\": 143\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Mass storage controller\",\n          \"value\": 1\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"Storage controller\",\n          \"value\": 128\n        },\n        \"vendor\": \"Virtio\",\n        \"device\": \"Storage 0\",\n        \"model\": \"Virtio Storage 0\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:10.0/virtio1\",\n        \"sysfs_bus_id\": \"virtio1\",\n        \"driver\": \"virtio_blk\",\n        \"driver_module\": \"virtio_blk\",\n        \"drivers\": [\n          \"virtio_blk\"\n        ],\n        \"driver_modules\": [\n          \"virtio_blk\"\n        ],\n        \"module_alias\": \"virtio:d00000002v00001AF4\"\n      }\n    ],\n    \"system\": {\n      \"form_factor\": \"desktop\"\n    },\n    \"unknown\": [\n      {\n        \"index\": 17,\n        \"attached_to\": 6,\n        \"class_list\": [\n          \"unknown\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"vendor\": \"Virtio\",\n        \"device\": \"\",\n        \"model\": \"Virtio Unclassified device\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1c.0/virtio2\",\n        \"sysfs_bus_id\": \"virtio2\",\n        \"driver\": \"virtio_console\",\n        \"driver_module\": \"virtio_console\",\n        \"drivers\": [\n          \"virtio_console\"\n        ],\n        \"driver_modules\": [\n          \"virtio_console\"\n        ],\n        \"module_alias\": \"virtio:d00000003v00001AF4\"\n      },\n      {\n        \"index\": 19,\n        \"attached_to\": 10,\n        \"class_list\": [\n          \"unknown\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"vendor\": \"Virtio\",\n        \"device\": \"\",\n        \"model\": \"Virtio Unclassified device\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1e.0/virtio3\",\n        \"sysfs_bus_id\": \"virtio3\",\n        \"driver\": \"virtio_balloon\",\n        \"driver_module\": \"virtio_balloon\",\n        \"drivers\": [\n          \"virtio_balloon\"\n        ],\n        \"driver_modules\": [\n          \"virtio_balloon\"\n        ],\n        \"module_alias\": \"virtio:d00000005v00001AF4\"\n      }\n    ],\n    \"usb_controller\": [\n      {\n        \"index\": 7,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb_controller\",\n          \"pci\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 1\n        },\n        \"base_class\": {\n          \"hex\": \"000c\",\n          \"name\": \"Serial bus controller\",\n          \"value\": 12\n        },\n        \"sub_class\": {\n          \"hex\": \"0003\",\n          \"name\": \"USB Controller\",\n          \"value\": 3\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"UHCI\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1af4\",\n          \"value\": 6900\n        },\n        \"device\": {\n          \"hex\": \"7020\",\n          \"value\": 28704\n        },\n        \"sub_device\": {\n          \"hex\": \"1100\",\n          \"value\": 4352\n        },\n        \"revision\": {\n          \"hex\": \"0001\",\n          \"value\": 1\n        },\n        \"model\": \"Intel USB Controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:01.2\",\n        \"sysfs_bus_id\": \"0000:00:01.2\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 49472,\n            \"range\": 32,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          },\n          {\n            \"type\": \"irq\",\n            \"base\": 11,\n            \"triggered\": 0,\n            \"enabled\": true\n          }\n        ],\n        \"detail\": {\n          \"function\": 2,\n          \"command\": 263,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"irq\": 11,\n          \"prog_if\": 0\n        },\n        \"driver\": \"uhci_hcd\",\n        \"driver_module\": \"uhci_hcd\",\n        \"drivers\": [\n          \"uhci_hcd\"\n        ],\n        \"driver_modules\": [\n          \"uhci_hcd\"\n        ],\n        \"driver_info\": {\n          \"type\": \"module\",\n          \"db_entry_0\": [\n            \"uhci-hcd\"\n          ],\n          \"active\": true,\n          \"modprobe\": true,\n          \"names\": [\n            \"uhci-hcd\"\n          ],\n          \"module_args\": [\n            \"\"\n          ],\n          \"conf\": \"\"\n        },\n        \"module_alias\": \"pci:v00008086d00007020sv00001AF4sd00001100bc0Csc03i00\"\n      }\n    ]\n  },\n  \"smbios\": {\n    \"bios\": {\n      \"handle\": 0,\n      \"vendor\": \"netcup\",\n      \"version\": \"VPS 500 G12\",\n      \"date\": \"03/04/2026\",\n      \"features\": null,\n      \"start_address\": \"0xe8000\",\n      \"rom_size\": 65536\n    },\n    \"chassis\": [\n      {\n        \"handle\": 768,\n        \"manufacturer\": \"QEMU\",\n        \"version\": \"pc-i440fx-9.2\",\n        \"chassis_type\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"lock_present\": false,\n        \"bootup_state\": {\n          \"hex\": \"0003\",\n          \"name\": \"Safe\",\n          \"value\": 3\n        },\n        \"power_state\": {\n          \"hex\": \"0003\",\n          \"name\": \"Safe\",\n          \"value\": 3\n        },\n        \"thermal_state\": {\n          \"hex\": \"0003\",\n          \"name\": \"Safe\",\n          \"value\": 3\n        },\n        \"security_state\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"oem\": \"0x0\"\n      }\n    ],\n    \"memory_array\": [\n      {\n        \"handle\": 4096,\n        \"location\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"usage\": {\n          \"hex\": \"0003\",\n          \"name\": \"System memory\",\n          \"value\": 3\n        },\n        \"ecc\": {\n          \"hex\": \"0006\",\n          \"name\": \"Multi-bit\",\n          \"value\": 6\n        },\n        \"max_size\": \"0x400000\",\n        \"error_handle\": 65534,\n        \"slots\": 1\n      }\n    ],\n    \"memory_array_mapped_address\": [\n      {\n        \"handle\": 4864,\n        \"array_handle\": 4096,\n        \"start_address\": \"0x0\",\n        \"end_address\": \"0xc0000000\",\n        \"part_width\": 1\n      },\n      {\n        \"handle\": 4865,\n        \"array_handle\": 4096,\n        \"start_address\": \"0x100000000\",\n        \"end_address\": \"0x140000000\",\n        \"part_width\": 1\n      }\n    ],\n    \"memory_device\": [\n      {\n        \"handle\": 4352,\n        \"location\": \"DIMM 0\",\n        \"bank_location\": \"\",\n        \"manufacturer\": \"QEMU\",\n        \"part_number\": \"\",\n        \"array_handle\": 4096,\n        \"error_handle\": 65534,\n        \"width\": 0,\n        \"ecc_bits\": 0,\n        \"size\": 4194304,\n        \"form_factor\": {\n          \"hex\": \"0009\",\n          \"name\": \"DIMM\",\n          \"value\": 9\n        },\n        \"set\": 0,\n        \"memory_type\": {\n          \"hex\": \"0007\",\n          \"name\": \"RAM\",\n          \"value\": 7\n        },\n        \"memory_type_details\": [\n          \"Other\"\n        ],\n        \"speed\": 0\n      }\n    ],\n    \"processor\": [\n      {\n        \"handle\": 1024,\n        \"socket\": \"CPU 0\",\n        \"socket_type\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"socket_populated\": true,\n        \"manufacturer\": \"QEMU\",\n        \"version\": \"pc-i440fx-9.2\",\n        \"part\": \"\",\n        \"processor_type\": {\n          \"hex\": \"0003\",\n          \"name\": \"CPU\",\n          \"value\": 3\n        },\n        \"processor_family\": {\n          \"hex\": \"00fe\",\n          \"name\": \"Other\",\n          \"value\": 254\n        },\n        \"processor_status\": {\n          \"hex\": \"0001\",\n          \"name\": \"Enabled\",\n          \"value\": 1\n        },\n        \"clock_ext\": 0,\n        \"clock_max\": 2000,\n        \"cache_handle_l1\": 0,\n        \"cache_handle_l2\": 0,\n        \"cache_handle_l3\": 0\n      },\n      {\n        \"handle\": 1025,\n        \"socket\": \"CPU 1\",\n        \"socket_type\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"socket_populated\": true,\n        \"manufacturer\": \"QEMU\",\n        \"version\": \"pc-i440fx-9.2\",\n        \"part\": \"\",\n        \"processor_type\": {\n          \"hex\": \"0003\",\n          \"name\": \"CPU\",\n          \"value\": 3\n        },\n        \"processor_family\": {\n          \"hex\": \"00fe\",\n          \"name\": \"Other\",\n          \"value\": 254\n        },\n        \"processor_status\": {\n          \"hex\": \"0001\",\n          \"name\": \"Enabled\",\n          \"value\": 1\n        },\n        \"clock_ext\": 0,\n        \"clock_max\": 2000,\n        \"cache_handle_l1\": 0,\n        \"cache_handle_l2\": 0,\n        \"cache_handle_l3\": 0\n      }\n    ],\n    \"system\": {\n      \"handle\": 256,\n      \"manufacturer\": \"netcup\",\n      \"product\": \"KVM Server\",\n      \"version\": \"VPS 500 G12\",\n      \"wake_up\": {\n        \"hex\": \"0006\",\n        \"name\": \"Power Switch\",\n        \"value\": 6\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "machines/fichte/configuration.nix",
    "content": "{\n  pkgs,\n  nixos-hardware,\n  lib,\n  ...\n}:\n{\n\n  imports = [\n    ./disko-config-btrfs.nix\n    nixos-hardware.nixosModules.lenovo-thinkpad-t490\n  ];\n\n  environment.systemPackages = with pkgs; [\n    python3\n\n  ];\n\nprograms.vscode.enable = true;\n\n  networking.hostName = \"fichte\";\n\n  # Set keymap to DE on this device\n  console.keyMap = lib.mkForce \"de\";\n  services.xserver = {\n    layout = \"de\";\n    xkbOptions = \"eurosign:e\";\n  };\n}\n"
  },
  {
    "path": "machines/fichte/disko-config-btrfs.nix",
    "content": "{\n\n  boot.growPartition = true;\n  boot.supportedFilesystems.btrfs = true;\n\n  services.btrfs.autoScrub = {\n    enable = true;\n    interval = \"weekly\";\n    # Defaults to all\n    # fileSystems = [ \"/\" ];\n  };\n\n  disko.devices = {\n    disk = {\n      main = {\n        type = \"disk\";\n        device = \"/dev/nvme0n1\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n            ESP = {\n              size = \"512M\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"umask=0077\" ];\n              };\n            };\n            luks = {\n              size = \"100%\";\n              content = {\n                type = \"luks\";\n                name = \"crypted\";\n                # disable settings.keyFile if you want to use interactive password entry\n                #passwordFile = \"/tmp/secret.key\"; # Interactive\n                settings = {\n                  allowDiscards = true;\n                  # keyFile = \"/tmp/secret.key\";\n                };\n                # additionalKeyFiles = [ \"/tmp/additionalSecret.key\" ];\n                content = {\n                  type = \"btrfs\";\n                  extraArgs = [ \"-f\" ];\n                  subvolumes = {\n                    \"/root\" = {\n                      mountpoint = \"/\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/home\" = {\n                      mountpoint = \"/home\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/nix\" = {\n                      mountpoint = \"/nix\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/swap\" = {\n                      mountpoint = \"/.swapvol\";\n                      swap.swapfile.size = \"8G\";\n                    };\n                  };\n                };\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/fichte/facter.json",
    "content": "{\n  \"version\": 1,\n  \"system\": \"x86_64-linux\",\n  \"virtualisation\": \"none\",\n  \"hardware\": {\n    \"bios\": {\n      \"apm_info\": {\n        \"supported\": false,\n        \"enabled\": false,\n        \"version\": 0,\n        \"sub_version\": 0,\n        \"bios_flags\": 0\n      },\n      \"vbe_info\": {\n        \"version\": 0,\n        \"video_memory\": 0\n      },\n      \"pnp\": false,\n      \"pnp_id\": 0,\n      \"lba_support\": false,\n      \"low_memory_size\": 0,\n      \"smbios_version\": 769\n    },\n    \"bluetooth\": [\n      {\n        \"index\": 37,\n        \"attached_to\": 42,\n        \"class_list\": [\"usb\", \"bluetooth\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0115\",\n          \"name\": \"Bluetooth Device\",\n          \"value\": 277\n        },\n        \"vendor\": {\n          \"hex\": \"8087\",\n          \"value\": 32903\n        },\n        \"device\": {\n          \"hex\": \"0aaa\",\n          \"value\": 2730\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"0.02\",\n          \"value\": 0\n        },\n        \"model\": \"Bluetooth Device\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10:1.0\",\n        \"sysfs_bus_id\": \"1-10:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 12000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00e0\",\n            \"name\": \"wireless\",\n            \"value\": 224\n          },\n          \"device_subclass\": {\n            \"hex\": \"0001\",\n            \"name\": \"audio\",\n            \"value\": 1\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"00e0\",\n            \"name\": \"wireless\",\n            \"value\": 224\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0001\",\n            \"name\": \"audio\",\n            \"value\": 1\n          },\n          \"interface_protocol\": 1,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"btusb\",\n        \"driver_module\": \"btusb\",\n        \"drivers\": [\"btusb\"],\n        \"driver_modules\": [\"btusb\"],\n        \"module_alias\": \"usb:v8087p0AAAd0002dcE0dsc01dp01icE0isc01ip01in00\"\n      },\n      {\n        \"index\": 41,\n        \"attached_to\": 42,\n        \"class_list\": [\"usb\", \"bluetooth\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0115\",\n          \"name\": \"Bluetooth Device\",\n          \"value\": 277\n        },\n        \"vendor\": {\n          \"hex\": \"8087\",\n          \"value\": 32903\n        },\n        \"device\": {\n          \"hex\": \"0aaa\",\n          \"value\": 2730\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"0.02\",\n          \"value\": 0\n        },\n        \"model\": \"Bluetooth Device\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10:1.1\",\n        \"sysfs_bus_id\": \"1-10:1.1\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 12000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00e0\",\n            \"name\": \"wireless\",\n            \"value\": 224\n          },\n          \"device_subclass\": {\n            \"hex\": \"0001\",\n            \"name\": \"audio\",\n            \"value\": 1\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"00e0\",\n            \"name\": \"wireless\",\n            \"value\": 224\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0001\",\n            \"name\": \"audio\",\n            \"value\": 1\n          },\n          \"interface_protocol\": 1,\n          \"interface_number\": 1,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"btusb\",\n        \"driver_module\": \"btusb\",\n        \"drivers\": [\"btusb\"],\n        \"driver_modules\": [\"btusb\"],\n        \"module_alias\": \"usb:v8087p0AAAd0002dcE0dsc01dp01icE0isc01ip01in01\"\n      }\n    ],\n    \"bridge\": [\n      {\n        \"index\": 12,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"bridge\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 28\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9db8\",\n          \"value\": 40376\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"00f0\",\n          \"value\": 240\n        },\n        \"model\": \"Intel PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1c.0\",\n        \"sysfs_bus_id\": \"0000:00:1c.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1031,\n          \"header_type\": 1,\n          \"secondary_bus\": 1,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\"pcieport\"],\n        \"driver_modules\": [\"pcieportdrv\"],\n        \"module_alias\": \"pci:v00008086d00009DB8sv000017AAsd00002279bc06sc04i00\"\n      },\n      {\n        \"index\": 14,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"bridge\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 31\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"ISA bridge\",\n          \"value\": 1\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9d84\",\n          \"value\": 40324\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel ISA bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1f.0\",\n        \"sysfs_bus_id\": \"0000:00:1f.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 7,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"module_alias\": \"pci:v00008086d00009D84sv000017AAsd00002279bc06sc01i00\"\n      },\n      {\n        \"index\": 21,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"bridge\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Host bridge\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"3e34\",\n          \"value\": 15924\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"000c\",\n          \"value\": 12\n        },\n        \"model\": \"Intel Host bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:00.0\",\n        \"sysfs_bus_id\": \"0000:00:00.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 6,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"skl_uncore\",\n        \"driver_module\": \"intel_uncore\",\n        \"drivers\": [\"skl_uncore\"],\n        \"driver_modules\": [\"intel_uncore\"],\n        \"module_alias\": \"pci:v00008086d00003E34sv000017AAsd00002279bc06sc00i00\"\n      },\n      {\n        \"index\": 23,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"bridge\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 29\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9db4\",\n          \"value\": 40372\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"00f0\",\n          \"value\": 240\n        },\n        \"model\": \"Intel PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1d.4\",\n        \"sysfs_bus_id\": \"0000:00:1d.4\",\n        \"detail\": {\n          \"function\": 4,\n          \"command\": 1031,\n          \"header_type\": 1,\n          \"secondary_bus\": 61,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\"pcieport\"],\n        \"driver_modules\": [\"pcieportdrv\"],\n        \"module_alias\": \"pci:v00008086d00009DB4sv000017AAsd00002279bc06sc04i00\"\n      },\n      {\n        \"index\": 26,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"bridge\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 29\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9db0\",\n          \"value\": 40368\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"00f0\",\n          \"value\": 240\n        },\n        \"model\": \"Intel PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1d.0\",\n        \"sysfs_bus_id\": \"0000:00:1d.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1031,\n          \"header_type\": 1,\n          \"secondary_bus\": 60,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\"pcieport\"],\n        \"driver_modules\": [\"pcieportdrv\"],\n        \"module_alias\": \"pci:v00008086d00009DB0sv000017AAsd00002279bc06sc04i00\"\n      },\n      {\n        \"index\": 29,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"bridge\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 28\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9dbc\",\n          \"value\": 40380\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"00f0\",\n          \"value\": 240\n        },\n        \"model\": \"Intel PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1c.4\",\n        \"sysfs_bus_id\": \"0000:00:1c.4\",\n        \"detail\": {\n          \"function\": 4,\n          \"command\": 1031,\n          \"header_type\": 1,\n          \"secondary_bus\": 2,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\"pcieport\"],\n        \"driver_modules\": [\"pcieportdrv\"],\n        \"module_alias\": \"pci:v00008086d00009DBCsv000017AAsd00002279bc06sc04i00\"\n      }\n    ],\n    \"camera\": [\n      {\n        \"index\": 38,\n        \"attached_to\": 42,\n        \"class_list\": [\"camera\", \"usb\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010f\",\n          \"name\": \"Camera\",\n          \"value\": 271\n        },\n        \"vendor\": {\n          \"hex\": \"30c9\",\n          \"name\": \"8SSC20X55495L1GZ05W0SA1\",\n          \"value\": 12489\n        },\n        \"device\": {\n          \"hex\": \"001b\",\n          \"name\": \"Integrated Camera\",\n          \"value\": 27\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"0.02\",\n          \"value\": 0\n        },\n        \"serial\": \"0001\",\n        \"model\": \"8SSC20X55495L1GZ05W0SA1 Integrated Camera\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.1\",\n        \"sysfs_bus_id\": \"1-8:1.1\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00ef\",\n            \"name\": \"miscellaneous\",\n            \"value\": 239\n          },\n          \"device_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"000e\",\n            \"name\": \"video\",\n            \"value\": 14\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 1,\n          \"interface_alternate_setting\": 0,\n          \"interface_association\": {\n            \"function_class\": {\n              \"hex\": \"000e\",\n              \"name\": \"video\",\n              \"value\": 14\n            },\n            \"function_subclass\": {\n              \"hex\": \"0003\",\n              \"name\": \"hid\",\n              \"value\": 3\n            },\n            \"function_protocol\": 0,\n            \"interface_count\": 2,\n            \"first_interface\": 0\n          }\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"uvcvideo\",\n        \"driver_module\": \"uvcvideo\",\n        \"drivers\": [\"uvcvideo\"],\n        \"driver_modules\": [\"uvcvideo\"],\n        \"module_alias\": \"usb:v30C9p001Bd0002dcEFdsc02dp01ic0Eisc02ip00in01\"\n      },\n      {\n        \"index\": 43,\n        \"attached_to\": 42,\n        \"class_list\": [\"camera\", \"usb\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010f\",\n          \"name\": \"Camera\",\n          \"value\": 271\n        },\n        \"vendor\": {\n          \"hex\": \"30c9\",\n          \"name\": \"8SSC20X55495L1GZ05W0SA1\",\n          \"value\": 12489\n        },\n        \"device\": {\n          \"hex\": \"001b\",\n          \"name\": \"Integrated Camera\",\n          \"value\": 27\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"0.02\",\n          \"value\": 0\n        },\n        \"serial\": \"0001\",\n        \"model\": \"8SSC20X55495L1GZ05W0SA1 Integrated Camera\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0\",\n        \"sysfs_bus_id\": \"1-8:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00ef\",\n            \"name\": \"miscellaneous\",\n            \"value\": 239\n          },\n          \"device_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"000e\",\n            \"name\": \"video\",\n            \"value\": 14\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0001\",\n            \"name\": \"audio\",\n            \"value\": 1\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0,\n          \"interface_association\": {\n            \"function_class\": {\n              \"hex\": \"000e\",\n              \"name\": \"video\",\n              \"value\": 14\n            },\n            \"function_subclass\": {\n              \"hex\": \"0003\",\n              \"name\": \"hid\",\n              \"value\": 3\n            },\n            \"function_protocol\": 0,\n            \"interface_count\": 2,\n            \"first_interface\": 0\n          }\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"uvcvideo\",\n        \"driver_module\": \"uvcvideo\",\n        \"drivers\": [\"uvcvideo\"],\n        \"driver_modules\": [\"uvcvideo\"],\n        \"module_alias\": \"usb:v30C9p001Bd0002dcEFdsc02dp01ic0Eisc01ip00in00\"\n      }\n    ],\n    \"chip_card\": [\n      {\n        \"index\": 45,\n        \"attached_to\": 42,\n        \"class_list\": [\"chip_card\", \"usb\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010e\",\n          \"name\": \"Chipcard Reader\",\n          \"value\": 270\n        },\n        \"vendor\": {\n          \"hex\": \"058f\",\n          \"name\": \"Alcor Micro, Inc.\",\n          \"value\": 1423\n        },\n        \"device\": {\n          \"hex\": \"9540\",\n          \"name\": \"EMV Smartcard Reader\",\n          \"value\": 38208\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"1.20\",\n          \"value\": 0\n        },\n        \"model\": \"Alcor Micro EMV Smartcard Reader\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0\",\n        \"sysfs_bus_id\": \"1-1:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 12000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 0,\n          \"interface_class\": {\n            \"hex\": \"000b\",\n            \"name\": \"smart_card\",\n            \"value\": 11\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"module_alias\": \"usb:v058Fp9540d0120dc00dsc00dp00ic0Bisc00ip00in00\"\n      }\n    ],\n    \"cpu\": [\n      {\n        \"architecture\": \"x86_64\",\n        \"vendor_name\": \"GenuineIntel\",\n        \"family\": 6,\n        \"model\": 142,\n        \"stepping\": 12,\n        \"features\": [\n          \"fpu\",\n          \"vme\",\n          \"de\",\n          \"pse\",\n          \"tsc\",\n          \"msr\",\n          \"pae\",\n          \"mce\",\n          \"cx8\",\n          \"apic\",\n          \"sep\",\n          \"mtrr\",\n          \"pge\",\n          \"mca\",\n          \"cmov\",\n          \"pat\",\n          \"pse36\",\n          \"clflush\",\n          \"dts\",\n          \"acpi\",\n          \"mmx\",\n          \"fxsr\",\n          \"sse\",\n          \"sse2\",\n          \"ss\",\n          \"ht\",\n          \"tm\",\n          \"pbe\",\n          \"syscall\",\n          \"nx\",\n          \"pdpe1gb\",\n          \"rdtscp\",\n          \"lm\",\n          \"constant_tsc\",\n          \"art\",\n          \"arch_perfmon\",\n          \"pebs\",\n          \"bts\",\n          \"rep_good\",\n          \"nopl\",\n          \"xtopology\",\n          \"nonstop_tsc\",\n          \"cpuid\",\n          \"aperfmperf\",\n          \"pni\",\n          \"pclmulqdq\",\n          \"dtes64\",\n          \"monitor\",\n          \"ds_cpl\",\n          \"vmx\",\n          \"est\",\n          \"tm2\",\n          \"ssse3\",\n          \"sdbg\",\n          \"fma\",\n          \"cx16\",\n          \"xtpr\",\n          \"pdcm\",\n          \"pcid\",\n          \"sse4_1\",\n          \"sse4_2\",\n          \"x2apic\",\n          \"movbe\",\n          \"popcnt\",\n          \"tsc_deadline_timer\",\n          \"aes\",\n          \"xsave\",\n          \"avx\",\n          \"f16c\",\n          \"rdrand\",\n          \"lahf_lm\",\n          \"abm\",\n          \"3dnowprefetch\",\n          \"cpuid_fault\",\n          \"epb\",\n          \"ssbd\",\n          \"ibrs\",\n          \"ibpb\",\n          \"stibp\",\n          \"ibrs_enhanced\",\n          \"tpr_shadow\",\n          \"flexpriority\",\n          \"ept\",\n          \"vpid\",\n          \"ept_ad\",\n          \"fsgsbase\",\n          \"tsc_adjust\",\n          \"bmi1\",\n          \"avx2\",\n          \"smep\",\n          \"bmi2\",\n          \"erms\",\n          \"invpcid\",\n          \"mpx\",\n          \"rdseed\",\n          \"adx\",\n          \"smap\",\n          \"clflushopt\",\n          \"intel_pt\",\n          \"xsaveopt\",\n          \"xsavec\",\n          \"xgetbv1\",\n          \"xsaves\",\n          \"dtherm\",\n          \"ida\",\n          \"arat\",\n          \"pln\",\n          \"pts\",\n          \"hwp\",\n          \"hwp_notify\",\n          \"hwp_act_window\",\n          \"hwp_epp\",\n          \"vnmi\",\n          \"md_clear\",\n          \"flush_l1d\",\n          \"arch_capabilities\"\n        ],\n        \"bugs\": [\n          \"spectre_v1\",\n          \"spectre_v2\",\n          \"spec_store_bypass\",\n          \"swapgs\",\n          \"itlb_multihit\",\n          \"srbds\",\n          \"mmio_stale_data\",\n          \"retbleed\",\n          \"eibrs_pbrsb\",\n          \"gds\",\n          \"bhi\",\n          \"spectre_v2_user\",\n          \"old_microcode\",\n          \"its\",\n          \"vmscape\"\n        ],\n        \"bogo\": 3999,\n        \"cache\": 8192,\n        \"units\": 16,\n        \"physical_id\": 0,\n        \"siblings\": 8,\n        \"cores\": 4,\n        \"fpu\": true,\n        \"fpu_exception\": true,\n        \"cpuid_level\": 22,\n        \"write_protect\": false,\n        \"tlb_size\": 32766,\n        \"clflush_size\": 64,\n        \"cache_alignment\": 64,\n        \"address_sizes\": {\n          \"physical\": \"0x27\",\n          \"virtual\": \"0x30\"\n        }\n      }\n    ],\n    \"disk\": [\n      {\n        \"index\": 34,\n        \"attached_to\": 25,\n        \"class_list\": [\"disk\", \"block_device\", \"nvme\"],\n        \"bus_type\": {\n          \"hex\": \"0096\",\n          \"name\": \"NVME\",\n          \"value\": 150\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0106\",\n          \"name\": \"Mass Storage Device\",\n          \"value\": 262\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disk\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"1e4b\",\n          \"value\": 7755\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1e4b\",\n          \"value\": 7755\n        },\n        \"device\": {\n          \"hex\": \"1202\",\n          \"name\": \"512GB PCIe SSD\",\n          \"value\": 4610\n        },\n        \"sub_device\": {\n          \"hex\": \"1202\",\n          \"value\": 4610\n        },\n        \"serial\": \"2024121103000964\",\n        \"model\": \"512GB PCIe SSD\",\n        \"sysfs_id\": \"/class/block/nvme0n1\",\n        \"sysfs_bus_id\": \"nvme0\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:1d.4/0000:3d:00.0/nvme/nvme0\",\n        \"unix_device_names\": [\n          \"/dev/disk/by-id/nvme-512GB_PCIe_SSD_2024121103000964\",\n          \"/dev/disk/by-id/nvme-512GB_PCIe_SSD_2024121103000964_1\",\n          \"/dev/disk/by-id/nvme-nvme.1e4b-32303234313231313033303030393634-3531324742205043496520535344-00000001\",\n          \"/dev/disk/by-path/pci-0000:3d:00.0-nvme-1\",\n          \"/dev/nvme0n1\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"disk_geo\",\n            \"cylinders\": 488386,\n            \"heads\": 64,\n            \"sectors\": 32,\n            \"size\": \"0x0\",\n            \"geo_type\": \"logical\"\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"sectors\",\n            \"value_1\": 1000215216,\n            \"value_2\": 512\n          }\n        ],\n        \"driver\": \"nvme\",\n        \"driver_module\": \"nvme\",\n        \"drivers\": [\"nvme\"],\n        \"driver_modules\": [\"nvme\"]\n      },\n      {\n        \"index\": 35,\n        \"attached_to\": 31,\n        \"class_list\": [\"disk\", \"usb\", \"scsi\", \"block_device\"],\n        \"bus_type\": {\n          \"hex\": \"0084\",\n          \"name\": \"SCSI\",\n          \"value\": 132\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0106\",\n          \"name\": \"Mass Storage Device\",\n          \"value\": 262\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disk\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"0781\",\n          \"name\": \"USB\",\n          \"value\": 1921\n        },\n        \"device\": {\n          \"hex\": \"5583\",\n          \"name\": \"SanDisk 3.2Gen1\",\n          \"value\": 21891\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"1.00\",\n          \"value\": 0\n        },\n        \"serial\": \"01017a33f8c1d6bbbfe7\",\n        \"model\": \"USB SanDisk 3.2Gen1\",\n        \"sysfs_id\": \"/class/block/sda\",\n        \"sysfs_bus_id\": \"0:0:0:0\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:14.0/usb2/2-4/2-4:1.0/host0/target0:0:0/0:0:0:0\",\n        \"unix_device_names\": [\n          \"/dev/disk/by-id/usb-USB_SanDisk_3.2Gen1_01017a33f8c1d6bbbfe70a7b26cd37feae47d7bdcd66539fbda070dffc8874bcaa990000000000000000000020b7a975008a4b00835581076e2f52e7-0:0\",\n          \"/dev/disk/by-path/pci-0000:00:14.0-usb-0:4:1.0-scsi-0:0:0:0\",\n          \"/dev/disk/by-path/pci-0000:00:14.0-usbv3-0:4:1.0-scsi-0:0:0:0\",\n          \"/dev/sda\"\n        ],\n        \"unix_device_name2\": \"/dev/sg0\",\n        \"resources\": [\n          {\n            \"type\": \"disk_geo\",\n            \"cylinders\": 58656,\n            \"heads\": 64,\n            \"sectors\": 32,\n            \"size\": \"0x0\",\n            \"geo_type\": \"logical\"\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"sectors\",\n            \"value_1\": 120127488,\n            \"value_2\": 512\n          }\n        ],\n        \"driver\": \"usb-storage\",\n        \"driver_module\": \"usb_storage\",\n        \"drivers\": [\"sd\", \"usb-storage\"],\n        \"driver_modules\": [\"sd_mod\", \"usb_storage\"],\n        \"module_alias\": \"usb:v0781p5583d0100dc00dsc00dp00ic08isc06ip50in00\"\n      }\n    ],\n    \"graphics_card\": [\n      {\n        \"index\": 30,\n        \"attached_to\": 0,\n        \"class_list\": [\"graphics_card\", \"pci\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 2\n        },\n        \"base_class\": {\n          \"hex\": \"0003\",\n          \"name\": \"Display controller\",\n          \"value\": 3\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"VGA compatible controller\",\n          \"value\": 0\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"VGA\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"3ea0\",\n          \"value\": 16032\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"model\": \"Intel VGA compatible controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:02.0\",\n        \"sysfs_bus_id\": \"0000:00:02.0\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 12288,\n            \"range\": 64,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1031,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"i915\",\n        \"driver_module\": \"i915\",\n        \"drivers\": [\"i915\"],\n        \"driver_modules\": [\"i915\"],\n        \"module_alias\": \"pci:v00008086d00003EA0sv000017AAsd00002279bc03sc00i00\"\n      }\n    ],\n    \"hub\": [\n      {\n        \"index\": 42,\n        \"attached_to\": 31,\n        \"class_list\": [\"usb\", \"hub\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.16.7 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.16\",\n          \"value\": 0\n        },\n        \"serial\": \"0000:00:14.0\",\n        \"model\": \"Linux 6.16.7 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-0:1.0\",\n        \"sysfs_bus_id\": \"1-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\"hub\"],\n        \"driver_modules\": [\"usbcore\"],\n        \"module_alias\": \"usb:v1D6Bp0002d0616dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 47,\n        \"attached_to\": 31,\n        \"class_list\": [\"usb\", \"hub\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.16.7 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.16\",\n          \"value\": 0\n        },\n        \"serial\": \"0000:00:14.0\",\n        \"model\": \"Linux 6.16.7 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb2/2-0:1.0\",\n        \"sysfs_bus_id\": \"2-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\"hub\"],\n        \"driver_modules\": [\"usbcore\"],\n        \"module_alias\": \"usb:v1D6Bp0003d0616dc09dsc00dp03ic09isc00ip00in00\"\n      }\n    ],\n    \"memory\": [\n      {\n        \"index\": 11,\n        \"attached_to\": 0,\n        \"class_list\": [\"memory\"],\n        \"base_class\": {\n          \"hex\": \"0101\",\n          \"name\": \"Internally Used Class\",\n          \"value\": 257\n        },\n        \"sub_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Main Memory\",\n          \"value\": 2\n        },\n        \"model\": \"Main Memory\",\n        \"resources\": [\n          {\n            \"type\": \"phys_mem\",\n            \"range\": 25769803776\n          }\n        ]\n      }\n    ],\n    \"monitor\": [\n      {\n        \"index\": 33,\n        \"attached_to\": 30,\n        \"class_list\": [\"monitor\"],\n        \"base_class\": {\n          \"hex\": \"0100\",\n          \"name\": \"Monitor\",\n          \"value\": 256\n        },\n        \"sub_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"LCD Monitor\",\n          \"value\": 2\n        },\n        \"vendor\": {\n          \"hex\": \"30e4\",\n          \"name\": \"LG Display\",\n          \"value\": 12516\n        },\n        \"device\": {\n          \"hex\": \"05fa\",\n          \"value\": 1530\n        },\n        \"serial\": \"0\",\n        \"model\": \"LG Display LCD Monitor\",\n        \"resources\": [\n          {\n            \"type\": \"monitor\",\n            \"width\": 1920,\n            \"height\": 1080,\n            \"vertical_frequency\": 60,\n            \"interlaced\": false\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"mm\",\n            \"value_1\": 309,\n            \"value_2\": 174\n          }\n        ],\n        \"detail\": {\n          \"manufacture_year\": 2018,\n          \"manufacture_week\": 0,\n          \"vertical_sync\": {\n            \"min\": 0,\n            \"max\": 0\n          },\n          \"horizontal_sync\": {\n            \"min\": 0,\n            \"max\": 0\n          },\n          \"horizontal_sync_timings\": {\n            \"disp\": 1920,\n            \"sync_start\": 1968,\n            \"sync_end\": 2000,\n            \"total\": 2164\n          },\n          \"vertical_sync_timings\": {\n            \"disp\": 1080,\n            \"sync_start\": 1083,\n            \"sync_end\": 1088,\n            \"total\": 1102\n          },\n          \"clock\": 114460,\n          \"width\": 1920,\n          \"height\": 1080,\n          \"width_millimetres\": 309,\n          \"height_millimetres\": 174,\n          \"horizontal_flag\": 45,\n          \"vertical_flag\": 43,\n          \"vendor\": \"LG Display\",\n          \"name\": \"\"\n        }\n      }\n    ],\n    \"mouse\": [\n      {\n        \"index\": 52,\n        \"attached_to\": 0,\n        \"bus_type\": {\n          \"hex\": \"0080\",\n          \"name\": \"ps2\",\n          \"value\": 128\n        },\n        \"base_class\": {\n          \"hex\": \"0118\",\n          \"name\": \"touchpad\",\n          \"value\": 280\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"ps2\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"device\": {\n          \"hex\": \"0007\",\n          \"value\": 7\n        },\n        \"sysfs_id\": \"/devices/platform/i8042/serio1/input/input5\",\n        \"unix_device_names\": [\"/dev/input/event5\", \"/dev/input/ + handler\"]\n      }\n    ],\n    \"network_controller\": [\n      {\n        \"index\": 16,\n        \"attached_to\": 0,\n        \"class_list\": [\"network_controller\", \"pci\", \"wlan_card\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 20\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0082\",\n          \"name\": \"WLAN controller\",\n          \"value\": 130\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"device\": {\n          \"hex\": \"9df0\",\n          \"value\": 40432\n        },\n        \"sub_device\": {\n          \"hex\": \"0034\",\n          \"value\": 52\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel WLAN controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.3\",\n        \"sysfs_bus_id\": \"0000:00:14.3\",\n        \"unix_device_names\": [\"wlan0\"],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 50\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 99\n          },\n          {\n            \"type\": \"wlan\",\n            \"channels\": [\n              \"1\",\n              \"2\",\n              \"3\",\n              \"4\",\n              \"5\",\n              \"6\",\n              \"7\",\n              \"8\",\n              \"9\",\n              \"10\",\n              \"11\",\n              \"12\",\n              \"13\",\n              \"36\",\n              \"40\",\n              \"44\",\n              \"48\",\n              \"52\",\n              \"56\",\n              \"60\",\n              \"64\",\n              \"100\",\n              \"104\",\n              \"108\",\n              \"112\",\n              \"116\",\n              \"120\",\n              \"124\",\n              \"128\",\n              \"132\",\n              \"136\",\n              \"140\"\n            ],\n            \"frequencies\": [\n              \"2.412\",\n              \"2.417\",\n              \"2.422\",\n              \"2.427\",\n              \"2.432\",\n              \"2.437\",\n              \"2.442\",\n              \"2.447\",\n              \"2.452\",\n              \"2.457\",\n              \"2.462\",\n              \"2.467\",\n              \"2.472\",\n              \"5.18\",\n              \"5.2\",\n              \"5.22\",\n              \"5.24\",\n              \"5.26\",\n              \"5.28\",\n              \"5.3\",\n              \"5.32\",\n              \"5.5\",\n              \"5.52\",\n              \"5.54\",\n              \"5.56\",\n              \"5.58\",\n              \"5.6\",\n              \"5.62\",\n              \"5.64\",\n              \"5.66\",\n              \"5.68\",\n              \"5.7\"\n            ],\n            \"auth_modes\": [\"open\", \"sharedkey\", \"wpa-psk\", \"wpa-eap\"],\n            \"enc_modes\": [\"WEP40\", \"WEP104\", \"TKIP\", \"CCMP\"]\n          }\n        ],\n        \"detail\": {\n          \"function\": 3,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"iwlwifi\",\n        \"driver_module\": \"iwlwifi\",\n        \"drivers\": [\"iwlwifi\"],\n        \"driver_modules\": [\"iwlwifi\"],\n        \"module_alias\": \"pci:v00008086d00009DF0sv00008086sd00000034bc02sc80i00\"\n      },\n      {\n        \"index\": 28,\n        \"attached_to\": 0,\n        \"class_list\": [\"network_controller\", \"pci\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 31\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Ethernet controller\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"15be\",\n          \"value\": 5566\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel Ethernet controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1f.6\",\n        \"sysfs_bus_id\": \"0000:00:1f.6\",\n        \"unix_device_names\": [\"enp0s31f6\"],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 48\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 48\n          }\n        ],\n        \"detail\": {\n          \"function\": 6,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"e1000e\",\n        \"driver_module\": \"e1000e\",\n        \"drivers\": [\"e1000e\"],\n        \"driver_modules\": [\"e1000e\"],\n        \"module_alias\": \"pci:v00008086d000015BEsv000017AAsd00002279bc02sc00i00\"\n      },\n      {\n        \"index\": 40,\n        \"attached_to\": 42,\n        \"class_list\": [\"network_controller\", \"usb\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Ethernet controller\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"2cb7\",\n          \"name\": \"FIBOCOM\",\n          \"value\": 11447\n        },\n        \"device\": {\n          \"hex\": \"0210\",\n          \"name\": \"L830-EB\",\n          \"value\": 528\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"3.33\",\n          \"value\": 0\n        },\n        \"serial\": \"004999010640000\",\n        \"model\": \"FIBOCOM L830-EB\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0\",\n        \"sysfs_bus_id\": \"1-7:1.0\",\n        \"unix_device_names\": [\"wwp0s20f0u7\"],\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          },\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 99\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00ef\",\n            \"name\": \"miscellaneous\",\n            \"value\": 239\n          },\n          \"device_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"interface_subclass\": {\n            \"hex\": \"000e\",\n            \"name\": \"video\",\n            \"value\": 14\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0,\n          \"interface_association\": {\n            \"function_class\": {\n              \"hex\": \"0002\",\n              \"name\": \"comm\",\n              \"value\": 2\n            },\n            \"function_subclass\": {\n              \"hex\": \"000e\",\n              \"name\": \"video\",\n              \"value\": 14\n            },\n            \"function_protocol\": 0,\n            \"interface_count\": 2,\n            \"first_interface\": 0\n          }\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"cdc_mbim\",\n        \"driver_module\": \"cdc_mbim\",\n        \"drivers\": [\"cdc_mbim\"],\n        \"driver_modules\": [\"cdc_mbim\"],\n        \"module_alias\": \"usb:v2CB7p0210d0333dcEFdsc02dp01ic02isc0Eip00in00\"\n      }\n    ],\n    \"network_interface\": [\n      {\n        \"index\": 48,\n        \"attached_to\": 40,\n        \"class_list\": [\"network_interface\"],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Ethernet\",\n          \"value\": 1\n        },\n        \"model\": \"Ethernet network interface\",\n        \"sysfs_id\": \"/class/net/wwp0s20f0u7\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0\",\n        \"unix_device_names\": [\"wwp0s20f0u7\"],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 99\n          }\n        ],\n        \"driver\": \"cdc_mbim\",\n        \"driver_module\": \"cdc_mbim\",\n        \"drivers\": [\"cdc_mbim\"],\n        \"driver_modules\": [\"cdc_mbim\"]\n      },\n      {\n        \"index\": 49,\n        \"attached_to\": 28,\n        \"class_list\": [\"network_interface\"],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Ethernet\",\n          \"value\": 1\n        },\n        \"model\": \"Ethernet network interface\",\n        \"sysfs_id\": \"/class/net/enp0s31f6\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:1f.6\",\n        \"unix_device_names\": [\"enp0s31f6\"],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 48\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 48\n          }\n        ],\n        \"driver\": \"e1000e\",\n        \"driver_module\": \"e1000e\",\n        \"drivers\": [\"e1000e\"],\n        \"driver_modules\": [\"e1000e\"]\n      },\n      {\n        \"index\": 50,\n        \"attached_to\": 16,\n        \"class_list\": [\"network_interface\"],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"000a\",\n          \"name\": \"WLAN\",\n          \"value\": 10\n        },\n        \"model\": \"WLAN network interface\",\n        \"sysfs_id\": \"/class/net/wlan0\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:14.3\",\n        \"unix_device_names\": [\"wlan0\"],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 50\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 99\n          }\n        ],\n        \"driver\": \"iwlwifi\",\n        \"driver_module\": \"iwlwifi\",\n        \"drivers\": [\"iwlwifi\"],\n        \"driver_modules\": [\"iwlwifi\"]\n      },\n      {\n        \"index\": 51,\n        \"attached_to\": 0,\n        \"class_list\": [\"network_interface\"],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Loopback\",\n          \"value\": 0\n        },\n        \"model\": \"Loopback network interface\",\n        \"sysfs_id\": \"/class/net/lo\",\n        \"unix_device_names\": [\"lo\"]\n      }\n    ],\n    \"pci\": [\n      {\n        \"index\": 13,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 8\n        },\n        \"base_class\": {\n          \"hex\": \"0008\",\n          \"name\": \"Generic system peripheral\",\n          \"value\": 8\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"System peripheral\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"1911\",\n          \"value\": 6417\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"model\": \"Intel System peripheral\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:08.0\",\n        \"sysfs_bus_id\": \"0000:00:08.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 0,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"module_alias\": \"pci:v00008086d00001911sv000017AAsd00002279bc08sc80i00\"\n      },\n      {\n        \"index\": 15,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 4\n        },\n        \"base_class\": {\n          \"hex\": \"0011\",\n          \"name\": \"Signal processing controller\",\n          \"value\": 17\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"Signal processing controller\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"1903\",\n          \"value\": 6403\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"000c\",\n          \"value\": 12\n        },\n        \"model\": \"Intel Signal processing controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:04.0\",\n        \"sysfs_bus_id\": \"0000:00:04.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 2,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"proc_thermal\",\n        \"driver_module\": \"processor_thermal_device_pci_legacy\",\n        \"drivers\": [\"proc_thermal\"],\n        \"driver_modules\": [\"processor_thermal_device_pci_legacy\"],\n        \"module_alias\": \"pci:v00008086d00001903sv000017AAsd00002279bc11sc80i00\"\n      },\n      {\n        \"index\": 17,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 22\n        },\n        \"base_class\": {\n          \"hex\": \"0007\",\n          \"name\": \"Communication controller\",\n          \"value\": 7\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"Communication controller\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9de0\",\n          \"value\": 40416\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel Communication controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:16.0\",\n        \"sysfs_bus_id\": \"0000:00:16.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"mei_me\",\n        \"driver_module\": \"mei_me\",\n        \"drivers\": [\"mei_me\"],\n        \"driver_modules\": [\"mei_me\"],\n        \"module_alias\": \"pci:v00008086d00009DE0sv000017AAsd00002279bc07sc80i00\"\n      },\n      {\n        \"index\": 18,\n        \"attached_to\": 12,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 1,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"00ff\",\n          \"name\": \"Unclassified device\",\n          \"value\": 255\n        },\n        \"vendor\": {\n          \"hex\": \"10ec\",\n          \"value\": 4332\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"522a\",\n          \"value\": 21034\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0001\",\n          \"value\": 1\n        },\n        \"model\": \"Unclassified device\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1c.0/0000:01:00.0\",\n        \"sysfs_bus_id\": \"0000:01:00.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"rtsx_pci\",\n        \"driver_module\": \"rtsx_pci\",\n        \"drivers\": [\"rtsx_pci\"],\n        \"driver_modules\": [\"rtsx_pci\"],\n        \"module_alias\": \"pci:v000010ECd0000522Asv000017AAsd00002279bcFFsc00i00\"\n      },\n      {\n        \"index\": 19,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 31\n        },\n        \"base_class\": {\n          \"hex\": \"000c\",\n          \"name\": \"Serial bus controller\",\n          \"value\": 12\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9da4\",\n          \"value\": 40356\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel Serial bus controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1f.5\",\n        \"sysfs_bus_id\": \"0000:00:1f.5\",\n        \"detail\": {\n          \"function\": 5,\n          \"command\": 1026,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"intel-spi\",\n        \"driver_module\": \"spi_intel_pci\",\n        \"drivers\": [\"intel-spi\"],\n        \"driver_modules\": [\"spi_intel_pci\"],\n        \"module_alias\": \"pci:v00008086d00009DA4sv000017AAsd00002279bc0Csc80i00\"\n      },\n      {\n        \"index\": 22,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 18\n        },\n        \"base_class\": {\n          \"hex\": \"0011\",\n          \"name\": \"Signal processing controller\",\n          \"value\": 17\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"name\": \"Signal processing controller\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9df9\",\n          \"value\": 40441\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel Signal processing controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:12.0\",\n        \"sysfs_bus_id\": \"0000:00:12.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 2,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"intel_pch_thermal\",\n        \"driver_module\": \"intel_pch_thermal\",\n        \"drivers\": [\"intel_pch_thermal\"],\n        \"driver_modules\": [\"intel_pch_thermal\"],\n        \"module_alias\": \"pci:v00008086d00009DF9sv000017AAsd00002279bc11sc80i00\"\n      },\n      {\n        \"index\": 24,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 21\n        },\n        \"base_class\": {\n          \"hex\": \"000c\",\n          \"name\": \"Serial bus controller\",\n          \"value\": 12\n        },\n        \"sub_class\": {\n          \"hex\": \"0080\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9de8\",\n          \"value\": 40424\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel Serial bus controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:15.0\",\n        \"sysfs_bus_id\": \"0000:00:15.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 6,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"intel-lpss\",\n        \"driver_module\": \"intel_lpss_pci\",\n        \"drivers\": [\"intel-lpss\"],\n        \"driver_modules\": [\"intel_lpss_pci\"],\n        \"module_alias\": \"pci:v00008086d00009DE8sv000017AAsd00002279bc0Csc80i00\"\n      },\n      {\n        \"index\": 27,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 20\n        },\n        \"base_class\": {\n          \"hex\": \"0005\",\n          \"name\": \"Memory controller\",\n          \"value\": 5\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"RAM memory\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9def\",\n          \"value\": 40431\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel RAM memory\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.2\",\n        \"sysfs_bus_id\": \"0000:00:14.2\",\n        \"detail\": {\n          \"function\": 2,\n          \"command\": 0,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"module_alias\": \"pci:v00008086d00009DEFsv000017AAsd00002279bc05sc00i00\"\n      },\n      {\n        \"index\": 32,\n        \"attached_to\": 0,\n        \"class_list\": [\"pci\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 31\n        },\n        \"base_class\": {\n          \"hex\": \"000c\",\n          \"name\": \"Serial bus controller\",\n          \"value\": 12\n        },\n        \"sub_class\": {\n          \"hex\": \"0005\",\n          \"name\": \"SMBus\",\n          \"value\": 5\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9da3\",\n          \"value\": 40355\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel SMBus\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1f.4\",\n        \"sysfs_bus_id\": \"0000:00:1f.4\",\n        \"resources\": [\n          {\n            \"type\": \"io\",\n            \"base\": 61344,\n            \"range\": 32,\n            \"enabled\": true,\n            \"access\": \"read_write\"\n          }\n        ],\n        \"detail\": {\n          \"function\": 4,\n          \"command\": 3,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"i801_smbus\",\n        \"driver_module\": \"i2c_i801\",\n        \"drivers\": [\"i801_smbus\"],\n        \"driver_modules\": [\"i2c_i801\"],\n        \"module_alias\": \"pci:v00008086d00009DA3sv000017AAsd00002279bc0Csc05i00\"\n      }\n    ],\n    \"sound\": [\n      {\n        \"index\": 20,\n        \"attached_to\": 0,\n        \"class_list\": [\"sound\", \"pci\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 31\n        },\n        \"base_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"Multimedia controller\",\n          \"value\": 4\n        },\n        \"sub_class\": {\n          \"hex\": \"0003\",\n          \"value\": 3\n        },\n        \"pci_interface\": {\n          \"hex\": \"0080\",\n          \"value\": 128\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9dc8\",\n          \"value\": 40392\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel Multimedia controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1f.3\",\n        \"sysfs_bus_id\": \"0000:00:1f.3\",\n        \"detail\": {\n          \"function\": 3,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 128\n        },\n        \"driver\": \"snd_hda_intel\",\n        \"driver_module\": \"snd_hda_intel\",\n        \"drivers\": [\"snd_hda_intel\"],\n        \"driver_modules\": [\"snd_hda_intel\"],\n        \"module_alias\": \"pci:v00008086d00009DC8sv000017AAsd00002279bc04sc03i80\"\n      }\n    ],\n    \"storage_controller\": [\n      {\n        \"index\": 25,\n        \"attached_to\": 23,\n        \"class_list\": [\"storage_controller\", \"pci\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 61,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Mass storage controller\",\n          \"value\": 1\n        },\n        \"sub_class\": {\n          \"hex\": \"0008\",\n          \"value\": 8\n        },\n        \"pci_interface\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"vendor\": {\n          \"hex\": \"1e4b\",\n          \"value\": 7755\n        },\n        \"sub_vendor\": {\n          \"hex\": \"1e4b\",\n          \"value\": 7755\n        },\n        \"device\": {\n          \"hex\": \"1202\",\n          \"value\": 4610\n        },\n        \"sub_device\": {\n          \"hex\": \"1202\",\n          \"value\": 4610\n        },\n        \"revision\": {\n          \"hex\": \"0001\",\n          \"value\": 1\n        },\n        \"model\": \"Mass storage controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:1d.4/0000:3d:00.0\",\n        \"sysfs_bus_id\": \"0000:3d:00.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 2\n        },\n        \"driver\": \"nvme\",\n        \"driver_module\": \"nvme\",\n        \"drivers\": [\"nvme\"],\n        \"driver_modules\": [\"nvme\"],\n        \"module_alias\": \"pci:v00001E4Bd00001202sv00001E4Bsd00001202bc01sc08i02\"\n      }\n    ],\n    \"system\": {\n      \"form_factor\": \"laptop\"\n    },\n    \"usb\": [\n      {\n        \"index\": 39,\n        \"attached_to\": 42,\n        \"class_list\": [\"usb\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"2cb7\",\n          \"name\": \"FIBOCOM\",\n          \"value\": 11447\n        },\n        \"device\": {\n          \"hex\": \"0210\",\n          \"name\": \"L830-EB\",\n          \"value\": 528\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"3.33\",\n          \"value\": 0\n        },\n        \"serial\": \"004999010640000\",\n        \"model\": \"FIBOCOM L830-EB\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.2\",\n        \"sysfs_bus_id\": \"1-7:1.2\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00ef\",\n            \"name\": \"miscellaneous\",\n            \"value\": 239\n          },\n          \"device_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 2,\n          \"interface_alternate_setting\": 0,\n          \"interface_association\": {\n            \"function_class\": {\n              \"hex\": \"0002\",\n              \"name\": \"comm\",\n              \"value\": 2\n            },\n            \"function_subclass\": {\n              \"hex\": \"0002\",\n              \"name\": \"comm\",\n              \"value\": 2\n            },\n            \"function_protocol\": 0,\n            \"interface_count\": 2,\n            \"first_interface\": 2\n          }\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"cdc_acm\",\n        \"driver_module\": \"cdc_acm\",\n        \"drivers\": [\"cdc_acm\"],\n        \"driver_modules\": [\"cdc_acm\"],\n        \"module_alias\": \"usb:v2CB7p0210d0333dcEFdsc02dp01ic02isc02ip00in02\"\n      },\n      {\n        \"index\": 44,\n        \"attached_to\": 42,\n        \"class_list\": [\"usb\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"2cb7\",\n          \"name\": \"FIBOCOM\",\n          \"value\": 11447\n        },\n        \"device\": {\n          \"hex\": \"0210\",\n          \"name\": \"L830-EB\",\n          \"value\": 528\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"3.33\",\n          \"value\": 0\n        },\n        \"serial\": \"004999010640000\",\n        \"model\": \"FIBOCOM L830-EB\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.3\",\n        \"sysfs_bus_id\": \"1-7:1.3\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00ef\",\n            \"name\": \"miscellaneous\",\n            \"value\": 239\n          },\n          \"device_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"000a\",\n            \"name\": \"data\",\n            \"value\": 10\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 3,\n          \"interface_alternate_setting\": 0,\n          \"interface_association\": {\n            \"function_class\": {\n              \"hex\": \"0002\",\n              \"name\": \"comm\",\n              \"value\": 2\n            },\n            \"function_subclass\": {\n              \"hex\": \"0002\",\n              \"name\": \"comm\",\n              \"value\": 2\n            },\n            \"function_protocol\": 0,\n            \"interface_count\": 2,\n            \"first_interface\": 2\n          }\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"cdc_acm\",\n        \"driver_module\": \"cdc_acm\",\n        \"drivers\": [\"cdc_acm\"],\n        \"driver_modules\": [\"cdc_acm\"],\n        \"module_alias\": \"usb:v2CB7p0210d0333dcEFdsc02dp01ic0Aisc00ip00in03\"\n      },\n      {\n        \"index\": 46,\n        \"attached_to\": 42,\n        \"class_list\": [\"usb\", \"unknown\"],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Unclassified device\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"2cb7\",\n          \"name\": \"FIBOCOM\",\n          \"value\": 11447\n        },\n        \"device\": {\n          \"hex\": \"0210\",\n          \"name\": \"L830-EB\",\n          \"value\": 528\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"3.33\",\n          \"value\": 0\n        },\n        \"serial\": \"004999010640000\",\n        \"model\": \"FIBOCOM L830-EB\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.1\",\n        \"sysfs_bus_id\": \"1-7:1.1\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"00ef\",\n            \"name\": \"miscellaneous\",\n            \"value\": 239\n          },\n          \"device_subclass\": {\n            \"hex\": \"0002\",\n            \"name\": \"comm\",\n            \"value\": 2\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"000a\",\n            \"name\": \"data\",\n            \"value\": 10\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 2,\n          \"interface_number\": 1,\n          \"interface_alternate_setting\": 1,\n          \"interface_association\": {\n            \"function_class\": {\n              \"hex\": \"0002\",\n              \"name\": \"comm\",\n              \"value\": 2\n            },\n            \"function_subclass\": {\n              \"hex\": \"000e\",\n              \"name\": \"video\",\n              \"value\": 14\n            },\n            \"function_protocol\": 0,\n            \"interface_count\": 2,\n            \"first_interface\": 0\n          }\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"cdc_mbim\",\n        \"driver_module\": \"cdc_mbim\",\n        \"drivers\": [\"cdc_mbim\"],\n        \"driver_modules\": [\"cdc_mbim\"],\n        \"module_alias\": \"usb:v2CB7p0210d0333dcEFdsc02dp01ic0Aisc00ip02in01\"\n      }\n    ],\n    \"usb_controller\": [\n      {\n        \"index\": 31,\n        \"attached_to\": 0,\n        \"class_list\": [\"usb_controller\", \"pci\"],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 20\n        },\n        \"base_class\": {\n          \"hex\": \"000c\",\n          \"name\": \"Serial bus controller\",\n          \"value\": 12\n        },\n        \"sub_class\": {\n          \"hex\": \"0003\",\n          \"name\": \"USB Controller\",\n          \"value\": 3\n        },\n        \"pci_interface\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"vendor\": {\n          \"hex\": \"8086\",\n          \"name\": \"Intel Corporation\",\n          \"value\": 32902\n        },\n        \"sub_vendor\": {\n          \"hex\": \"17aa\",\n          \"value\": 6058\n        },\n        \"device\": {\n          \"hex\": \"9ded\",\n          \"value\": 40429\n        },\n        \"sub_device\": {\n          \"hex\": \"2279\",\n          \"value\": 8825\n        },\n        \"revision\": {\n          \"hex\": \"0030\",\n          \"value\": 48\n        },\n        \"model\": \"Intel USB Controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:14.0\",\n        \"sysfs_bus_id\": \"0000:00:14.0\",\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 48\n        },\n        \"driver\": \"xhci_hcd\",\n        \"driver_module\": \"xhci_pci\",\n        \"drivers\": [\"xhci_hcd\"],\n        \"driver_modules\": [\"xhci_pci\"],\n        \"module_alias\": \"pci:v00008086d00009DEDsv000017AAsd00002279bc0Csc03i30\"\n      }\n    ]\n  },\n  \"smbios\": {\n    \"bios\": {\n      \"handle\": 17,\n      \"vendor\": \"LENOVO\",\n      \"version\": \"N2IETA5W (1.83 )\",\n      \"date\": \"06/20/2024\",\n      \"features\": [\n        \"PCI supported\",\n        \"PnP supported\",\n        \"BIOS flashable\",\n        \"BIOS shadowing allowed\",\n        \"CD boot supported\",\n        \"Selectable boot supported\",\n        \"EDD spec supported\",\n        \"720kB Floppy supported\",\n        \"Print Screen supported\",\n        \"8042 Keyboard Services supported\",\n        \"Serial Services supported\",\n        \"Printer Services supported\",\n        \"CGA/Mono Video supported\",\n        \"ACPI supported\",\n        \"USB Legacy supported\",\n        \"BIOS Boot Spec supported\"\n      ],\n      \"start_address\": \"0xe0000\",\n      \"rom_size\": 16777216\n    },\n    \"board\": {\n      \"handle\": 19,\n      \"manufacturer\": \"LENOVO\",\n      \"product\": \"20N2S00600\",\n      \"version\": \"SDK0J40697 WIN\",\n      \"board_type\": {\n        \"hex\": \"000a\",\n        \"name\": \"Motherboard\",\n        \"value\": 10\n      },\n      \"features\": [\"Hosting Board\", \"Replaceable\"],\n      \"location\": \"Not Available\",\n      \"chassis\": 0\n    },\n    \"cache\": [\n      {\n        \"handle\": 12,\n        \"socket\": \"L1 Cache\",\n        \"size_max\": 256,\n        \"size_current\": 256,\n        \"speed\": 0,\n        \"mode\": {\n          \"hex\": \"0001\",\n          \"name\": \"Write Back\",\n          \"value\": 1\n        },\n        \"enabled\": true,\n        \"location\": {\n          \"hex\": \"0000\",\n          \"name\": \"Internal\",\n          \"value\": 0\n        },\n        \"socketed\": false,\n        \"level\": 0,\n        \"ecc\": {\n          \"hex\": \"0004\",\n          \"name\": \"Parity\",\n          \"value\": 4\n        },\n        \"cache_type\": {\n          \"hex\": \"0005\",\n          \"name\": \"Unified\",\n          \"value\": 5\n        },\n        \"associativity\": {\n          \"hex\": \"0007\",\n          \"name\": \"8-way Set-Associative\",\n          \"value\": 7\n        },\n        \"sram_type_current\": [\"Synchronous\"],\n        \"sram_type_supported\": [\"Synchronous\"]\n      },\n      {\n        \"handle\": 13,\n        \"socket\": \"L2 Cache\",\n        \"size_max\": 1024,\n        \"size_current\": 1024,\n        \"speed\": 0,\n        \"mode\": {\n          \"hex\": \"0001\",\n          \"name\": \"Write Back\",\n          \"value\": 1\n        },\n        \"enabled\": true,\n        \"location\": {\n          \"hex\": \"0000\",\n          \"name\": \"Internal\",\n          \"value\": 0\n        },\n        \"socketed\": false,\n        \"level\": 1,\n        \"ecc\": {\n          \"hex\": \"0005\",\n          \"name\": \"Single-bit\",\n          \"value\": 5\n        },\n        \"cache_type\": {\n          \"hex\": \"0005\",\n          \"name\": \"Unified\",\n          \"value\": 5\n        },\n        \"associativity\": {\n          \"hex\": \"0005\",\n          \"name\": \"4-way Set-Associative\",\n          \"value\": 5\n        },\n        \"sram_type_current\": [\"Synchronous\"],\n        \"sram_type_supported\": [\"Synchronous\"]\n      },\n      {\n        \"handle\": 14,\n        \"socket\": \"L3 Cache\",\n        \"size_max\": 8192,\n        \"size_current\": 8192,\n        \"speed\": 0,\n        \"mode\": {\n          \"hex\": \"0001\",\n          \"name\": \"Write Back\",\n          \"value\": 1\n        },\n        \"enabled\": true,\n        \"location\": {\n          \"hex\": \"0000\",\n          \"name\": \"Internal\",\n          \"value\": 0\n        },\n        \"socketed\": false,\n        \"level\": 2,\n        \"ecc\": {\n          \"hex\": \"0006\",\n          \"name\": \"Multi-bit\",\n          \"value\": 6\n        },\n        \"cache_type\": {\n          \"hex\": \"0005\",\n          \"name\": \"Unified\",\n          \"value\": 5\n        },\n        \"associativity\": {\n          \"hex\": \"0008\",\n          \"name\": \"16-way Set-Associative\",\n          \"value\": 8\n        },\n        \"sram_type_current\": [\"Synchronous\"],\n        \"sram_type_supported\": [\"Synchronous\"]\n      }\n    ],\n    \"chassis\": [\n      {\n        \"handle\": 20,\n        \"manufacturer\": \"LENOVO\",\n        \"version\": \"None\",\n        \"chassis_type\": {\n          \"hex\": \"000a\",\n          \"name\": \"Notebook\",\n          \"value\": 10\n        },\n        \"lock_present\": false,\n        \"bootup_state\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"power_state\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"thermal_state\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"security_state\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"oem\": \"0x0\"\n      }\n    ],\n    \"config\": {\n      \"handle\": 42\n    },\n    \"group_associations\": [\n      {\n        \"handle\": 1,\n        \"name\": \"Intel(R) Silicon View Technology\",\n        \"handles\": [0]\n      },\n      {\n        \"handle\": 51,\n        \"power\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disabled\",\n          \"value\": 0\n        },\n        \"keyboard\": {\n          \"hex\": \"0002\",\n          \"name\": \"Not Implemented\",\n          \"value\": 2\n        },\n        \"admin\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disabled\",\n          \"value\": 0\n        },\n        \"reset\": {\n          \"hex\": \"0002\",\n          \"name\": \"Not Implemented\",\n          \"value\": 2\n        }\n      },\n      {\n        \"handle\": 61,\n        \"name\": \"$MEI\",\n        \"handles\": [0]\n      }\n    ],\n    \"language\": [\n      {\n        \"handle\": 43,\n        \"languages\": [\"en-US\"]\n      }\n    ],\n    \"memory_array\": [\n      {\n        \"handle\": 2,\n        \"location\": {\n          \"hex\": \"0003\",\n          \"name\": \"Motherboard\",\n          \"value\": 3\n        },\n        \"usage\": {\n          \"hex\": \"0003\",\n          \"name\": \"System memory\",\n          \"value\": 3\n        },\n        \"ecc\": {\n          \"hex\": \"0003\",\n          \"name\": \"None\",\n          \"value\": 3\n        },\n        \"max_size\": \"0x1800000\",\n        \"error_handle\": 65534,\n        \"slots\": 2\n      }\n    ],\n    \"memory_array_mapped_address\": [\n      {\n        \"handle\": 5,\n        \"array_handle\": 2,\n        \"start_address\": \"0x0\",\n        \"end_address\": \"0x600000000\",\n        \"part_width\": 2\n      }\n    ],\n    \"memory_device\": [\n      {\n        \"handle\": 3,\n        \"location\": \"ChannelA-DIMM0\",\n        \"bank_location\": \"BANK 0\",\n        \"manufacturer\": \"SK Hynix\",\n        \"part_number\": \"HMAB2GS6AMR6N-XN\",\n        \"array_handle\": 2,\n        \"error_handle\": 65534,\n        \"width\": 64,\n        \"ecc_bits\": 0,\n        \"size\": 16777216,\n        \"form_factor\": {\n          \"hex\": \"000d\",\n          \"name\": \"SODIMM\",\n          \"value\": 13\n        },\n        \"set\": 0,\n        \"memory_type\": {\n          \"hex\": \"001a\",\n          \"name\": \"Other\",\n          \"value\": 26\n        },\n        \"memory_type_details\": [\"Synchronous\"],\n        \"speed\": 3200\n      },\n      {\n        \"handle\": 4,\n        \"location\": \"ChannelB-DIMM0\",\n        \"bank_location\": \"BANK 2\",\n        \"manufacturer\": \"SK Hynix\",\n        \"part_number\": \"HMA81GS6DJR8N-XN\",\n        \"array_handle\": 2,\n        \"error_handle\": 65534,\n        \"width\": 64,\n        \"ecc_bits\": 0,\n        \"size\": 8388608,\n        \"form_factor\": {\n          \"hex\": \"000d\",\n          \"name\": \"SODIMM\",\n          \"value\": 13\n        },\n        \"set\": 0,\n        \"memory_type\": {\n          \"hex\": \"001a\",\n          \"name\": \"Other\",\n          \"value\": 26\n        },\n        \"memory_type_details\": [\"Synchronous\"],\n        \"speed\": 3200\n      }\n    ],\n    \"memory_error\": [\n      {\n        \"handle\": 53,\n        \"error_type\": {\n          \"hex\": \"0003\",\n          \"name\": \"OK\",\n          \"value\": 3\n        },\n        \"granularity\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"operation\": {\n          \"hex\": \"0002\",\n          \"name\": \"Unknown\",\n          \"value\": 2\n        },\n        \"syndrome\": 0,\n        \"array_address\": \"0x80000000\",\n        \"device_address\": \"0x80000000\",\n        \"range\": 2147483648\n      }\n    ],\n    \"pointing_device\": [\n      {\n        \"handle\": 54,\n        \"mouse_type\": {\n          \"hex\": \"0005\",\n          \"name\": \"Track Point\",\n          \"value\": 5\n        },\n        \"interface\": {\n          \"hex\": \"0004\",\n          \"name\": \"PS/2\",\n          \"value\": 4\n        },\n        \"buttons\": 3\n      },\n      {\n        \"handle\": 55,\n        \"mouse_type\": {\n          \"hex\": \"0007\",\n          \"name\": \"Touch Pad\",\n          \"value\": 7\n        },\n        \"interface\": {\n          \"hex\": \"0004\",\n          \"name\": \"PS/2\",\n          \"value\": 4\n        },\n        \"buttons\": 2\n      }\n    ],\n    \"port_connector\": [\n      {\n        \"handle\": 21,\n        \"port_type\": {\n          \"hex\": \"0010\",\n          \"name\": \"USB\",\n          \"value\": 16\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"0012\",\n          \"name\": \"Access Bus [USB]\",\n          \"value\": 18\n        },\n        \"external_reference_designator\": \"USB 1\"\n      },\n      {\n        \"handle\": 22,\n        \"port_type\": {\n          \"hex\": \"0010\",\n          \"name\": \"USB\",\n          \"value\": 16\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"0012\",\n          \"name\": \"Access Bus [USB]\",\n          \"value\": 18\n        },\n        \"external_reference_designator\": \"USB 2\"\n      },\n      {\n        \"handle\": 23,\n        \"port_type\": {\n          \"hex\": \"0010\",\n          \"name\": \"USB\",\n          \"value\": 16\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"0012\",\n          \"name\": \"Access Bus [USB]\",\n          \"value\": 18\n        },\n        \"external_reference_designator\": \"USB 3\"\n      },\n      {\n        \"handle\": 24,\n        \"port_type\": {\n          \"hex\": \"0010\",\n          \"name\": \"USB\",\n          \"value\": 16\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"0012\",\n          \"name\": \"Access Bus [USB]\",\n          \"value\": 18\n        },\n        \"external_reference_designator\": \"USB 4\"\n      },\n      {\n        \"handle\": 30,\n        \"port_type\": {\n          \"hex\": \"001f\",\n          \"name\": \"Network Port\",\n          \"value\": 31\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"000b\",\n          \"name\": \"RJ-45\",\n          \"value\": 11\n        },\n        \"external_reference_designator\": \"Ethernet\"\n      },\n      {\n        \"handle\": 33,\n        \"port_type\": {\n          \"hex\": \"001c\",\n          \"name\": \"Video Port\",\n          \"value\": 28\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"00ff\",\n          \"name\": \"Other\",\n          \"value\": 255\n        },\n        \"external_reference_designator\": \"Hdmi1\"\n      },\n      {\n        \"handle\": 38,\n        \"port_type\": {\n          \"hex\": \"001d\",\n          \"name\": \"Audio Port\",\n          \"value\": 29\n        },\n        \"internal_reference_designator\": \"Not Available\",\n        \"external_connector_type\": {\n          \"hex\": \"001f\",\n          \"name\": \"Mini-jack [headphones]\",\n          \"value\": 31\n        },\n        \"external_reference_designator\": \"Headphone/Microphone Combo Jack1\"\n      }\n    ],\n    \"processor\": [\n      {\n        \"handle\": 15,\n        \"socket\": \"U3E1\",\n        \"socket_type\": {\n          \"hex\": \"003c\",\n          \"name\": \"Other\",\n          \"value\": 60\n        },\n        \"socket_populated\": true,\n        \"manufacturer\": \"Intel(R) Corporation\",\n        \"version\": \"Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz\",\n        \"part\": \"None\",\n        \"processor_type\": {\n          \"hex\": \"0003\",\n          \"name\": \"CPU\",\n          \"value\": 3\n        },\n        \"processor_family\": {\n          \"hex\": \"00c6\",\n          \"name\": \"Other\",\n          \"value\": 198\n        },\n        \"processor_status\": {\n          \"hex\": \"0001\",\n          \"name\": \"Enabled\",\n          \"value\": 1\n        },\n        \"clock_ext\": 100,\n        \"clock_max\": 2000,\n        \"cache_handle_l1\": 12,\n        \"cache_handle_l2\": 13,\n        \"cache_handle_l3\": 14\n      }\n    ],\n    \"slot\": [\n      {\n        \"handle\": 40,\n        \"designation\": \"Media Card Slot\",\n        \"slot_type\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"bus_width\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"usage\": {\n          \"hex\": \"0003\",\n          \"name\": \"Available\",\n          \"value\": 3\n        },\n        \"length\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"id\": 0,\n        \"features\": [\"Hot-Plug\"]\n      },\n      {\n        \"handle\": 41,\n        \"designation\": \"SimCard Slot\",\n        \"slot_type\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"bus_width\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"usage\": {\n          \"hex\": \"0003\",\n          \"name\": \"Available\",\n          \"value\": 3\n        },\n        \"length\": {\n          \"hex\": \"0001\",\n          \"name\": \"Other\",\n          \"value\": 1\n        },\n        \"id\": 0\n      }\n    ],\n    \"system\": {\n      \"handle\": 18,\n      \"manufacturer\": \"LENOVO\",\n      \"product\": \"20N2S00600\",\n      \"version\": \"ThinkPad T490\",\n      \"wake_up\": {\n        \"hex\": \"0006\",\n        \"name\": \"Power Switch\",\n        \"value\": 6\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "machines/kartoffel/configuration.nix",
    "content": "# Configuration for kartoffel\n{ pkgs, lib, ... }:\n{\n\n  imports = [\n    ./hardware-configuration.nix\n    ./retiolum.nix\n  ];\n\n  networking.hostName = \"kartoffel\";\n\n  pinpox.defaults.lvm-grub.enable = true;\n\n  # Encrypted drive to be mounted by the bootloader. Path of the device will\n  # have to be changed for each install.\n  boot.initrd.luks.devices = {\n    root = {\n      # Get UUID from blkid /dev/sda2\n      device = \"/dev/disk/by-uuid/608e0e77-eea4-4dc4-b88d-76cc63e4488b\";\n      preLVM = true;\n      allowDiscards = true;\n    };\n  };\n\n  # Video driver for nvidia graphics card\n  hardware.nvidia.open = false;\n  boot.blacklistedKernelModules = [ \"nouveau\" ];\n  services.greetd.settings.default_session.command =\n    lib.mkForce \"${pkgs.tuigreet}/bin/tuigreet --time --cmd 'sway --unsupported-gpu'\";\n\n  boot.supportedFilesystems = {\n    btrfs = true;\n    zfs = true;\n  };\n\n}\n"
  },
  {
    "path": "machines/kartoffel/hardware-configuration.nix",
    "content": "# Do not modify this file!  It was generated by ‘nixos-generate-config’\n# and may be overwritten by future invocations.  Please make changes\n# to /etc/nixos/configuration.nix instead.\n{\n  config,\n  lib,\n  pkgs,\n  modulesPath,\n  ...\n}:\n\n{\n  imports = [ (modulesPath + \"/installer/scan/not-detected.nix\") ];\n\n  boot.initrd.availableKernelModules = [\n    \"xhci_pci\"\n    \"ahci\"\n    \"usbhid\"\n    \"sd_mod\"\n  ];\n  boot.initrd.kernelModules = [ \"dm-snapshot\" ];\n  boot.kernelModules = [ \"kvm-amd\" ];\n  boot.extraModulePackages = [ ];\n\n  fileSystems.\"/\" = {\n    device = \"/dev/disk/by-uuid/8dcfb3f0-4dba-4c32-af96-84024706ff76\";\n    fsType = \"ext4\";\n  };\n\n  fileSystems.\"/boot\" = {\n    device = \"/dev/disk/by-uuid/5D7C-69F9\";\n    fsType = \"vfat\";\n  };\n\n  swapDevices = [ { device = \"/dev/disk/by-uuid/0f369649-cdbc-4a34-82dc-9f442c445c53\"; } ];\n\n  powerManagement.cpuFreqGovernor = lib.mkDefault \"performance\";\n}\n"
  },
  {
    "path": "machines/kartoffel/retiolum.nix",
    "content": "{\n  config,\n  retiolum,\n  ...\n}:\n{\n\n  imports = [ retiolum.nixosModules.retiolum ];\n\n  networking.retiolum = {\n    ipv4 = \"10.243.100.100\";\n    ipv6 = \"42:0:3c46:519d:1696:f464:9756:8727\";\n  };\n\n  networking.retiolum.nodename = \"ahorn\";\n\n  clan.core.vars.generators.\"retiolum\" = {\n    prompts.rsa_priv.persist = true;\n    prompts.ed25519_priv.persist = true;\n  };\n\n  services.tinc.networks.retiolum = {\n    rsaPrivateKeyFile = config.clan.core.vars.generators.\"retiolum\".files.\"rsa_priv\".path;\n    ed25519PrivateKeyFile = config.clan.core.vars.generators.\"retiolum\".files.\"ed25519_priv\".path;\n  };\n}\n"
  },
  {
    "path": "machines/kfbox/configuration.nix",
    "content": "{\n  aoe-taunt-discord-bot,\n  config,\n  go-karma-bot,\n\n  pkgs,\n  lib,\n  ...\n}:\nlet\n  pinpox-utils = import ../../utils { inherit pkgs lib; };\nin\n{\n\n  # Build on machine executing the clan\n  clan.core.networking.buildHost = \"pinpox@localhost\";\n\n  networking.interfaces.ens3 = {\n    ipv6.addresses = [\n      {\n        address = \"2a03:4000:7:4e0::\";\n        prefixLength = 64;\n      }\n    ];\n  };\n\n  clan.core.networking.targetHost = \"46.38.242.17\";\n\n  services.logind.settings.Login.RuntimeDirectorySize = \"20G\";\n\n  imports = [\n    ./retiolum.nix\n    ./hardware-configuration.nix\n    go-karma-bot.nixosModules.go-karma-bot\n    aoe-taunt-discord-bot.nixosModules.aoe-taunt-discord-bot\n  ];\n\n  systemd.services.NetworkManager-wait-online.serviceConfig.ExecStart = [\n    \"${pkgs.networkmanager}/bin/nm-online -q\"\n  ];\n\n  # Karmabot for IRC channel\n  clan.core.vars.generators.\"go-karma-bot\" = pinpox-utils.mkEnvGenerator [\n    \"IRC_BOT_SERVER\"\n    \"IRC_BOT_CHANNEL\"\n    \"IRC_BOT_NICK\"\n    \"IRC_BOT_PASS\"\n  ];\n\n  services.go-karma-bot = {\n    enable = true;\n    environmentFile = config.clan.core.vars.generators.\"go-karma-bot\".files.\"envfile\".path;\n  };\n\n  systemd.services.go-karma-bot.serviceConfig = {\n    RestartSec = 5;\n    Restart = \"on-abnormal\";\n  };\n\n  # Discord AoE2 taunt bot\n  clan.core.vars.generators.\"aoe-taunt-discord-bot\" = {\n    prompts.discord_token.persist = true;\n  };\n\n  services.aoe-taunt-discord-bot = {\n    enable = true;\n    discordTokenFile =\n      config.clan.core.vars.generators.\"aoe-taunt-discord-bot\".files.\"discord_token\".path;\n  };\n\n  networking.hostName = \"kfbox\";\n\n  pinpox = {\n    services = {\n      immich.enable = false;\n      radio.enable = true;\n      jitsi-matrix-presence.enable = true;\n      hedgedoc.enable = false;\n      screego.enable = true;\n      miniflux.enable = true;\n      kf-homepage.enable = true;\n      calibre-web.enable = false;\n      gitea.enable = true;\n      owncast.enable = false;\n      vikunja.enable = false;\n      wastebin.enable = true;\n\n    };\n  };\n\n  programs.ssh.startAgent = false;\n  services.qemuGuest.enable = true;\n\n  fileSystems.\"/\" = {\n    device = \"/dev/disk/by-label/nixos\";\n    fsType = \"ext4\";\n    autoResize = true;\n  };\n\n  fileSystems.\"/tmp\" = {\n    fsType = \"tmpfs\";\n    device = \"tmpfs\";\n    options = [\n      \"nosuid\"\n      \"nodev\"\n      \"relatime\"\n      \"size=14G\"\n    ];\n  };\n\n  boot.growPartition = true;\n  boot.kernelParams = [ \"console=ttyS0\" ];\n  boot.loader.grub.device = \"/dev/sda\";\n  boot.loader.timeout = 0;\n\n  # Block anything that is not HTTP(s) or SSH.\n  networking.firewall = {\n    enable = true;\n    allowPing = true;\n    allowedTCPPorts = [\n      # 8989\n      80\n      443\n      22\n      7777\n    ];\n  };\n\n  # virtualisation.docker.enable = true;\n  # services.traccar = {\n  #   enable = true;\n  #   # config.xml configuration as a Nix attribute set. This option is ignored if settingsFile is set.\n  #   #\n  #   # Nested attributes get translated to a properties entry in the traccar configuration. For instance: mail.smtp.port = \"25\" results in the following entry: <entry key='mail.smtp.port'>25</entry>\n  #\n  #   settings = {\n  #     h02.port = \"8989\";\n  #     filter = {\n  #       # This filters out positions within 5 meters of the previous position.\n  #       enable = \"true\";\n  #       duplicate = \"true\";\n  #       distance = \"5\";\n  #     };\n  #   };\n  #\n  # };\n\n  services.caddy = {\n\n    enable = true;\n\n    virtualHosts = {\n\n      # \"track.0cx.de\".extraConfig = \"reverse_proxy 127.0.0.1:8082\";\n\n      # \"track.0cx.de\".extraConfig = \"\n      # reverse_proxy 127.0.0.1:8080 {\n      #         header_up Upgrade {http.request.header.upgrade}\n      #         header_up Connection {http.request.header.connection}\n      # }\n      # \";\n\n      # \"transfer.0cx.de\".extraConfig = \"reverse_proxy 127.0.0.1:6767\";\n\n      # \"pads.0cx.de\".extraConfig = \"reverse_proxy 127.0.0.1:3000\";\n\n      # \"photos-api.0cx.de\".extraConfig = \"reverse_proxy 127.0.0.1:8080\";\n\n      # \"matrixpresence.0cx.de\".extraConfig = \"reverse_proxy 127.0.0.1:8227\";\n\n      \"paste.0cx.de\".extraConfig =\n        \"reverse_proxy ${config.services.wastebin.settings.WASTEBIN_ADDRESS_PORT}\";\n    };\n  };\n}\n"
  },
  {
    "path": "machines/kfbox/hardware-configuration.nix",
    "content": "{ modulesPath, ... }:\n{\n  imports = [ (modulesPath + \"/profiles/qemu-guest.nix\") ];\n}\n"
  },
  {
    "path": "machines/kfbox/retiolum.nix",
    "content": "{\n  config,\n  retiolum,\n  ...\n}:\n{\n\n  imports = [ retiolum.nixosModules.retiolum ];\n\n  networking.retiolum = {\n    ipv4 = \"10.243.100.102\";\n    ipv6 = \"42:0:3c46:3ae6:90a8:b220:e772:8a5c\";\n  };\n\n  clan.core.vars.generators.\"retiolum\" = {\n    prompts.rsa_priv.persist = true;\n    prompts.ed25519_priv.persist = true;\n  };\n\n  services.tinc.networks.retiolum = {\n    rsaPrivateKeyFile = config.clan.core.vars.generators.\"retiolum\".files.\"rsa_priv\".path;\n    ed25519PrivateKeyFile = config.clan.core.vars.generators.\"retiolum\".files.\"ed25519_priv\".path;\n  };\n}\n"
  },
  {
    "path": "machines/kiwi/configuration.nix",
    "content": "{\n  nixos-hardware,\n  lib,\n  ...\n}:\n{\n\n  imports = [\n    ./ollama-local.nix\n    ./opencrow-geninf.nix\n    ./disko-config-btrfs.nix\n    # ./framework.nix\n    nixos-hardware.nixosModules.framework-amd-ai-300-series\n  ];\n\n  # `boltctl`, to authorize Thunderbolt docs (e.g. lenovo dock)\n  services.hardware.bolt.enable = true;\n\n  # Trust all thunderbolt devices\n  # boot.kernelParams = [ \"thunderbolt.host_reset=0\" ];\n  # services.udev.extraRules = ''\n  #   ACTION==\"add\", SUBSYSTEM==\"thunderbolt\", ATTR{authorized}==\"0\", ATTR{authorized}=\"1\"\n  # '';\n\n  hardware = {\n    fw-fanctrl.enable = true;\n    rtl-sdr.enable = true;\n    amdgpu.opencl.enable = true;\n    xone.enable = true;\n  };\n\n  networking.hostName = \"kiwi\";\n\n  # Games\n  programs.steam.enable = true;\n  programs.gamemode.enable = true;\n\n  # For dual-boot\n  boot.loader.efi.canTouchEfiVariables = true;\n  boot.loader.grub.efiInstallAsRemovable = lib.mkForce false;\n\n  # Enable aarch64 emulation for cross-building ARM images\n  boot.binfmt.emulatedSystems = [ \"aarch64-linux\" ];\n\n  # Remap Caps Lock to Esc and vice versa\n  services.udev.extraHwdb = ''\n    evdev:atkbd:dmi:*\n      KEYBOARD_KEY_3a=esc      # Caps Lock -> Esc\n      KEYBOARD_KEY_01=capslock # Esc -> Caps Lock\n  '';\n}\n"
  },
  {
    "path": "machines/kiwi/disko-config-btrfs.nix",
    "content": "{\n\n  boot.growPartition = true;\n  boot.supportedFilesystems.btrfs = true;\n\n  services.btrfs.autoScrub = {\n    enable = true;\n    interval = \"weekly\";\n    # Defaults to all\n    # fileSystems = [ \"/\" ];\n  };\n\n  disko.devices = {\n    disk = {\n      main = {\n        type = \"disk\";\n        device = \"/dev/nvme0n1\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n\n            ESP = {\n              size = \"512M\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"umask=0077\" ];\n              };\n            };\n            windows = {\n              size = \"512G\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n              };\n            };\n            luks = {\n              size = \"100%\";\n              content = {\n                type = \"luks\";\n                name = \"crypted\";\n                # disable settings.keyFile if you want to use interactive password entry\n                #passwordFile = \"/tmp/secret.key\"; # Interactive\n                settings = {\n                  allowDiscards = true;\n                  # keyFile = \"/tmp/secret.key\";\n                };\n                # additionalKeyFiles = [ \"/tmp/additionalSecret.key\" ];\n                content = {\n                  type = \"btrfs\";\n                  extraArgs = [ \"-f\" ];\n                  subvolumes = {\n                    \"/root\" = {\n                      mountpoint = \"/\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/home\" = {\n                      mountpoint = \"/home\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/nix\" = {\n                      mountpoint = \"/nix\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/swap\" = {\n                      mountpoint = \"/.swapvol\";\n                      swap.swapfile.size = \"20M\";\n                    };\n                  };\n                };\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/kiwi/framework.nix",
    "content": "\n\n  config,\n  pkgs,\n  ...\n}:\n{\n\n  # For fingerprint support\n  # To enroll prints: `sudo fprint-enroll <username>`\n  services.fprintd.enable = true;\n\n  boot.extraModulePackages = with config.boot.kernelPackages; [ framework-laptop-kmod ];\n\n  # Module is not used for Framework EC but causes boot time error log.\n  # boot.blacklistedKernelModules = [ \"cros-usbpd-charger\" ];\n\n  # https://github.com/DHowett/framework-laptop-kmod?tab=readme-ov-file#usage\n  # boot.kernelModules = [\n  #   \"cros_ec\"\n  #   \"cros_ec_lpcs\"\n  # ];\n\n  # boot.kernelParams = [\n  # For Power consumption\n  # https://community.frame.work/t/linux-battery-life-tuning/6665/156\n  # \"nvme.noacpi=1\"\n  # ];\n\n  # Custom udev rules\n  # services.udev.extraRules = ''\n  #   # Fix headphone noise when on powersave\n  #   # https://community.frame.work/t/headphone-jack-intermittent-noise/5246/55\n  #   SUBSYSTEM==\"pci\", ATTR{vendor}==\"0x8086\", ATTR{device}==\"0xa0e0\", ATTR{power/control}=\"on\"\n  # '';\n\n  # Ethernet expansion card support\n  services.udev.extraRules = ''\n    ACTION==\"add\", SUBSYSTEM==\"usb\", ATTR{idVendor}==\"0bda\", ATTR{idProduct}==\"8156\", ATTR{power/autosuspend}=\"20\"\n  '';\n\n  environment.systemPackages = [\n    # This adds a patched ectool, to interact with the Embedded Controller\n    # Can be used to interact with leds from userspace, etc.\n    # Not part of a nixos release yet, so package only gets added if it exists.\n    pkgs.fw-ectool\n    pkgs.framework-tool\n  ];\n\n  # AMD has better battery life with PPD over TLP:\n  # https://community.frame.work/t/responded-amd-7040-sleep-states/38101/13\n  services.power-profiles-daemon.enable = true;\n  services.tlp.enable = false;\n\n  # Needed for desktop environments to detect/manage display brightness\n  hardware.sensor.iio.enable = true;\n\n  # TODO not sure if needed\n  # Deactivates light sensor?\n  # https://github.com/NixOS/nixpkgs/issues/171093\n  # https://wiki.archlinux.org/title/Framework_Laptop#Changing_the_brightness_of_the_monitor_does_not_work\n  hardware.acpilight.enable = true;\n}\n"
  },
  {
    "path": "machines/kiwi/ollama-local.nix",
    "content": "{ ... }:\nlet\n\n  vllmProvider = host: model: {\n    baseUrl = \"http://${host}:8000/v1\";\n    api = \"openai-completions\";\n    apiKey = \"dummy\";\n    compat = {\n      supportsDeveloperRole = false;\n      supportsReasoningEffort = false;\n    };\n    models = [\n      {\n        id = model;\n        reasoning = true;\n      }\n    ];\n  };\n\nin\n{\n  services.tailscale.enable = true;\n\n  # Pi providers via home-manager module\n  home-manager.users.pinpox.pinpox.programs.pi.providers = {\n    llama-cpp = {\n      baseUrl = \"http://127.0.0.1:8080/v1\";\n      api = \"openai-completions\";\n      apiKey = \"dummy\";\n      models = [\n        {\n          id = \"glm-4.7-flash\";\n          name = \"GLM-4.7 Flash (Local)\";\n          reasoning = true;\n          input = [ \"text\" ];\n          contextWindow = 32768;\n          maxTokens = 8192;\n        }\n      ];\n    };\n    vllm_1 = vllmProvider \"100.96.100.100\" \"openai/gpt-oss-120b\";\n    vllm_2 = vllmProvider \"100.96.100.101\" \"Qwen/Qwen3.6-35B-A3B\";\n    vllm_3 = vllmProvider \"100.96.100.102\" \"Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8\";\n    # Pull models with: OLLAMA_HOST=\"100.96.100.103:11434\" ollama pull <model>\n    ollama = {\n      baseUrl = \"http://100.96.100.103:11434/v1\";\n      api = \"openai-completions\";\n      apiKey = \"dummy\";\n      compat = {\n        supportsDeveloperRole = false;\n        supportsReasoningEffort = false;\n      };\n      models = [\n        {\n          id = \"codegemma:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-0528-q4_K_M\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-0528-q4_K_M_131072\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-0528-q4_K_M_16384\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-0528-q4_K_M_32768\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-0528-q4_K_M_65536\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-0528-q4_K_M_8192\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:671b-q4_K_M\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:70b\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-r1:latest\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-v3.1:671b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-v3.1:671b_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"deepseek-v3.1:671b_163840\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"devstral:24b-small-2505-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gemma3:27b-it-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"glm-4.7-flash:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"glm-5:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gpt-oss:120b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gpt-oss:120b_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gpt-oss:120b-cloud\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gpt-oss:20b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"hf.co/mradermacher/Qwen3.5-397B-A17B-GGUF:Qwen3.5-397B-A17B.Q3_K_M.gguf\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"hf.co/sebsigma/SemanticCite-Checker-Qwen3-4B:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"hf.co/sebsigma/SemanticCite-Refiner-Qwen3-1B:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"hf.co/unsloth/GLM-5-GGUF:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"hf.co/unsloth/MiniMax-M2.5-GGUF:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"jmorgan/bespoke-minicheck:7b-fp16\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"jmorgan/bespoke-minicheck:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama3.2:1b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama3.2:3b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama3.3:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:128x17b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:17b-maverick-128e-instruct-q4_K_M\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:17b-maverick-128e-instruct-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:maverick\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:maverick_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:maverick_262144\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:maverick_32768\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"llama4:scout\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"ministral-3:14b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mistral:7b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mistral:7b_32768\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mistral-nemo:12b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mistral-nemo:12b_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mistral-small3.2:24b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mixtral:8x22b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"mixtral:8x22b_65536\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"my-model:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"phi4:14b-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen2.5-coder:32b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen2.5-coder:32b-instruct-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3:235b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3:32b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3:32b_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3:32b-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3.5:397b-q3_k_m\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:30b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:30b-a3b-fp16\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:30b-a3b-q4_K_M\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:30b-a3b-q4_K_M_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:30b-a3b-q4_K_M_65536\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:30b-a3b-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:480b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder:480b_131072\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"SemanticCite-Checker-Qwen3-4B:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"SemanticCite-Refiner-Qwen3-1B:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"tinyllama:1.1b-chat-v1-q2_K\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3.5:122b\";\n          contextWindow = 256000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder-next:q8_0\";\n          contextWindow = 256000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-next:80b\";\n          contextWindow = 256000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"sinhang/QWen35-27b-q4_K_M-Claude\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"frob/qwen3.5:397b-a17b-q4_K_XL\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gemma4:31b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"gemma4:31b-cloud\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"kwangsuklee/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled-GGUF:latest\";\n          reasoning = true;\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3:235b-a22b-instruct-2507-q8_0\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3.5:0.8b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3.5:35b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3.6:latest\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-coder-next:q4_K_M\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-vl:235b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n        {\n          id = \"qwen3-vl:32b\";\n          contextWindow = 128000;\n          maxTokens = 32000;\n        }\n      ];\n    };\n  };\n\n}\n"
  },
  {
    "path": "machines/kiwi/opencrow-geninf.nix",
    "content": "{\n  distro,\n  opencrow,\n  pkgs,\n  ...\n}:\n{\n  imports = [\n    opencrow.nixosModules.default\n    distro.nixosModules.opencrow\n    distro.nixosModules.llama-swap\n  ];\n\n\n  # Local LLM server\n  services.llama-swap.enable = true;\n\n\n  services.opencrow-local = {\n    enable = true;\n    instanceName = \"geninf\";\n    piPackage = pkgs.pi;\n    llmUrl = \"http://127.0.0.1:8012\";\n    model = \"qwen2.5:0.5b\";\n    socketName = \"GenInf Crow\";\n    noctaliaPlugin = true;\n    noctaliaPluginUsers = [ \"pinpox\" ];\n    extraPackages = [\n      pkgs.pi\n      pkgs.curl\n      pkgs.jq\n    ];\n  };\n}\n"
  },
  {
    "path": "machines/limette/configuration.nix",
    "content": "{\n  pkgs,\n  nixos-hardware,\n  ...\n}:\n{\n\n  networking.hostName = \"limette\";\n\n  pinpox.defaults.lvm-grub.enable = true;\n  boot.growPartition = true;\n\n  hardware.enableRedistributableFirmware = true;\n  imports = [\n    nixos-hardware.nixosModules.lenovo-thinkpad-x230\n    ./disko-config-btrfs.nix\n  ];\n\n  disko.imageBuilder.extraDependencies = [ pkgs.kmod ];\n\n  hardware.graphics.extraPackages = [\n    pkgs.intel-media-driver # LIBVA_DRIVER_NAME=iHD\n  ];\n\n  boot.loader.efi.canTouchEfiVariables = false;\n}\n"
  },
  {
    "path": "machines/limette/disko-config-btrfs.nix",
    "content": "{\n\n  boot.growPartition = true;\n  boot.supportedFilesystems.btrfs = true;\n\n  disko.devices = {\n    disk = {\n      main = {\n        type = \"disk\";\n        device = \"/dev/sda\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n            ESP = {\n              size = \"512M\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"umask=0077\" ];\n              };\n            };\n            luks = {\n              size = \"100%\";\n              content = {\n                type = \"luks\";\n                name = \"crypted\";\n                # disable settings.keyFile if you want to use interactive password entry\n                #passwordFile = \"/tmp/secret.key\"; # Interactive\n                settings = {\n                  allowDiscards = true;\n                  # keyFile = \"/tmp/secret.key\";\n                };\n                # additionalKeyFiles = [ \"/tmp/additionalSecret.key\" ];\n                content = {\n                  type = \"btrfs\";\n                  extraArgs = [ \"-f\" ];\n                  subvolumes = {\n                    \"/root\" = {\n                      mountpoint = \"/\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/home\" = {\n                      mountpoint = \"/home\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/nix\" = {\n                      mountpoint = \"/nix\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/swap\" = {\n                      mountpoint = \"/.swapvol\";\n                      swap.swapfile.size = \"20M\";\n                    };\n                  };\n                };\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/limette/disko-config-zfs.nix",
    "content": "{ lib, config, ... }:\n{\n\n  # You may also find this setting useful to automatically set the latest compatible kernel:\n  boot.kernelPackages = lib.mkDefault config.boot.zfs.package.latestCompatibleLinuxPackages;\n  boot.supportedFilesystems.zfs = true;\n\n  services.zfs.autoSnapshot = {\n    enable = true;\n    frequent = 4; # 15 min\n    hourly = 24;\n    daily = 7;\n    weekly = 4;\n    monthly = 0;\n  };\n\n  disko.devices = {\n    disk = {\n      root = {\n        type = \"disk\";\n        device = \"/dev/sda\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n            ESP = {\n              size = \"1G\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"nofail\" ];\n              };\n            };\n            zfs = {\n              size = \"100%\";\n              content = {\n                type = \"zfs\";\n                pool = \"zroot\";\n              };\n            };\n          };\n        };\n      };\n    };\n    zpool = {\n      zroot = {\n        type = \"zpool\";\n        rootFsOptions = {\n          mountpoint = \"none\";\n          compression = \"zstd\";\n          acltype = \"posixacl\";\n          xattr = \"sa\";\n          \"com.sun:auto-snapshot\" = \"true\";\n        };\n        options.ashift = \"12\";\n        datasets = {\n          \"root\" = {\n            type = \"zfs_fs\";\n            options = {\n              encryption = \"aes-256-gcm\";\n              keyformat = \"passphrase\";\n              #keylocation = \"file:///tmp/secret.key\";\n              keylocation = \"prompt\";\n            };\n            mountpoint = \"/\";\n          };\n\n          \"root/nix\" = {\n            type = \"zfs_fs\";\n            options.mountpoint = \"/nix\";\n            mountpoint = \"/nix\";\n          };\n\n          \"root/home\" = {\n            type = \"zfs_fs\";\n            options.mountpoint = \"/home\";\n            mountpoint = \"/home\";\n          };\n\n          \"root/var/lib\" = {\n            type = \"zfs_fs\";\n            options.mountpoint = \"/var/lib\";\n            mountpoint = \"/var/lib\";\n          };\n\n          # \"root/tmp\" = {\n          #   type = \"zfs_fs\";\n          #   mountpoint = \"/tmp\";\n          #   options = {\n          #     mountpoint = \"/tmp\";\n          #     sync = \"disabled\";\n          #   };\n          # };\n\n          # README MORE: https://wiki.archlinux.org/title/ZFS#Swap_volume\n          # \"root/swap\" = {\n          #   type = \"zfs_volume\";\n          #   size = \"10M\";\n          #   content = {\n          #     type = \"swap\";\n          #   };\n          #   options = {\n          #     volblocksize = \"4096\";\n          #     compression = \"zle\";\n          #     logbias = \"throughput\";\n          #     sync = \"always\";\n          #     primarycache = \"metadata\";\n          #     secondarycache = \"none\";\n          #     \"com.sun:auto-snapshot\" = \"false\";\n          #   };\n          # };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/porree/blog.nix",
    "content": "with import <nixpkgs> { };\n\nstdenv.mkDerivation rec {\n  name = \"blog\";\n\n  # src = ./git-repos/hugo-website;\n  src = builtins.fetchurl { url = \"https://github.com/pinpox/hugo-website/archive/main.tar.gz\"; };\n\n  buildInputs = [ hugo ];\n  buildPhase = \"hugo\";\n  installPhase = \"cp -R public/ $out\";\n}\n"
  },
  {
    "path": "machines/porree/caddy.nix",
    "content": "{\n  config,\n  pkgs,\n  ...\n}:\nlet\n  caddy-authfiles = config.clan.core.vars.generators.\"caddy-basicauth\".files;\n\n  mkBasicAuthFiles = hosts: {\n\n    files = builtins.listToAttrs (\n      map (h: {\n        name = h;\n        value = { };\n      }) hosts\n      ++ (map (h: {\n        name = \"${h}.auth\";\n        value = {\n          owner = \"caddy\";\n          group = \"caddy\";\n        };\n      }) hosts)\n    );\n\n    prompts = builtins.listToAttrs (\n      map (h: {\n        name = h;\n        value = {\n          persist = true;\n          description = \"Username for ${h}\";\n        };\n      }) hosts\n    );\n\n    runtimeInputs = with pkgs; [\n      coreutils\n      caddy\n      xkcdpass\n    ];\n\n    validation.script = builtins.concatStringsSep \"\" hosts;\n\n    script = ''\n      mkdir -p $out\n    ''\n    + builtins.concatStringsSep \"\\n\" (\n      map (h: ''\n        xkcdpass -d- > $out/${h}\n        printf \"%s %s\" \"$(cat $prompts/${h})\" \"$(cat $out/${h} | caddy hash-password)\" \\\n          > $out/${h}.auth\n      '') hosts\n    );\n  };\nin\n{\n\n  clan.core.vars.generators.\"caddy-basicauth\" = mkBasicAuthFiles [\n    \"notify.pablo.tools\"\n  ];\n\n  systemd.services.caddy.restartTriggers = map (f: caddy-authfiles.${f}.path) (\n    builtins.attrNames caddy-authfiles\n  );\n  services.caddy = {\n    enable = true;\n\n    # Handle errors for all pages\n    # respond \"{err.status_code} {err.status_text}\"\n    extraConfig = ''\n      :443, :80 {\n        handle_errors {\n         respond * \"This page does not exist or is not for your eyes.\" {\n           close\n         }\n        }\n      }\n    '';\n\n    # The difference between {$ and {env. is that {$ is evaluated before Caddyfile\n    # parsing begins, and {env. is evaluated at runtime. This matters if your\n    # config is adapted in a different environment from which it is being run.\n    virtualHosts = {\n\n      # NIP-05 identity\n      \"pinpox.com\".extraConfig = ''\n        handle /.well-known/nostr.json {\n          header Content-Type application/json\n          header Access-Control-Allow-Origin *\n          root * ${./nostr}\n          rewrite * /nostr.json\n          file_server\n        }\n        respond \"nothing here\" 404\n      '';\n\n      # Homepage\n      \"pablo.tools\".extraConfig = ''\n        handle /.well-known/nostr.json {\n          header Content-Type application/json\n          header Access-Control-Allow-Origin *\n          root * ${./nostr}\n          rewrite * /nostr.json\n          file_server\n        }\n        root * /var/www/pablo-tools\n        file_server\n        encode zstd gzip\n      '';\n\n      # Homepage (dev) - protected by Authelia\n      \"beta.pablo.tools\".extraConfig = ''\n        forward_auth http://127.0.0.1:9091 {\n          uri /api/authz/forward-auth\n          copy_headers Remote-User Remote-Groups Remote-Name Remote-Email\n        }\n        root * /var/www/pablo-tools-beta\n        file_server\n        encode zstd gzip\n      '';\n\n      # Camera (read-only) stream - protected by Authelia\n      \"3dprint.pablo.tools\".extraConfig = ''\n        forward_auth http://127.0.0.1:9091 {\n          uri /api/authz/forward-auth\n          copy_headers Remote-User Remote-Groups Remote-Name Remote-Email\n        }\n        reverse_proxy 192.168.2.121:8081\n      '';\n\n      # Notifications API\n      \"notify.pablo.tools\".extraConfig = ''\n        reverse_proxy 127.0.0.1:11000\n        basic_auth {\n          import ${config.clan.core.vars.generators.\"caddy-basicauth\".files.\"notify.pablo.tools.auth\".path}\n        }\n      '';\n\n      # Home-assistant\n      \"home.pablo.tools\".extraConfig = \"reverse_proxy birne.wireguard:8123\";\n\n      # Navidrome\n      \"music.pablo.tools\".extraConfig = \"reverse_proxy birne.wireguard:4533\";\n\n      # Alertmanager\n      \"vpn.alerts.pablo.tools\".extraConfig = ''\n        @vpnonly {\n          remote_ip 192.168.0.0/16 172.168.7.0/16\n        }\n        reverse_proxy @vpnonly 127.0.0.1:9093\n      '';\n\n      # Prometheus\n      \"vpn.prometheus.pablo.tools\".extraConfig = ''\n        @vpnonly {\n          remote_ip 192.168.0.0/16 172.168.7.0/16\n        }\n        reverse_proxy @vpnonly 127.0.0.1:9090\n      '';\n\n      # ntfy\n      \"vpn.notify.pablo.tools\".extraConfig = ''\n        @vpnonly {\n          remote_ip 192.168.0.0/16 172.168.7.0/16\n        }\n        reverse_proxy @vpnonly 127.0.0.1:11000\n      '';\n\n      # Minio admin console\n      \"vpn.minio.pablo.tools\".extraConfig = ''\n        @vpnonly {\n          remote_ip 192.168.0.0/16 172.168.7.0/16\n        }\n        reverse_proxy @vpnonly birne.wireguard:9001\n      '';\n\n      # Minio s3 backend\n      \"vpn.s3.pablo.tools\".extraConfig = ''\n        @vpnonly {\n          remote_ip 192.168.0.0/16 172.168.7.0/16\n        }\n        reverse_proxy @vpnonly birne.wireguard:9000\n      '';\n    };\n  };\n}\n"
  },
  {
    "path": "machines/porree/configuration.nix",
    "content": "{\n  matrix-hook,\n  config,\n  pkgs,\n  alertmanager-ntfy,\n  pinpox-utils,\n  ...\n}:\n{\n\n  imports = [\n    ./hardware-configuration.nix\n    matrix-hook.nixosModule\n    alertmanager-ntfy.nixosModules.default\n    ./caddy.nix\n    # ./retiolum.nix\n    ../../modules/opencrow\n  ];\n\n  clan.core.networking.targetHost = \"94.16.108.229\";\n  networking.hostName = \"porree\";\n\n  networking.interfaces.ens3 = {\n    ipv6.addresses = [\n      {\n        address = \"2a03:4000:51:aa3::1\";\n        prefixLength = 64;\n      }\n    ];\n  };\n\n  clan.core.vars.generators.\"matrix-hook\" = pinpox-utils.mkEnvGenerator [ \"MX_TOKEN\" ];\n  clan.core.vars.generators.\"alertmanager-ntfy\" = pinpox-utils.mkEnvGenerator [\n    \"NTFY_USER\"\n    \"NTFY_PASS\"\n  ];\n\n  # Per-user authelia password generators are now auto-created by the\n  # authelia clan service based on auth.user exports from user instances.\n\n  # OIDC client secret for miniflux (generated here, shared to kfbox).\n  # client_secret      → raw value for the miniflux OIDC client side\n  # client_secret_hash → argon2 hash for the Authelia client_secret_file\n  clan.core.vars.generators.\"miniflux-oidc\" = {\n    share = true;\n    files.client_secret.owner = \"authelia-main\";\n    files.client_secret_hash.owner = \"authelia-main\";\n    runtimeInputs = with pkgs; [\n      coreutils\n      openssl\n      authelia\n      gnused\n    ];\n    script = ''\n      mkdir -p $out\n      openssl rand -hex 32 > $out/client_secret\n      authelia crypto hash generate argon2 --password \"$(cat $out/client_secret)\" \\\n        | sed 's/^Digest: //' > $out/client_secret_hash\n    '';\n  };\n\n  # OIDC client secret for forgejo (generated here, shared to forgejo host).\n  # client_secret      → raw value for the forgejo OIDC client side\n  # client_secret_hash → argon2 hash for the Authelia client_secret_file\n  clan.core.vars.generators.\"forgejo-oidc\" = {\n    share = true;\n    files.client_secret.owner = \"authelia-main\";\n    files.client_secret_hash.owner = \"authelia-main\";\n    runtimeInputs = with pkgs; [\n      coreutils\n      openssl\n      authelia\n      gnused\n    ];\n    script = ''\n      mkdir -p $out\n      openssl rand -hex 32 > $out/client_secret\n      authelia crypto hash generate argon2 --password \"$(cat $out/client_secret)\" \\\n        | sed 's/^Digest: //' > $out/client_secret_hash\n    '';\n  };\n\n  services.qemuGuest.enable = true;\n  services.tailscale.enable = true;\n\n  fileSystems.\"/\" = {\n    device = \"/dev/disk/by-label/nixos\";\n    fsType = \"ext4\";\n    autoResize = true;\n  };\n\n  # Block anything that is not HTTP(s) or SSH.\n  networking.firewall = {\n    enable = true;\n    allowPing = true;\n    allowedTCPPorts = [\n      80\n      443\n      22\n    ];\n    allowedUDPPorts = [ 51820 ];\n\n    interfaces.wg-clan.allowedTCPPorts = [\n      2812\n      8086 # InfluxDB\n    ];\n  };\n\n  boot.growPartition = true;\n  boot.kernelParams = [ \"console=ttyS0\" ];\n  boot.loader.grub.device = \"/dev/sda\";\n  boot.loader.timeout = 0;\n\n  programs.ssh.startAgent = false;\n\n  services.alertmanager-ntfy = {\n    enable = true;\n    httpAddress = \"localhost\";\n    httpPort = \"9099\";\n    ntfyTopic = \"https://push.pablo.tools/pinpox_alertmanager\";\n    ntfyPriority = \"default\";\n    envFile = \"${config.clan.core.vars.generators.\"alertmanager-ntfy\".files.\"envfile\".path}\";\n  };\n\n  pinpox = {\n\n    services = {\n      opencrow.enable = true;\n      # Authelia is now a clan service (clan-service-modules/authelia).\n      # Configuration is in inventory.nix.\n      vaultwarden.enable = true;\n      ntfy-sh.enable = true;\n\n      matrix-hook = {\n        enable = true;\n        httpAddress = \"localhost\";\n        matrixHomeserver = \"https://matrix.org\";\n        matrixUser = \"@alertus-maximus:matrix.org\";\n        matrixRoom = \"!ilXTQgAfoBlNBuDmsz:matrix.org\";\n        envFile = \"${config.clan.core.vars.generators.\"matrix-hook\".files.\"envfile\".path}\";\n        msgTemplatePath = \"${matrix-hook.packages.\"x86_64-linux\".matrix-hook}/bin/message.html.tmpl\";\n      };\n\n      # Enable paperless-ngx document management\n      paperless.enable = true;\n\n      # Enable nextcloud configuration\n      nextcloud.enable = true;\n\n      # Enable OpenCloud (uses Authelia OIDC)\n      opencloud.enable = true;\n\n    };\n  };\n}\n"
  },
  {
    "path": "machines/porree/hardware-configuration.nix",
    "content": "{ modulesPath, ... }:\n{\n  imports = [ (modulesPath + \"/profiles/qemu-guest.nix\") ];\n}\n"
  },
  {
    "path": "machines/porree/nostr/nostr.json",
    "content": "{\n  \"names\": {\n    \"pinpox\": \"cb1250be2faac3f301b2e9f6abc8651fbd7de5623fa7cc9b98d260ff266ce878\"\n  }\n}\n"
  },
  {
    "path": "machines/porree/retiolum.nix",
    "content": "{\n  config,\n  retiolum,\n  ...\n}:\n{\n\n  imports = [ retiolum.nixosModules.retiolum ];\n\n  networking.retiolum.ipv4 = \"10.243.100.101\";\n  networking.retiolum.ipv6 = \"42:0:3c46:b51c:b34d:b7e1:3b02:8d24\";\n\n  clan.core.vars.generators.\"retiolum\" = {\n    prompts.rsa_priv.persist = true;\n    prompts.ed25519_priv.persist = true;\n  };\n\n  services.tinc.networks.retiolum = {\n    rsaPrivateKeyFile = config.clan.core.vars.generators.\"retiolum\".files.\"rsa_priv\".path;\n    ed25519PrivateKeyFile = config.clan.core.vars.generators.\"retiolum\".files.\"ed25519_priv\".path;\n  };\n}\n"
  },
  {
    "path": "machines/tanne/configuration.nix",
    "content": "{\n  nixos-hardware,\n  lib,\n  ...\n}:\n{\n\n  imports = [\n    ./disko-config-btrfs.nix\n    nixos-hardware.nixosModules.lenovo-thinkpad-t480s\n  ];\n\n  networking.hostName = \"tanne\";\n\n  programs.steam.enable = true;\n  programs.gamemode.enable = true;\n\n  # For dual-boot\n  boot.loader.efi.canTouchEfiVariables = true;\n  boot.loader.grub.efiInstallAsRemovable = lib.mkForce false;\n}\n"
  },
  {
    "path": "machines/tanne/disko-config-btrfs.nix",
    "content": "{\n\n  boot.growPartition = true;\n  boot.supportedFilesystems.btrfs = true;\n\n  services.btrfs.autoScrub = {\n    enable = true;\n    interval = \"weekly\";\n    # Defaults to all\n    # fileSystems = [ \"/\" ];\n  };\n\n  disko.devices = {\n    disk = {\n      main = {\n        type = \"disk\";\n        device = \"/dev/nvme0n1\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n\n            ESP = {\n              size = \"512M\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"umask=0077\" ];\n              };\n            };\n            windows = {\n              size = \"512G\";\n              type = \"EF00\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n              };\n            };\n            luks = {\n              size = \"100%\";\n              content = {\n                type = \"luks\";\n                name = \"crypted\";\n                # disable settings.keyFile if you want to use interactive password entry\n                #passwordFile = \"/tmp/secret.key\"; # Interactive\n                settings = {\n                  allowDiscards = true;\n                  # keyFile = \"/tmp/secret.key\";\n                };\n                # additionalKeyFiles = [ \"/tmp/additionalSecret.key\" ];\n                content = {\n                  type = \"btrfs\";\n                  extraArgs = [ \"-f\" ];\n                  subvolumes = {\n                    \"/root\" = {\n                      mountpoint = \"/\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/home\" = {\n                      mountpoint = \"/home\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/nix\" = {\n                      mountpoint = \"/nix\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/swap\" = {\n                      mountpoint = \"/.swapvol\";\n                      swap.swapfile.size = \"8G\";\n                    };\n                  };\n                };\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/traube/configuration.nix",
    "content": "{\n  pkgs,\n  lib,\n  ...\n}:\n{\n  imports = [ ];\n\n  nixpkgs.hostPlatform = \"aarch64-linux\";\n\n  # Lock CPU governor to performance mode (avoid frequency scaling latency)\n  powerManagement.cpuFreqGovernor = \"performance\";\n\n  boot.kernel.sysctl = {\n    \"vm.swappiness\" = 0;\n  };\n}\n"
  },
  {
    "path": "machines/traube/disko.nix",
    "content": "# ---\n# schema = \"btrfs-single-disk-subvolumes\"\n# [placeholders]\n# mainDisk = \"/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y\" \n# ---\n# This file was automatically generated!\n# CHANGING this configuration requires wiping and reinstalling the machine\n{\n  boot.loader.grub = {\n    efiInstallAsRemovable = true;\n    efiSupport = true;\n  };\n\n  disko.devices = {\n    disk = {\n      \"main\" = {\n        name = \"main-9bea948ae69a4a528dcdabbe9fe39bfb\";\n        device = \"/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y\";\n        type = \"disk\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n            \"boot\" = {\n              size = \"1M\";\n              type = \"EF02\"; # for grub MBR\n              priority = 1;\n            };\n            \"ESP\" = {\n              type = \"EF00\";\n              size = \"500M\";\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot\";\n                mountOptions = [ \"umask=0077\" ];\n              };\n            };\n            #\"swap\" = {\n            #  size = \"8G\"; # adjust\n            #  content = {\n            #    type = \"swap\";\n            #    discardPolicy = \"both\";\n            #  };\n            #};\n            \"root\" = {\n              size = \"100%\";\n              content = {\n                type = \"btrfs\";\n                extraArgs = [\n                  \"--force\"\n                  \"--label root\"\n                ];\n                subvolumes = {\n                  \"@root\" = {\n                    mountpoint = \"/\";\n                    mountOptions = [ ];\n                  };\n                  \"@nix\" = {\n                    mountpoint = \"/nix\";\n                    mountOptions = [\n                      \"compress=zstd\"\n                      \"noatime\"\n                    ];\n                  };\n                  \"@home\" = {\n                    mountpoint = \"/home\";\n                    mountOptions = [ \"compress=zstd\" ];\n                  };\n                };\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n\n  # Automatic local snapshots\n  # https://digint.ch/btrbk/doc/readme.html\n  #$ systemctl start btrbk-<instance>\n  services.btrbk = {\n    instances.\"nix\" = {\n      onCalendar = \"*/2:00\";\n      settings = {\n        subvolume = \"/nix\";\n        snapshot_create = \"onchange\";\n        snapshot_dir = \"/nix\";\n        snapshot_preserve = \"16h 7d 2w\";\n        snapshot_preserve_min = \"3d\";\n      };\n    };\n    instances.\"home\" = {\n      onCalendar = \"*/2:00\";\n      settings = {\n        subvolume = \"/home\";\n        snapshot_dir = \"/home\";\n        snapshot_preserve = \"16h 7d 3w 2m\";\n        snapshot_preserve_min = \"3d\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "machines/traube/facter.json",
    "content": "{\n  \"version\": 1,\n  \"system\": \"aarch64-linux\",\n  \"virtualisation\": \"none\",\n  \"uefi\": {\n    \"supported\": true,\n    \"platform_size\": 64\n  },\n  \"hardware\": {\n    \"bridge\": [\n      {\n        \"index\": 16,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"bridge\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 48,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"17cd\",\n          \"value\": 6093\n        },\n        \"device\": {\n          \"hex\": \"0100\",\n          \"value\": 256\n        },\n        \"model\": \"PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:30/0000:30:00.0\",\n        \"sysfs_bus_id\": \"0000:30:00.0\",\n        \"sysfs_iommu_group_id\": 1,\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 7,\n          \"header_type\": 1,\n          \"secondary_bus\": 49,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\n          \"pcieport\"\n        ],\n        \"driver_modules\": [\n          \"pcieportdrv\"\n        ],\n        \"module_alias\": \"pci:v000017CDd00000100sv00000000sd00000000bc06sc04i00\"\n      },\n      {\n        \"index\": 18,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"bridge\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"17cd\",\n          \"value\": 6093\n        },\n        \"device\": {\n          \"hex\": \"0100\",\n          \"value\": 256\n        },\n        \"model\": \"PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:00.0\",\n        \"sysfs_bus_id\": \"0000:00:00.0\",\n        \"sysfs_iommu_group_id\": 2,\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 7,\n          \"header_type\": 1,\n          \"secondary_bus\": 1,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\n          \"pcieport\"\n        ],\n        \"driver_modules\": [\n          \"pcieportdrv\"\n        ],\n        \"module_alias\": \"pci:v000017CDd00000100sv00000000sd00000000bc06sc04i00\"\n      },\n      {\n        \"index\": 20,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"pci\",\n          \"bridge\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 144,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0006\",\n          \"name\": \"Bridge\",\n          \"value\": 6\n        },\n        \"sub_class\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI bridge\",\n          \"value\": 4\n        },\n        \"pci_interface\": {\n          \"hex\": \"0000\",\n          \"name\": \"Normal decode\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"17cd\",\n          \"value\": 6093\n        },\n        \"device\": {\n          \"hex\": \"0100\",\n          \"value\": 256\n        },\n        \"model\": \"PCI bridge\",\n        \"sysfs_id\": \"/devices/pci0000:90/0000:90:00.0\",\n        \"sysfs_bus_id\": \"0000:90:00.0\",\n        \"sysfs_iommu_group_id\": 0,\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 7,\n          \"header_type\": 1,\n          \"secondary_bus\": 145,\n          \"prog_if\": 0\n        },\n        \"driver\": \"pcieport\",\n        \"driver_module\": \"pcieportdrv\",\n        \"drivers\": [\n          \"pcieport\"\n        ],\n        \"driver_modules\": [\n          \"pcieportdrv\"\n        ],\n        \"module_alias\": \"pci:v000017CDd00000100sv00000000sd00000000bc06sc04i00\"\n      }\n    ],\n    \"cpu\": [\n      {\n        \"architecture\": \"aarch64\",\n        \"vendor_name\": \"ARM Limited\",\n        \"family\": 0,\n        \"model\": 1,\n        \"stepping\": 0,\n        \"features\": [\n          \"fp\",\n          \"asimd\",\n          \"evtstrm\",\n          \"aes\",\n          \"pmull\",\n          \"sha1\",\n          \"sha2\",\n          \"crc32\",\n          \"atomics\",\n          \"fphp\",\n          \"asimdhp\",\n          \"cpuid\",\n          \"asimdrdm\",\n          \"jscvt\",\n          \"fcma\",\n          \"lrcpc\",\n          \"dcpop\",\n          \"sha3\",\n          \"sm3\",\n          \"sm4\",\n          \"asimddp\",\n          \"sha512\",\n          \"sve\",\n          \"asimdfhm\",\n          \"dit\",\n          \"uscat\",\n          \"ilrcpc\",\n          \"flagm\",\n          \"sb\",\n          \"paca\",\n          \"pacg\",\n          \"dcpodp\",\n          \"sve2\",\n          \"sveaes\",\n          \"svepmull\",\n          \"svebitperm\",\n          \"svesha3\",\n          \"svesm4\",\n          \"flagm2\",\n          \"frint\",\n          \"svei8mm\",\n          \"svebf16\",\n          \"i8mm\",\n          \"bf16\",\n          \"dgh\",\n          \"bti\",\n          \"mte\",\n          \"ecv\",\n          \"afp\",\n          \"mte3\",\n          \"wfxt\"\n        ],\n        \"bogo\": 2000,\n        \"page_size\": 4096,\n        \"physical_id\": 0,\n        \"fpu\": false,\n        \"fpu_exception\": false,\n        \"write_protect\": false,\n        \"address_sizes\": {\n          \"physical\": \"0x0\",\n          \"virtual\": \"0x0\"\n        }\n      }\n    ],\n    \"disk\": [\n      {\n        \"index\": 21,\n        \"attached_to\": 19,\n        \"class_list\": [\n          \"disk\",\n          \"block_device\",\n          \"nvme\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0096\",\n          \"name\": \"NVME\",\n          \"value\": 150\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0106\",\n          \"name\": \"Mass Storage Device\",\n          \"value\": 262\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disk\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"144d\",\n          \"value\": 5197\n        },\n        \"sub_vendor\": {\n          \"hex\": \"144d\",\n          \"value\": 5197\n        },\n        \"device\": {\n          \"hex\": \"a808\",\n          \"name\": \"Samsung SSD 970 EVO 1TB\",\n          \"value\": 43016\n        },\n        \"sub_device\": {\n          \"hex\": \"a801\",\n          \"value\": 43009\n        },\n        \"serial\": \"S467NX0MB03860Y\",\n        \"model\": \"Samsung SSD 970 EVO 1TB\",\n        \"sysfs_id\": \"/class/block/nvme0n1\",\n        \"sysfs_bus_id\": \"nvme0\",\n        \"sysfs_device_link\": \"/devices/pci0000:90/0000:90:00.0/0000:91:00.0/nvme/nvme0\",\n        \"unix_device_names\": [\n          \"/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y\",\n          \"/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_1TB_S467NX0MB03860Y_1\",\n          \"/dev/disk/by-id/nvme-eui.0025385b91b0fe46\",\n          \"/dev/disk/by-path/pci-0000:91:00.0-nvme-1\",\n          \"/dev/nvme0n1\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"disk_geo\",\n            \"cylinders\": 953869,\n            \"heads\": 64,\n            \"sectors\": 32,\n            \"size\": \"0x0\",\n            \"geo_type\": \"logical\"\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"sectors\",\n            \"value_1\": 1953525168,\n            \"value_2\": 512\n          }\n        ],\n        \"driver\": \"nvme\",\n        \"driver_module\": \"nvme\",\n        \"drivers\": [\n          \"nvme\"\n        ],\n        \"driver_modules\": [\n          \"nvme\"\n        ]\n      },\n      {\n        \"index\": 22,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"disk\",\n          \"usb\",\n          \"scsi\",\n          \"block_device\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0084\",\n          \"name\": \"SCSI\",\n          \"value\": 132\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0106\",\n          \"name\": \"Mass Storage Device\",\n          \"value\": 262\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Disk\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"0781\",\n          \"name\": \"USB\",\n          \"value\": 1921\n        },\n        \"device\": {\n          \"hex\": \"5583\",\n          \"name\": \"SanDisk 3.2Gen1\",\n          \"value\": 21891\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"1.00\",\n          \"value\": 0\n        },\n        \"serial\": \"01017a33f8c1d6bbbfe7\",\n        \"model\": \"USB SanDisk 3.2Gen1\",\n        \"sysfs_id\": \"/class/block/sda\",\n        \"sysfs_bus_id\": \"0:0:0:0\",\n        \"sysfs_device_link\": \"/devices/platform/PNP0D10:04/usb10/10-1/10-1:1.0/host0/target0:0:0/0:0:0:0\",\n        \"unix_device_names\": [\n          \"/dev/disk/by-id/usb-USB_SanDisk_3.2Gen1_01017a33f8c1d6bbbfe70a7b26cd37feae47d7bdcd66539fbda070dffc8874bcaa990000000000000000000020b7a975008a4b00835581076e2f52e7-0:0\",\n          \"/dev/disk/by-label/nixos-minimal-26.05-aarch64\",\n          \"/dev/disk/by-path/platform-PNP0D10:04-usb-0:1:1.0-scsi-0:0:0:0\",\n          \"/dev/disk/by-path/platform-PNP0D10:04-usbv3-0:1:1.0-scsi-0:0:0:0\",\n          \"/dev/disk/by-uuid/1980-01-01-00-00-00-00\",\n          \"/dev/root\",\n          \"/dev/sda\"\n        ],\n        \"unix_device_name2\": \"/dev/sg0\",\n        \"resources\": [\n          {\n            \"type\": \"disk_geo\",\n            \"cylinders\": 58656,\n            \"heads\": 64,\n            \"sectors\": 32,\n            \"size\": \"0x0\",\n            \"geo_type\": \"logical\"\n          },\n          {\n            \"type\": \"size\",\n            \"unit\": \"sectors\",\n            \"value_1\": 120127488,\n            \"value_2\": 512\n          }\n        ],\n        \"driver\": \"usb-storage\",\n        \"driver_module\": \"usb_storage\",\n        \"drivers\": [\n          \"sd\",\n          \"usb-storage\"\n        ],\n        \"driver_modules\": [\n          \"usb_storage\"\n        ],\n        \"module_alias\": \"usb:v0781p5583d0100dc00dsc00dp00ic08isc06ip50in00\"\n      }\n    ],\n    \"hub\": [\n      {\n        \"index\": 23,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:02\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:02/usb6/6-0:1.0\",\n        \"sysfs_bus_id\": \"6-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 24,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:09\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:09/usb16/16-0:1.0\",\n        \"sysfs_bus_id\": \"16-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 25,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:01\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:01/usb3/3-0:1.0\",\n        \"sysfs_bus_id\": \"3-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 26,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:06\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:06/usb13/13-0:1.0\",\n        \"sysfs_bus_id\": \"13-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 27,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:03\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:03/usb7/7-0:1.0\",\n        \"sysfs_bus_id\": \"7-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 28,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:04\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:04/usb10/10-0:1.0\",\n        \"sysfs_bus_id\": \"10-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 29,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:01\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:01/usb4/4-0:1.0\",\n        \"sysfs_bus_id\": \"4-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 30,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:07\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:07/usb14/14-0:1.0\",\n        \"sysfs_bus_id\": \"14-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 31,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:03\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:03/usb8/8-0:1.0\",\n        \"sysfs_bus_id\": \"8-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 32,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:00\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:00/usb1/1-0:1.0\",\n        \"sysfs_bus_id\": \"1-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 34,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:05\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:05/usb11/11-0:1.0\",\n        \"sysfs_bus_id\": \"11-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 35,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:02\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:02/usb5/5-0:1.0\",\n        \"sysfs_bus_id\": \"5-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 36,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:08\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:08/usb15/15-0:1.0\",\n        \"sysfs_bus_id\": \"15-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 37,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0002\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 2\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:04\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:04/usb9/9-0:1.0\",\n        \"sysfs_bus_id\": \"9-0:1.0\",\n        \"resources\": [\n          {\n            \"type\": \"baud\",\n            \"speed\": 480000000,\n            \"bits\": 0,\n            \"stop_bits\": 0,\n            \"parity\": 0,\n            \"handshake\": 0\n          }\n        ],\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 1,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0002d0618dc09dsc00dp01ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 38,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:00\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:00/usb2/2-0:1.0\",\n        \"sysfs_bus_id\": \"2-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00\"\n      },\n      {\n        \"index\": 39,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"usb\",\n          \"hub\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0086\",\n          \"name\": \"USB\",\n          \"value\": 134\n        },\n        \"slot\": {\n          \"bus\": 0,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"010a\",\n          \"name\": \"Hub\",\n          \"value\": 266\n        },\n        \"vendor\": {\n          \"hex\": \"1d6b\",\n          \"name\": \"Linux 6.18.16 xhci-hcd\",\n          \"value\": 7531\n        },\n        \"device\": {\n          \"hex\": \"0003\",\n          \"name\": \"xHCI Host Controller\",\n          \"value\": 3\n        },\n        \"revision\": {\n          \"hex\": \"0000\",\n          \"name\": \"6.18\",\n          \"value\": 0\n        },\n        \"serial\": \"PNP0D10:05\",\n        \"model\": \"Linux 6.18.16 xhci-hcd xHCI Host Controller\",\n        \"sysfs_id\": \"/devices/platform/PNP0D10:05/usb12/12-0:1.0\",\n        \"sysfs_bus_id\": \"12-0:1.0\",\n        \"detail\": {\n          \"device_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"device_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"device_protocol\": 3,\n          \"interface_class\": {\n            \"hex\": \"0009\",\n            \"name\": \"hub\",\n            \"value\": 9\n          },\n          \"interface_subclass\": {\n            \"hex\": \"0000\",\n            \"name\": \"per_interface\",\n            \"value\": 0\n          },\n          \"interface_protocol\": 0,\n          \"interface_number\": 0,\n          \"interface_alternate_setting\": 0\n        },\n        \"hotplug\": \"usb\",\n        \"driver\": \"hub\",\n        \"driver_module\": \"usbcore\",\n        \"drivers\": [\n          \"hub\"\n        ],\n        \"driver_modules\": [\n          \"usbcore\"\n        ],\n        \"module_alias\": \"usb:v1D6Bp0003d0618dc09dsc00dp03ic09isc00ip00in00\"\n      }\n    ],\n    \"memory\": [\n      {\n        \"index\": 14,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"memory\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0101\",\n          \"name\": \"Internally Used Class\",\n          \"value\": 257\n        },\n        \"sub_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Main Memory\",\n          \"value\": 2\n        },\n        \"model\": \"Main Memory\",\n        \"resources\": [\n          {\n            \"type\": \"phys_mem\",\n            \"range\": 34359738368\n          }\n        ]\n      }\n    ],\n    \"network_controller\": [\n      {\n        \"index\": 15,\n        \"attached_to\": 16,\n        \"class_list\": [\n          \"network_controller\",\n          \"pci\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 49,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Ethernet controller\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"10ec\",\n          \"value\": 4332\n        },\n        \"sub_vendor\": {\n          \"hex\": \"10ec\",\n          \"value\": 4332\n        },\n        \"device\": {\n          \"hex\": \"8125\",\n          \"value\": 33061\n        },\n        \"sub_device\": {\n          \"hex\": \"0123\",\n          \"value\": 291\n        },\n        \"revision\": {\n          \"hex\": \"0005\",\n          \"value\": 5\n        },\n        \"model\": \"Ethernet controller\",\n        \"sysfs_id\": \"/devices/pci0000:30/0000:30:00.0/0000:31:00.0\",\n        \"sysfs_bus_id\": \"0000:31:00.0\",\n        \"sysfs_iommu_group_id\": 1,\n        \"unix_device_names\": [\n          \"enp49s0\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 48\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 48\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1031,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"r8169\",\n        \"driver_module\": \"r8169\",\n        \"drivers\": [\n          \"r8169\"\n        ],\n        \"driver_modules\": [\n          \"r8169\"\n        ],\n        \"module_alias\": \"pci:v000010ECd00008125sv000010ECsd00000123bc02sc00i00\"\n      },\n      {\n        \"index\": 17,\n        \"attached_to\": 18,\n        \"class_list\": [\n          \"network_controller\",\n          \"pci\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 1,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0002\",\n          \"name\": \"Network controller\",\n          \"value\": 2\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Ethernet controller\",\n          \"value\": 0\n        },\n        \"vendor\": {\n          \"hex\": \"10ec\",\n          \"value\": 4332\n        },\n        \"sub_vendor\": {\n          \"hex\": \"10ec\",\n          \"value\": 4332\n        },\n        \"device\": {\n          \"hex\": \"8125\",\n          \"value\": 33061\n        },\n        \"sub_device\": {\n          \"hex\": \"0123\",\n          \"value\": 291\n        },\n        \"revision\": {\n          \"hex\": \"0005\",\n          \"value\": 5\n        },\n        \"model\": \"Ethernet controller\",\n        \"sysfs_id\": \"/devices/pci0000:00/0000:00:00.0/0000:01:00.0\",\n        \"sysfs_bus_id\": \"0000:01:00.0\",\n        \"sysfs_iommu_group_id\": 2,\n        \"unix_device_names\": [\n          \"enp1s0\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 48\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 48\n          }\n        ],\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1031,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 0\n        },\n        \"driver\": \"r8169\",\n        \"driver_module\": \"r8169\",\n        \"drivers\": [\n          \"r8169\"\n        ],\n        \"driver_modules\": [\n          \"r8169\"\n        ],\n        \"module_alias\": \"pci:v000010ECd00008125sv000010ECsd00000123bc02sc00i00\"\n      }\n    ],\n    \"network_interface\": [\n      {\n        \"index\": 40,\n        \"attached_to\": 17,\n        \"class_list\": [\n          \"network_interface\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Ethernet\",\n          \"value\": 1\n        },\n        \"model\": \"Ethernet network interface\",\n        \"sysfs_id\": \"/class/net/enp1s0\",\n        \"sysfs_device_link\": \"/devices/pci0000:00/0000:00:00.0/0000:01:00.0\",\n        \"unix_device_names\": [\n          \"enp1s0\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 48\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 48\n          }\n        ],\n        \"driver\": \"r8169\",\n        \"driver_module\": \"r8169\",\n        \"drivers\": [\n          \"r8169\"\n        ],\n        \"driver_modules\": [\n          \"r8169\"\n        ]\n      },\n      {\n        \"index\": 41,\n        \"attached_to\": 0,\n        \"class_list\": [\n          \"network_interface\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0000\",\n          \"name\": \"Loopback\",\n          \"value\": 0\n        },\n        \"model\": \"Loopback network interface\",\n        \"sysfs_id\": \"/class/net/lo\",\n        \"unix_device_names\": [\n          \"lo\"\n        ]\n      },\n      {\n        \"index\": 42,\n        \"attached_to\": 15,\n        \"class_list\": [\n          \"network_interface\"\n        ],\n        \"base_class\": {\n          \"hex\": \"0107\",\n          \"name\": \"Network Interface\",\n          \"value\": 263\n        },\n        \"sub_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Ethernet\",\n          \"value\": 1\n        },\n        \"model\": \"Ethernet network interface\",\n        \"sysfs_id\": \"/class/net/enp49s0\",\n        \"sysfs_device_link\": \"/devices/pci0000:30/0000:30:00.0/0000:31:00.0\",\n        \"unix_device_names\": [\n          \"enp49s0\"\n        ],\n        \"resources\": [\n          {\n            \"type\": \"hwaddr\",\n            \"address\": 48\n          },\n          {\n            \"type\": \"phwaddr\",\n            \"address\": 48\n          }\n        ],\n        \"driver\": \"r8169\",\n        \"driver_module\": \"r8169\",\n        \"drivers\": [\n          \"r8169\"\n        ],\n        \"driver_modules\": [\n          \"r8169\"\n        ]\n      }\n    ],\n    \"storage_controller\": [\n      {\n        \"index\": 19,\n        \"attached_to\": 20,\n        \"class_list\": [\n          \"storage_controller\",\n          \"pci\"\n        ],\n        \"bus_type\": {\n          \"hex\": \"0004\",\n          \"name\": \"PCI\",\n          \"value\": 4\n        },\n        \"slot\": {\n          \"bus\": 145,\n          \"number\": 0\n        },\n        \"base_class\": {\n          \"hex\": \"0001\",\n          \"name\": \"Mass storage controller\",\n          \"value\": 1\n        },\n        \"sub_class\": {\n          \"hex\": \"0008\",\n          \"value\": 8\n        },\n        \"pci_interface\": {\n          \"hex\": \"0002\",\n          \"value\": 2\n        },\n        \"vendor\": {\n          \"hex\": \"144d\",\n          \"value\": 5197\n        },\n        \"sub_vendor\": {\n          \"hex\": \"144d\",\n          \"value\": 5197\n        },\n        \"device\": {\n          \"hex\": \"a808\",\n          \"value\": 43016\n        },\n        \"sub_device\": {\n          \"hex\": \"a801\",\n          \"value\": 43009\n        },\n        \"model\": \"Mass storage controller\",\n        \"sysfs_id\": \"/devices/pci0000:90/0000:90:00.0/0000:91:00.0\",\n        \"sysfs_bus_id\": \"0000:91:00.0\",\n        \"sysfs_iommu_group_id\": 0,\n        \"detail\": {\n          \"function\": 0,\n          \"command\": 1030,\n          \"header_type\": 0,\n          \"secondary_bus\": 0,\n          \"prog_if\": 2\n        },\n        \"driver\": \"nvme\",\n        \"driver_module\": \"nvme\",\n        \"drivers\": [\n          \"nvme\"\n        ],\n        \"driver_modules\": [\n          \"nvme\"\n        ],\n        \"module_alias\": \"pci:v0000144Dd0000A808sv0000144Dsd0000A801bc01sc08i02\"\n      }\n    ],\n    \"system\": {}\n  },\n  \"smbios\": {}\n}\n"
  },
  {
    "path": "machines/uconsole/configuration.nix",
    "content": "{\n  nixos-hardware,\n  lib,\n  pkgs,\n  config,\n  ...\n}:\n{\n  imports = [\n    # nixos-hardware.nixosModules.clockworkpi-uconsole-cm4\n    ./disko-config.nix\n  ];\n\n  # uConsole uses a Raspberry Pi CM4 (aarch64)\n  nixpkgs.hostPlatform = \"aarch64-linux\";\n\n  networking.hostName = \"uconsole\";\n\n  # Disable grub - we use extlinux from the rpi4 module\n  boot.loader.grub.enable = lib.mkForce false;\n\n  # Additional kernel params (display params are in nixos-hardware)\n  boot.kernelParams = [\n    \"console=tty1\"\n    \"loglevel=4\"\n  ];\n\n  # Additional modules available in initrd (not forced to load)\n  # Display modules are now in nixos-hardware\n  boot.initrd.availableKernelModules = [\n    # USB ethernet adapters (for network debugging)\n    \"usbnet\"\n    \"ax88179_178a\"\n    \"cdc_ether\"\n    \"r8152\"\n    # LUKS/dm-crypt support\n    \"dm_crypt\"\n    \"dm_mod\"\n    # Crypto modules for xchacha20,aes-adiantum cipher (fast on ARM without AES-NI)\n    \"xchacha20\"\n    \"adiantum\"\n    \"nhpoly1305\"\n    \"chacha_generic\"\n    \"chacha_neon\"\n    \"aes_generic\"\n    \"aes_arm64\"\n    \"sha256_generic\"\n    \"sha256_arm64\"\n    \"algif_skcipher\"\n  ];\n\n  # 32-bit graphics not supported on aarch64\n  hardware.graphics.enable32Bit = lib.mkForce false;\n\n  # Console font sized for the 5\" 720x1280 display\n  console = {\n    font = \"ter-v24n\";\n    packages = with pkgs; [ terminus_font ];\n  };\n\n  # Filesystems are now managed by disko (see disko-config.nix)\n\n  # Enable NetworkManager for easy WiFi setup\n  networking.networkmanager.enable = true;\n\n  # Enable SSH for remote access\n  services.openssh.enable = true;\n  services.openssh.settings.PermitRootLogin = \"yes\";\n\n  # Set initial root password for debugging (change after first login!)\n  users.users.root.initialPassword = \"nixos\";\n}\n"
  },
  {
    "path": "machines/uconsole/disko-config.nix",
    "content": "{ pkgs, config, lib, ... }:\nlet\n  # Raspberry Pi firmware files needed for boot\n  rpiFirmware = pkgs.raspberrypifw;\n\n  # Get the kernel and initrd from the NixOS system being built\n  toplevel = config.system.build.toplevel;\n\n  # Get the kernel package for DTB overlays\n  kernelPackage = config.boot.kernelPackages.kernel;\n\n  # config.txt for Raspberry Pi CM4 / uConsole\n  # IMPORTANT: Kernel must be at root level, not in subdirectory - GPU firmware can't load from subdirs\n  configTxt = pkgs.writeText \"config.txt\" ''\n    [all]\n    arm_64bit=1\n    enable_uart=1\n    uart_2ndstage=1\n    avoid_warnings=1\n    disable_overscan=1\n    disable_splash=0\n\n    # GPU memory allocation (256MB for GPU)\n    gpu_mem=256\n\n    # Ignore LCD detect - we use DSI display\n    ignore_lcd=1\n\n    # Audio settings\n    disable_audio_dither=1\n    pwm_sample_bits=20\n\n    # Device tree debug (use vclog -m to view)\n    dtdebug=1\n\n    # GPIO configuration for uConsole hardware\n    gpio=10=ip,np\n    gpio=11=op,dh\n\n    # Enable VC4 graphics driver - MUST use pi4 variant for CM4\n    # cma-384 allocates 384MB contiguous memory for GPU (needed for full resolution)\n    dtoverlay=vc4-kms-v3d-pi4,cma-384\n\n    # USB controller - host mode\n    dtoverlay=dwc2,dr_mode=host\n\n    # uConsole display and hardware overlay\n    dtoverlay=clockworkpi-uconsole\n\n    # Audio routing to GPIO 12/13 (headphone jack)\n    dtoverlay=audremap,pins_12_13\n\n    # External WiFi antenna\n    dtparam=ant2=on\n    dtparam=audio=on\n\n    # Kernel and initrd at root level (GPU firmware can't load from subdirectories)\n    kernel=kernel.img\n    initramfs initrd.img followkernel\n\n    # Device tree for CM4\n    device_tree=bcm2711-rpi-cm4.dtb\n\n    [cm4]\n    # CM4-specific settings\n    otg_mode=0\n    over_voltage=6\n    arm_freq=2000\n    gpu_freq=750\n    force_turbo=1\n    dtparam=spi=on\n  '';\n\n  # cmdline.txt for kernel parameters (LUKS version)\n  cmdlineTxt = pkgs.writeText \"cmdline.txt\" ''\n    console=tty1 root=/dev/mapper/crypted rootfstype=btrfs rootflags=subvol=/root init=${toplevel}/init loglevel=4 fbcon=rotate:1,nodefer video=DSI-1:panel_orientation=right_side_up\n  '';\n\nin\n{\n  # Use a generic kernel for the disko VM (the RPi CM4 kernel doesn't work in QEMU)\n  disko.imageBuilder.kernelPackages = pkgs.linuxPackages;\n\n  # Increase VM memory for LUKS + nix store copy (default 1024 MiB is too low)\n  disko.memSize = 4096;\n\n  # Populate firmware partition after VM creates the base image\n  # Note: Images are in $out after VM completes\n  # We use mtools to write to the FAT partition without needing loop devices (sandbox-safe)\n  disko.imageBuilder.extraPostVM = ''\n    ${pkgs.coreutils}/bin/echo \"=== Populating firmware partition ===\"\n    ${pkgs.coreutils}/bin/echo \"Output directory: $out\"\n    ${pkgs.coreutils}/bin/ls -la \"$out\"\n\n    # The firmware partition starts at 1MB (sector 2048) in GPT\n    # mtools can access FAT filesystems with an offset\n    export MTOOLS_SKIP_CHECK=1\n\n    # Create mtools config to access the FAT partition at offset\n    # Partition 1 starts at sector 2048 (offset 1048576 bytes)\n    ${pkgs.coreutils}/bin/cat > /tmp/mtoolsrc << 'MTOOLSRC'\n    drive c:\n      file=\"$out/main.raw\"\n      partition=1\n    MTOOLSRC\n    export MTOOLSRC=/tmp/mtoolsrc\n\n    # Actually, mtools partition support is tricky. Let's use a simpler approach:\n    # Extract the partition offset using sfdisk and create a direct access config\n\n    # Get partition info\n    PART_INFO=$(${pkgs.util-linux}/bin/sfdisk -J \"$out/main.raw\")\n    ${pkgs.coreutils}/bin/echo \"Partition info: $PART_INFO\"\n\n    # First partition starts at sector 2048 (1MB) typically for GPT with 512M firmware\n    # Offset = 2048 * 512 = 1048576 bytes\n    OFFSET=1048576\n\n    # Create mtools drive config with explicit offset\n    ${pkgs.coreutils}/bin/cat > /tmp/mtoolsrc << EOF\n    drive c: file=\"$out/main.raw\" offset=$OFFSET\n    EOF\n    export MTOOLSRC=/tmp/mtoolsrc\n\n    ${pkgs.coreutils}/bin/echo \"=== Copying firmware files ===\"\n\n    # Create directories first\n    ${pkgs.mtools}/bin/mmd -i \"$out/main.raw\"@@$OFFSET ::overlays || true\n\n    # Copy Raspberry Pi firmware\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${rpiFirmware}/share/raspberrypi/boot/start4.elf ::\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${rpiFirmware}/share/raspberrypi/boot/fixup4.dat ::\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${rpiFirmware}/share/raspberrypi/boot/bootcode.bin :: || true\n\n    # Copy kernel and initrd to ROOT level (GPU firmware can't load from subdirectories!)\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${toplevel}/kernel ::kernel.img\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${toplevel}/initrd ::initrd.img\n\n    # Copy device tree for CM4 (from toplevel/dtbs/broadcom/)\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${toplevel}/dtbs/broadcom/bcm2711-rpi-cm4.dtb ::\n\n    # Copy device tree overlays from kernel package\n    for overlay in ${kernelPackage}/dtbs/overlays/*.dtbo; do\n      if [ -f \"$overlay\" ]; then\n        ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET \"$overlay\" ::overlays/ || true\n      fi\n    done\n\n    # Copy config files\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${configTxt} ::config.txt\n    ${pkgs.mtools}/bin/mcopy -i \"$out/main.raw\"@@$OFFSET ${cmdlineTxt} ::cmdline.txt\n\n    # List what we copied\n    ${pkgs.coreutils}/bin/echo \"=== Firmware partition contents ===\"\n    ${pkgs.mtools}/bin/mdir -i \"$out/main.raw\"@@$OFFSET ::\n\n    ${pkgs.coreutils}/bin/echo \"=== Firmware population complete ===\"\n  '';\n\n\n  # Auto-resize root partition on first boot to fill the SD card\n  boot.growPartition = true;\n  boot.supportedFilesystems.btrfs = true;\n\n  services.btrfs.autoScrub = {\n    enable = true;\n    interval = \"weekly\";\n  };\n\n  disko.devices = {\n    disk = {\n      main = {\n        type = \"disk\";\n        # Image size for generation (actual SD card will be larger)\n        imageSize = \"16G\";\n        content = {\n          type = \"gpt\";\n          partitions = {\n            # FAT32 firmware partition for Raspberry Pi bootloader\n            # Pi bootloader reads kernel, initrd, device tree from here\n            # Firmware files are populated by extraPostVM after VM creates the image\n            firmware = {\n              size = \"512M\";\n              type = \"EF00\";  # EFI System Partition type for GPT\n              content = {\n                type = \"filesystem\";\n                format = \"vfat\";\n                mountpoint = \"/boot/firmware\";\n                mountOptions = [\n                  \"fmask=0022\"\n                  \"dmask=0022\"\n                ];\n              };\n            };\n            # LUKS-encrypted partition containing BTRFS\n            luks = {\n              size = \"100%\";\n              content = {\n                type = \"luks\";\n                name = \"crypted\";\n                # Password file for image building (change after first boot!)\n                passwordFile = toString (pkgs.writeText \"luks-password\" \"changeme\");\n                # Use xchacha20,aes-adiantum because BCM2711 lacks AES hardware acceleration\n                # This provides good performance on ARM without AES-NI\n                extraFormatArgs = [\n                  \"--type\" \"luks2\"\n                  \"--cipher\" \"xchacha20,aes-adiantum-plain64\"\n                  \"--key-size\" \"256\"\n                ];\n                settings = {\n                  allowDiscards = true;\n                };\n                content = {\n                  type = \"btrfs\";\n                  extraArgs = [ \"-f\" ];\n                  subvolumes = {\n                    \"/root\" = {\n                      mountpoint = \"/\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/home\" = {\n                      mountpoint = \"/home\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/nix\" = {\n                      mountpoint = \"/nix\";\n                      mountOptions = [\n                        \"compress=zstd\"\n                        \"noatime\"\n                      ];\n                    };\n                    \"/swap\" = {\n                      mountpoint = \"/.swapvol\";\n                      swap.swapfile.size = \"1G\";\n                    };\n                  };\n                };\n              };\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/bluetooth/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.bluetooth;\nin\n{\n\n  options.pinpox.defaults.bluetooth = {\n    enable = mkEnableOption \"default bluetooth configuration\";\n  };\n\n  config = mkIf cfg.enable {\n\n    hardware.bluetooth = {\n      enable = true;\n      settings = {\n        General = {\n          Enable = \"Source,Sink,Media,Socket\";\n        };\n      };\n    };\n\n    services.blueman.enable = true;\n  };\n}\n"
  },
  {
    "path": "modules/caddy-security/default.nix",
    "content": "{\n  pkgs,\n  config,\n  lib,\n  pinpox-utils,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.caddy-security;\nin\n{\n\n  options.pinpox.services.caddy-security = {\n    enable = mkEnableOption \"Caddy security portal config\";\n\n    domain = mkOption {\n      type = types.str;\n      description = \"Domain protetected by this caddy instance\";\n      example = \"0cx.de\";\n    };\n\n    host = mkOption {\n      type = types.str;\n      default = \"auth.${cfg.domain}\";\n      defaultText = literalExpression ''\"auth.''${cfg.domain}\"'';\n      description = \"Host serving caddy-security portal\";\n      example = \"auth.0cx.de\";\n    };\n\n    authURL = mkOption {\n      type = types.str;\n      default = \"https://${cfg.host}/oauth2/generic\";\n      defaultText = literalExpression ''\"https://''${cfg.host}/oauth2/generic\"'';\n      description = \"Authentication URL\";\n      example = \"https://auth.mydomain.tld/oauth2/generic\";\n    };\n\n    openID = {\n\n      name = mkOption {\n        type = types.str;\n        default = \"Dex\";\n        description = \"Name of the OpenID provider, shown in the UI\";\n        example = \"GitHub\";\n      };\n\n      domain = mkOption {\n        type = types.str;\n        default = cfg.domain;\n        defaultText = literalExpression \"cfg.domain\";\n        description = \"Domain of the OpenID provider\";\n        example = \"mydomain.tld\";\n      };\n\n      host = mkOption {\n        type = types.str;\n        default = \"login.${cfg.openID.domain}\";\n        defaultText = literalExpression ''\"login.''${cfg.openID.domain}\"'';\n        description = \"Host of the OpenID provider\";\n        example = \"login.mydomain.tld\";\n      };\n\n      metadataUrl = mkOption {\n        type = types.str;\n        default = \"https://${cfg.openID.host}/.well-known/openid-configuration\";\n        defaultText = literalExpression ''\"https://''${cfg.openID.host}/.well-known/openid-configuration\"'';\n        description = \"Metadata URL of the OpenID Host\";\n        example = \"https://myhost.tld/.well-known/openid-configuration\";\n      };\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"caddy\" = pinpox-utils.mkEnvGenerator [\n      \"JWT_SHARED_KEY\"\n      \"GENERIC_CLIENT_ID\"\n      \"GENERIC_CLIENT_SECRET\"\n    ];\n\n    systemd.services.caddy.serviceConfig = {\n      AmbientCapabilities = [ \"CAP_NET_BIND_SERVICE\" ];\n      EnvironmentFile = [ config.clan.core.vars.generators.\"caddy\".files.\"envfile\".path ];\n    };\n\n    services.caddy = {\n\n      enable = true;\n\n      # package = caddy-patched.packages.x86_64-linux.caddy;\n      # Custom package no longer necessary. See https://github.com/NixOS/nixpkgs/pull/358586\n\n      globalConfig = ''\n        order authenticate before respond\n        order authorize before basicauth\n\n        security {\n\n          oauth identity provider generic {\n            icon \"${cfg.openID.name}\" \"las la-key la-2x\" \"white\" \"black\" priority 100\n            delay_start 5\n            retry_attempts 5\n            retry_interval 10\n            realm generic\n            driver generic\n            client_id {env.GENERIC_CLIENT_ID}\n            client_secret {env.GENERIC_CLIENT_SECRET}\n            scopes openid email profile groups federated:id\n            base_auth_url https://${cfg.openID.host}\n            metadata_url ${cfg.openID.metadataUrl}\n          }\n\n          authentication portal myportal {\n            crypto default token lifetime 3600\n            crypto key sign-verify {env.JWT_SHARED_KEY}\n            cookie domain ${cfg.domain}\n            enable identity provider generic\n            ui {\n              links {\n                \"My Identity\" \"/whoami\" icon \"las la-user\"\n              }\n            }\n\n            transform user {\n              match realm generic\n              action add role authp/user\n              ui link \"Test site 1 (user)\" https://static-site-one.${cfg.domain}/ icon \"las la-star\"\n            }\n\n            transform user {\n              match realm generic\n              match sub CgZwaW5wb3gSBmdpdGh1Yg # github ID of pinpox\n              action add role authp/admin\n              ui link \"Test site 2 (admin)\" https://static-site-two.${cfg.domain}/ icon \"las la-star\"\n            }\n          }\n\n          authorization policy pol-user {\n            set auth url ${cfg.authURL}\n            crypto key verify {env.JWT_SHARED_KEY}\n            allow roles authp/admin authp/user\n            validate bearer header\n            inject headers with claims\n          }\n\n          authorization policy pol-admin {\n            set auth url ${cfg.authURL}\n            crypto key verify {env.JWT_SHARED_KEY}\n            allow roles authp/admin\n            validate bearer header\n            inject headers with claims\n          }\n        }\n      '';\n\n      virtualHosts =\n        let\n          mkStaticTestSite =\n            num:\n            pkgs.writeTextFile {\n              name = \"index.html\";\n              text = ''\n                <!DOCTYPE html>\n                <html>\n                  <head> <meta charset=\"UTF-8\"> </head>\n                  <body>\n                  <h1>Hello World (${num})!</h1>\n                  <p>This is the site number ${num}</p>\n                  </body>\n                </html>\n              '';\n              executable = false;\n              destination = \"/html/index.html\";\n            };\n        in\n        {\n\n          \"${cfg.host}\".extraConfig = \"authenticate with myportal\";\n\n          \"static-site-one.${cfg.domain}\" = {\n            extraConfig = ''\n              authorize with pol-user\n              encode gzip\n              root * ${mkStaticTestSite \"one\"}/html\n              file_server\n            '';\n          };\n\n          \"static-site-two.${cfg.domain}\" = {\n            extraConfig = ''\n              authorize with pol-admin\n              encode gzip\n              root * ${mkStaticTestSite \"two\"}/html\n              file_server\n            '';\n          };\n        };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/calibre-web/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.calibre-web;\nin\n{\n\n  options.pinpox.services.calibre-web = {\n    enable = mkEnableOption \"calibre-web config\";\n    host = mkOption {\n      type = types.str;\n      default = \"books.0cx.de\";\n      description = \"Host serving calibre\";\n      example = \"books.0cx.de\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    services.calibre-web = {\n\n      enable = true;\n\n      # listen.port = 8083\n      listen.ip = \"127.0.0.1\";\n\n      options.enableBookUploading = true;\n      # options.reverseProxyAuth.header\n      # options.reverseProxyAuth.enable\n      # options.enableKepubify\n      # options.enableBookConversion\n      # options.calibreLibrary\n    };\n\n    # Reverse proxy\n    services.caddy.virtualHosts.\"${cfg.host}\".extraConfig =\n      with config.services.calibre-web.listen;\n      \"reverse_proxy ${ip}:${builtins.toString port}\";\n\n    # Backups\n    pinpox.services.restic-client.backup-paths-offsite = [ config.services.calibre-web.dataDir ];\n\n  };\n}\n"
  },
  {
    "path": "modules/ci/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  flake-self,\n  nixpkgs,\n  ...\n}:\nwith lib;\n{\n  options.pinpox.defaults = {\n    CISkip = mkOption {\n      type = types.bool;\n      default = false;\n      example = true;\n      description = \"Wheter this host should be skipped by the CI pipeline\";\n    };\n  };\n}\n"
  },
  {
    "path": "modules/clan-common/default.nix",
    "content": "{\n  config,\n  clan-core,\n  self,\n  pkgs,\n  lib,\n  ...\n}:\n{\n\n  # Make pinpox-utils available to all modules\n  # _module.args.pinpox-utils = import ../../utils { inherit pkgs lib; };\n\n  nixpkgs.hostPlatform = lib.mkDefault \"x86_64-linux\";\n\n  # Default to depolying to the hostname\n  clan.core.networking.targetHost = lib.mkOverride 999 \"${config.networking.hostName}.pin\";\n\n  # passCommand must remain at machine-level (not flake-level)\n  clan.core.vars.password-store.passCommand = \"passage\";\n\n  environment.systemPackages = [ pkgs.passage ];\n\n  clan.core.vars.generators.\"mkpasswd-generator\" = {\n    files.test-password = { };\n\n    runtimeInputs = with pkgs; [\n      coreutils\n      xkcdpass\n    ];\n\n    script = ''\n      mkdir -p $out\n      echo ${(self.clan.exports.\"clan-core/internet:internet::\".networking.module)}\n      xkcdpass > $out/test-password\n    '';\n  };\n\n  environment.etc.\"test-password\".source =\n    config.clan.core.vars.generators.\"mkpasswd-generator\".files.\"test-password\".path;\n\n  nix.settings.trusted-substituters = [\n    \"https://cache.clan.lol\"\n    \"https://nix-community.cachix.org\"\n  ];\n  nix.settings.trusted-public-keys = [\n    \"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\"\n    \"cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28=\"\n  ];\n\n}\n"
  },
  {
    "path": "modules/environment/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.environment;\nin\n{\n\n  options.pinpox.defaults.environment = {\n    enable = mkEnableOption \"Environment defaults\";\n  };\n\n  config = mkIf cfg.enable {\n\n    # System-wide environment variables to be set\n    environment = {\n      variables = {\n        EDITOR = \"nvim\";\n        GOPATH = \"/home/pinpox/.go\";\n        VISUAL = \"nvim\";\n        # Use librsvg's gdk-pixbuf loader cache file as it enables gdk-pixbuf to load\n        # SVG files (important for icons)\n        # GDK_PIXBUF_MODULE_FILE =\n        #   \"$(echo ${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/*/loaders.cache)\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/fonts/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.fonts;\nin\n{\n\n  options.pinpox.defaults.fonts = {\n    enable = mkEnableOption \"Fonts defaults\";\n  };\n\n  config = mkIf cfg.enable {\n\n    fonts = {\n      fontDir.enable = true;\n      packages = with pkgs; [\n\n        # stix-two\n        # league-of-moveable-type\n        # inter\n        # source-sans-pro\n        # source-serif-pro\n        noto-fonts-monochrome-emoji\n        # corefonts\n        # recursive\n        # iosevka-bin\n        font-awesome\n        line-awesome\n      ];\n\n      fontconfig = {\n        defaultFonts = {\n          serif = [\n            \"Berkeley Mono\"\n            \"Inconsolata Nerd Font Mono\"\n          ];\n          sansSerif = [\n            \"Berkeley Mono\"\n            \"Inconsolata Nerd Font Mono\"\n          ];\n          monospace = [\n            \"Berkeley Mono\"\n            \"Inconsolata Nerd Font Mono\"\n          ];\n          emoji = [ \"Noto Emoji\" ];\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/forgejo/default.nix",
    "content": "{ config, lib, pkgs, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.forgejo;\n  oidcSecretPath = config.clan.core.vars.generators.\"forgejo-oidc\".files.client_secret.path;\nin\n{\n\n  options.pinpox.services.forgejo = {\n    enable = mkEnableOption \"forgejo config\";\n    host = mkOption {\n      type = types.str;\n      default = \"git.pinpox.com\";\n      description = \"Host serving forgejo\";\n      example = \"git.pinpox.com\";\n    };\n    autheliaHost = mkOption {\n      type = types.str;\n      default = \"auth.pablo.tools\";\n      description = \"Authelia host for OIDC discovery\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    # Reverse proxy\n    services.caddy.virtualHosts.\"${cfg.host}\".extraConfig =\n      with config.services.forgejo.settings.server;\n      \"reverse_proxy ${HTTP_ADDR}:${toString HTTP_PORT}\";\n\n    # Backups\n    pinpox.services.restic-client.backup-paths-offsite = [\n      config.services.forgejo.dump.backupDir\n      # config.services.forgejo.stateDir\n      config.services.forgejo.lfs.contentDir\n    ];\n\n    # Shared OIDC secret (generated by the authelia host, shared via clan vars)\n    clan.core.vars.generators.\"forgejo-oidc\" = {\n      share = true;\n      files.client_secret = { };\n      files.client_secret_hash = { };\n      runtimeInputs = with pkgs; [\n        coreutils\n        openssl\n        authelia\n        gnused\n      ];\n      script = ''\n        mkdir -p $out\n        openssl rand -hex 32 > $out/client_secret\n        authelia crypto hash generate argon2 --password \"$(cat $out/client_secret)\" \\\n          | sed 's/^Digest: //' > $out/client_secret_hash\n      '';\n    };\n\n    # Register Authelia as OAuth2 source after forgejo starts\n    systemd.services.forgejo.postStart =\n      let\n        exe = lib.getExe config.services.forgejo.package;\n        providerName = \"authelia\";\n        customConf = \"--work-path ${config.services.forgejo.stateDir} --config ${config.services.forgejo.customDir}/conf/app.ini\";\n      in\n      ''\n        # Only add if not already registered\n        if ! ${exe} admin auth list ${customConf} 2>/dev/null | grep -q \"${providerName}\"; then\n          ${exe} admin auth add-oauth ${customConf} \\\n            --name \"${providerName}\" \\\n            --provider openidConnect \\\n            --key \"forgejo\" \\\n            --secret \"$(cat ${oidcSecretPath})\" \\\n            --auto-discover-url \"https://${cfg.autheliaHost}/.well-known/openid-configuration\" \\\n            --scopes \"openid email profile groups\"\n        fi\n      '';\n\n    services.forgejo = {\n      enable = true;\n\n      database.type = \"sqlite3\";\n      dump.enable = true;\n      lfs.enable = true;\n\n      settings = {\n\n        server = {\n          HTTP_PORT = 3333;\n          HTTP_ADDR = \"127.0.0.1\";\n          DOMAIN = cfg.host;\n          PROTOCOL = \"https\";\n        };\n\n        openid = {\n          ENABLE_OPENID_SIGNIN = false;\n          ENABLE_OPENID_SIGNUP = true;\n          WHITELISTED_URIS = cfg.autheliaHost;\n        };\n\n        oauth2_client = {\n          ENABLE_AUTO_REGISTRATION = true;\n        };\n\n        service = {\n          DISABLE_REGISTRATION = false;\n          ALLOW_ONLY_EXTERNAL_REGISTRATION = true;\n          SHOW_REGISTRATION_BUTTON = false;\n          ENABLE_INTERNAL_SIGNIN = false;\n          ENABLE_BASIC_AUTHENTICATION = false;\n          REQUIRE_SIGNIN_VIEW = true;\n        };\n\n        mailer = {\n          ENABLED = true;\n          FROM = \"git@0cx.de\";\n          PROTOCOL = \"smtp\";\n          IS_TLS_ENABLED = false;\n          USER = \"mail@0cx.de\";\n          SMTP_ADDR = \"r19.hallo.cloud:587\";\n        };\n\n        other.SHOW_FOOTER_VERSION = false;\n        session.COOKIE_SECURE = true;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/gitea/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.gitea;\nin\n{\n\n  options.pinpox.services.gitea = {\n    enable = mkEnableOption \"gitea config\";\n    host = mkOption {\n      type = types.str;\n      default = \"git.0cx.de\";\n      description = \"Host serving gitea\";\n      example = \"git.0cx.de\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    # Reverse proxy\n    services.caddy.virtualHosts.\"${cfg.host}\".extraConfig =\n      with config.services.gitea.settings.server;\n      \"reverse_proxy ${HTTP_ADDR}:${builtins.toString HTTP_PORT}\";\n\n    # Backups\n    pinpox.services.restic-client.backup-paths-offsite = [ \"/var/lib/gitea\" ];\n\n    clan.core.vars.generators.\"gitea\" = {\n      files.mailer-pw.owner = \"gitea\";\n      prompts.mailer-pw.persist = true;\n    };\n\n    services.gitea = {\n\n      enable = true;\n      mailerPasswordFile = \"${config.clan.core.vars.generators.\"gitea\".files.\"mailer-pw\".path}\";\n\n      settings = {\n        server = {\n          ROOT_URL = \"https://${cfg.host}\";\n          HTTP_PORT = 3333;\n          HTTP_ADDR = \"127.0.0.1\";\n        };\n        service = {\n          DISABLE_REGISTRATION = true;\n          REQUIRE_SIGNIN_VIEW = true;\n          DOMAIN = cfg.host;\n        };\n\n        mailer = {\n          ENABLED = true;\n          FROM = \"git@0cx.de\";\n          PROTOCOL = \"smtp\";\n          IS_TLS_ENABLED = false;\n          USER = \"mail@0cx.de\";\n          SMTP_ADDR = \"r19.hallo.cloud:587\";\n        };\n        markdown.ENABLE_MATH = true;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/hedgedoc/default.nix",
    "content": "{\n  config,\n  lib,\n  pinpox-utils,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.hedgedoc;\nin\n# pinpox-utils = import ../../utils { inherit pkgs; };\n{\n\n  options.pinpox.services.hedgedoc = {\n    enable = mkEnableOption \"Hedgedoc server\";\n  };\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"hedgedoc\" = pinpox-utils.mkEnvGenerator [\n      \"CMD_SESSION_SECRET\"\n      \"CMD_OAUTH2_CLIENT_ID\"\n      \"CMD_OAUTH2_CLIENT_SECRET\"\n    ];\n\n    systemd.services.hedgedoc.serviceConfig.Environment = [\n      # Allow creating on-the-fly by url\n      \"CMD_ALLOW_FREEURL=true\"\n\n      # Default permission of notes\n      \"CMD_DEFAULT_PERMISSION=limited\"\n\n      # Forbid anonymous usage\n      \"CMD_ALLOW_ANONYMOUS=false\"\n\n      # oauth2 with dex\n      \"CMD_OAUTH2_BASEURL=https://${config.pinpox.services.dex.host}\"\n      \"CMD_OAUTH2_AUTHORIZATION_URL=https://${config.pinpox.services.dex.host}/auth\"\n      \"CMD_OAUTH2_TOKEN_URL=https://${config.pinpox.services.dex.host}/token\"\n      \"CMD_OAUTH2_USER_PROFILE_URL='https://${config.pinpox.services.dex.host}/userinfo'\"\n      \"CMD_OAUTH2_PROVIDERNAME=dex\"\n      \"CMD_OAUTH2_SCOPE='openid email profile'\"\n      \"CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR='preferred_username'\"\n      \"CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR='name'\"\n      \"CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR='email'\"\n    ];\n\n    # Create system user and group\n    services.hedgedoc = {\n      enable = true;\n\n      environmentFile = \"${config.clan.core.vars.generators.\"hedgedoc\".files.\"envfile\".path}\";\n\n      settings = {\n\n        protocolUseSSL = true; # Use https when loading assets\n        allowEmailRegister = false; # Disable email registration\n        email = false; # Disable email login\n\n        domain = \"pads.0cx.de\";\n        host = \"127.0.0.1\";\n        # port = 3000; # Default\n        debug = true;\n\n        db = {\n          dialect = \"sqlite\";\n          storage = \"/var/lib/hedgedoc/db.sqlite\";\n        };\n\n        useCDN = true;\n      };\n    };\n\n    # Backup SQLite databse\n    pinpox.services.restic-client.backup-paths-offsite = [\n      config.services.hedgedoc.settings.db.storage\n    ];\n\n    # systemd.services.hedgedoc-git-sync = {\n    #   serviceConfig = {\n    #     Type = \"oneshot\";\n    #     Environment = [\n    #       \"GIT_SSH_COMMAND='ssh -i private_key_file'\"\n    #     ];\n    #   };\n    #   path = with pkgs; [ bash ];\n    #   script = ''\n    #     echo \"RUNNING IN \"\n    #     pwd\n    #   '';\n    # };\n\n    # systemd.timers.hedgedoc-git-sync = {\n    #   wantedBy = [ \"timers.target\" ];\n    #   partOf = [ \"hedgedoc-git-sync.service\" ];\n    #   timerConfig = {\n    #     OnCalendar = \"*:0/1\";\n    #     Unit = \"hedgedoc-git-sync.service\";\n    #   };\n    # };\n  };\n}\n"
  },
  {
    "path": "modules/hello/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.hello;\nin\n{\n  options.pinpox.services.hello = {\n    enable = mkEnableOption \"hello service\";\n    greeter = mkOption {\n      type = types.str;\n      default = \"world\";\n      example = \"universe\";\n      description = \"A very friendly service that greets you\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    environment.systemPackages = [ pkgs.hello ];\n\n    systemd.services.hello = {\n      wantedBy = [ \"multi-user.target\" ];\n      serviceConfig.ExecStart = \"${pkgs.hello}/bin/hello -g'Hello, ${escapeShellArg cfg.greeter}!'\";\n    };\n  };\n}\n"
  },
  {
    "path": "modules/hello/test.nix",
    "content": "{\n  pkgs,\n  system,\n  self,\n  ...\n}:\n\nwith import (pkgs + \"/nixos/lib/testing-python.nix\") { inherit system; };\n\n(makeTest {\n  nodes = {\n    client =\n      { ... }:\n      {\n        imports = [ self.nixosModules.hello ];\n        pinpox.services.hello.enable = true;\n      };\n  };\n\n  testScript = ''\n    start_all()\n    client.wait_for_unit(\"multi-user.target\")\n    print(client.succeed(\"uname\"))\n    print(client.succeed(\"hello\"))\n  '';\n}).test\n"
  },
  {
    "path": "modules/home-assistant/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.home-assistant;\nin\n{\n\n  options.pinpox.services.home-assistant = {\n    enable = mkEnableOption \"Home-assitant server\";\n  };\n\n  config = mkIf cfg.enable {\n\n    networking.firewall.trustedInterfaces = [ \"wg-clan\" ];\n\n    clan.core.vars.generators.\"home-assistant\" = {\n      prompts.\"secrets.yaml\".persist = true;\n      prompts.\"secrets.yaml\".type = \"multiline\";\n    };\n\n    systemd.services.home-manager.serviceConfig.BindReadOnlyPaths = [\n      \"${\n        config.clan.core.vars.generators.\"home-assistant\".files.\"secrets.yaml\".path\n      }:/var/lib/hass/secrets.yaml\"\n    ];\n\n    # https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_automations\n    systemd.tmpfiles.rules = [\n      \"f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass\"\n    ];\n\n    # Backup configuration dir - Config done via UI stateful\n    pinpox.services.restic-client.backup-paths-offsite = [ config.services.home-assistant.configDir ];\n\n    # Needed for some integrations\n    users.users.hass.extraGroups = [\n      \"dialout\"\n      \"keys\"\n    ];\n\n    # Open port for mqtt\n    networking.firewall = {\n\n      allowedTCPPorts = [ 1883 ];\n\n      # For home-assistant COIT\n      interfaces.eno1.allowedUDPPorts = [ 5683 ];\n\n      # Expose home-assitant over wireguard\n      interfaces.wg-clan.allowedTCPPorts = [\n        8123\n        9273 # Telegraf\n      ];\n    };\n\n    # Enable mosquitto MQTT broker\n    services.mosquitto = {\n      enable = true;\n\n      # Mosquitto is only listening on the local IP, traffic from outside is not\n      # allowed.\n      listeners = [\n        {\n          address = \"192.168.101.221\";\n          port = 1883;\n          users = {\n            # No real authentication needed here, since the local network is\n            # trusted.\n            mosquitto = {\n              acl = [ \"readwrite #\" ];\n              password = \"mosquitto\";\n            };\n          };\n        }\n      ];\n    };\n\n    # The prometheus integration of home-assistant is incomplete (e.g. missing\n    # GPS lon/lat data), but the influx integration is fine. To have the\n    # home-assistant states in prometheus to be able to create alerts and\n    # graphs, telegraf is used to listen for influx formatted data form\n    # home-assistant and export it as prometheus metrics.\n\n    services.telegraf = {\n      enable = true;\n      extraConfig = {\n\n        agent = {\n          interval = \"60s\";\n          ## Log at debug level.\n          debug = true;\n          ## Log only error level messages.\n          quiet = false;\n        };\n        inputs = {\n          influxdb_v2_listener = {\n            # Start influxdb V2 listener on localhost only as we are running on\n            # the same host as home-assistant.\n            service_address = \":8086\";\n          };\n        };\n\n        outputs = {\n          prometheus_client =\n            let\n              wg-clan-ip = builtins.elemAt (builtins.match \"(.*)/.*\" (builtins.elemAt config.networking.wireguard.interfaces.wg-clan.ips 0)) 0;\n            in\n            {\n              # Listen on the wireguard VPN IP. Localhost is not enough here, as\n              # prometheus is hosted on a different machine.\n              listen = \"${wg-clan-ip}:9273\";\n              metric_version = 2;\n            };\n        };\n      };\n    };\n\n    # Enable home-assistant service\n    services.home-assistant = {\n      enable = true;\n      customComponents = with pkgs.home-assistant-custom-components; [\n        ntfy\n        awtrix\n        moonraker\n      ];\n\n      # List extraComponents here to be installed. The names can be found here:\n      # https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/home-assistant/component-packages.nix\n      # Components listed here will be possible to add via the webUI if not\n      # automatically picked up.\n      extraComponents = [\n        # \"piper\"\n        # \"whisper\"\n        \"bthome\"\n        \"nextcloud\"\n        \"unifi_direct\"\n        \"unifi\"\n        \"esphome\"\n        # \"map\"\n        \"openweathermap\"\n        \"tasmota\"\n        \"icloud\"\n      ];\n\n      # Disable the python checks, they take for ever when building the\n      # configuration\n      # package = (home-assistant-package.overrideAttrs (old: {\n      #   doInstallCheck = false;\n      #   doCheck = false;\n      # }));\n\n      # Configuration generated to /var/lib/hass/configuration.yaml\n      config = {\n\n        /*\n          # M5Stack Atom Echo\n          # Example configuration.yaml\n          homeassistant.media_dirs = {\n            # media =\"/var/lib/hass/media\";\n            media = \"/var/lib/hass/tts\";\n            recording = \"/var/lib/hass/recordings\";\n          };\n        */\n\n        sensor = [\n          {\n            name = \"random_joke\";\n            platform = \"rest\";\n            json_attributes = \"joke\";\n            resource = \"https://icanhazdadjoke.com/\";\n            scan_interval = \"3600\";\n            headers.Accept = \"application/json\";\n          }\n        ];\n\n        conversation.intents.TellJoke = [ \"Witz\" ];\n\n        intent_script.TellJoke = {\n          speech.text = ''{{ state_attr(\"sensor.random_joke\", \"joke\") }}'';\n          action = {\n            service = \"homeassistant.update_entity\";\n            entity_id = \"sensor.random_joke\";\n          };\n        };\n\n        assist_pipeline = { };\n\n        notify = [\n          {\n            name = \"ntfy\";\n            platform = \"rest\";\n            method = \"POST_JSON\";\n            authentication = \"basic\";\n            username = \"nfty\";\n            password = \"!nfty-pass\";\n            data.topic = \"homeassistant\";\n            title_param_name = \"title\";\n            message_param_name = \"message\";\n            resource = \"https://push.pablo.tools\";\n          }\n        ];\n\n        device_tracker =\n          let\n            # Unifi APs\n            ap-ips = [\n              \"192.168.2.110\"\n              \"192.168.2.111\"\n              \"192.168.2.126\"\n            ];\n          in\n          map (host: {\n            inherit host;\n            platform = \"unifi_direct\";\n            username = \"pinpox\";\n            password = \"!unifi-ap-ssh\";\n          }) ap-ips;\n\n        weather = { };\n        sun = { };\n        # icloud = { };\n\n        intent_script.FindIphone = {\n          # speech.text = \"Notified pinpox\";\n          action = {\n            service = \"icloud.play_sound\";\n            data_template.account = \"apple@pablo.tools\";\n            data_template.device_name = \"Apfeltasche (2)\";\n          };\n        };\n\n        ios = {\n          actions = [\n            # Toggle RGB strip\n            {\n              name = \"Toggle RGB\";\n              background_color = \"#24283B\";\n              label = {\n                text = \"RGB-Kette\";\n                color = \"#E5E9F0\";\n              };\n              icon = {\n                icon = \"lightbulb-on\";\n                color = \"#FF5370\";\n              };\n            }\n            {\n              name = \"Toggle Bulbbox\";\n              background_color = \"#24283B\";\n              label = {\n                text = \"Bulbbox\";\n                color = \"#E5E9F0\";\n              };\n              icon = {\n                icon = \"lightbulb-on\";\n                color = \"#E5E9F0\";\n              };\n            }\n            # Toggle Deckenlicht\n            {\n              name = \"Toggle Deckenlicht\";\n              background_color = \"#24283B\";\n              label = {\n                text = \"Deckenlicht\";\n                color = \"#E5E9F0\";\n              };\n              icon = {\n                icon = \"lightbulb-on\";\n                color = \"#E5E9F0\";\n              };\n            }\n          ];\n        };\n\n        # https://home.pablo.tools/developer-tools/event\n        # https://home.pablo.tools/config/automation/dashboard\n\n        \"automation ui\" = \"!include automations.yaml\";\n\n        \"automation manual\" = [\n          {\n            id = \"rackmount_button1\";\n            mode = \"single\";\n            alias = \"Rack Button 1\";\n            description = \"Toggle Lightbulb Box\";\n            trigger = [\n              {\n                type = \"turned_off\";\n                platform = \"device\";\n                device_id = \"f0a65ba3fc5a542ec83a1fc22a36d2e2\";\n                entity_id = \"42b59835bb83659d01cc2ab0da4c429e\";\n                domain = \"binary_sensor\";\n              }\n            ];\n            action = [\n              {\n                type = \"toggle\";\n                device_id = \"a8c96a8429ae8a7a13c058f79c886684\";\n                entity_id = \"switch.lightbulb_box\";\n                domain = \"switch\";\n              }\n            ];\n          }\n          {\n            id = \"rackmount_button2\";\n            mode = \"single\";\n            alias = \"Rack Button 2\";\n            description = \"Toggle RGB Strip\";\n            trigger = [\n              {\n                type = \"turned_off\";\n                platform = \"device\";\n                device_id = \"f0a65ba3fc5a542ec83a1fc22a36d2e2\";\n                entity_id = \"binary_sensor.button_2\";\n                domain = \"binary_sensor\";\n              }\n            ];\n            action = [\n              {\n                type = \"toggle\";\n                device_id = \"d97c93bff99173ae0b3b20d640050508\";\n                entity_id = \"light.rgb_strip_1\";\n                domain = \"light\";\n              }\n            ];\n          }\n\n          {\n            id = \"rackmount_button3\";\n            mode = \"single\";\n            alias = \"Rack Button 3\";\n            description = \"Toggle Ceiling light\";\n            trigger = [\n              {\n                type = \"turned_off\";\n                platform = \"device\";\n                device_id = \"f0a65ba3fc5a542ec83a1fc22a36d2e2\";\n                entity_id = \"binary_sensor.button_3\";\n                domain = \"binary_sensor\";\n              }\n            ];\n            action = [\n              {\n                type = \"toggle\";\n                device_id = \"d71e3f9c22a777149793e6b126f27550\";\n                entity_id = \"switch.deckenlicht\";\n                domain = \"switch\";\n              }\n            ];\n          }\n          {\n            id = \"auto_deckenlicht_toggle\";\n            alias = \"Deckenlicht Toggle\";\n            trigger = [\n              {\n                platform = \"event\";\n                event_type = \"ios.action_fired\";\n                event_data.actionName = \"Toggle Deckenlicht\";\n              }\n            ];\n\n            action = [\n              {\n                type = \"toggle\";\n                device_id = \"d71e3f9c22a777149793e6b126f27550\";\n                entity_id = \"switch.deckenlicht\";\n                domain = \"switch\";\n              }\n            ];\n          }\n          {\n            id = \"auto_rgb_toggle\";\n            alias = \"RGB-Kette Toggle\";\n            trigger = [\n              {\n                platform = \"event\";\n                event_type = \"state_changed\";\n                event_data.entity_id = \"switch.lichterkette\";\n              }\n              {\n                platform = \"event\";\n                event_type = \"ios.action_fired\";\n                event_data.actionName = \"Toggle RGB\";\n              }\n            ];\n\n            condition = [\n              {\n                condition = \"template\";\n                value_template = \"{{ trigger.event.data.old_state.state != 'unavailable' }}\";\n              }\n              {\n                condition = \"template\";\n                value_template = \"{{ trigger.event.data.new_state.state != 'unavailable' }}\";\n              }\n            ];\n\n            action = [\n              {\n                type = \"toggle\";\n                device_id = \"d97c93bff99173ae0b3b20d640050508\";\n                entity_id = \"light.rgb_strip_1\";\n                domain = \"light\";\n              }\n            ];\n          }\n        ];\n\n        # Provides some sane defaults and minimal dependencies\n        default_config = { };\n\n        shelly = { };\n\n        zeroconf = {\n          # default_interface = true;\n        };\n        # Basic settings for home-assistant\n        homeassistant = {\n          name = \"Villa Kunterbunt\";\n          latitude = \"!secret home-latitude\";\n          longitude = \"!secret home-longitude\";\n          elevation = 86;\n          unit_system = \"metric\";\n          time_zone = \"Europe/Berlin\";\n          external_url = \"https://home.pablo.tools\";\n        };\n\n        http = {\n          use_x_forwarded_for = true;\n          trusted_proxies = [\n            (builtins.readFile (\n              config.clan.core.settings.directory + \"/vars/per-machine/porree/wireguard-wg-clan-ip/ipv4/value\"\n            ))\n          ];\n        };\n\n        frontend = { };\n        shopping_list = { };\n        sun = { };\n        config = { };\n        mobile_app = { };\n        cloud = { };\n        system_health = { };\n\n        # Discover some devices automatically\n        # discovery = { };\n\n        # Show some system health data\n        system_health = { };\n\n        # Enable support for tamota devices\n        # tasmota = { };\n\n        # Led strip wifi controller, component needs to be listed explicitely in\n        # extraComponents above\n        # light = [{\n        #   platform = \"flux_led\";\n        #   automatic_add = true;\n        #   devices = { \"192.168.2.106\" = { name = \"flux_led\"; }; };\n        # }];\n\n        # Fritzbox network traffic stats\n        # sensor = [{ platform = \"fritzbox_netmonitor\"; }];\n\n        # Metrics for prometheus\n        prometheus = {\n          namespace = \"hass\";\n        };\n\n        # Enable MQTT\n        mqtt = { };\n\n        logger.default = \"info\";\n        # logger.default = \"debug\";\n\n        influxdb = {\n          api_version = 2;\n          # host = \"vpn.influx.pablo.tools\";\n          host = \"localhost\";\n          port = \"8086\";\n          max_retries = 10;\n          ssl = false;\n          verify_ssl = false;\n          # Authorization is not used for telegraf, but home-assistant requires\n          # passing these parameters\n          token = \"!secret influx-token\";\n          organization = \"pinpox\";\n          bucket = \"home_assistant\";\n        };\n\n        # Track the sun\n        sun = { };\n\n        # Enable mobile app\n        mobile_app = { };\n\n        # Enable configuration UI\n        # config = { };\n\n        # Enable support for tracking state changes over time\n        history = { };\n\n        # Purge tracked history after 10 days\n        recorder.purge_keep_days = 10;\n\n        # View all events in o logbook\n        logbook = { };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/http2irc/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.monitoring-server.http-irc;\n\n  http2irc = pkgs.buildGoModule rec {\n\n    pname = \"http2irc\";\n    version = \"1.0\";\n\n    # TODO use flake inputs\n    src = pkgs.fetchFromGitHub {\n      owner = \"pinpox\";\n      repo = \"http2irc\";\n      rev = \"v${version}\";\n      sha256 = \"sha256-5aHQ3Y0Md0qrJlFju8Nx6S5Ul+SVZOtFrcx90oiVvWo=\";\n    };\n\n    vendorHash = \"sha256-k45e6RSIl3AQdOFQysIwJP9nlYsSFeaUznVIXfbYwLA=\";\n    subPackages = [ \".\" ];\n\n    meta = with lib; {\n      description = \"Webhook reciever to annouce in IRC channels\";\n      homepage = \"https://github.com/pinpox/http2irc\";\n      license = licenses.gpl3;\n      maintainers = with maintainers; [ pinpox ];\n      platforms = platforms.linux;\n    };\n  };\n\n  templateFile = pkgs.writeTextFile {\n    name = \"template.mustache\";\n    text = concatStrings [ \"{{#plain}}{{plain}}{{/plain}}\" ];\n  };\nin\n# port-loki = 3100;\n{\n\n  options.pinpox.services.monitoring-server.http-irc = {\n    enable = mkEnableOption \"http2irc webhook relay\";\n  };\n\n  config = mkIf cfg.enable {\n\n    # User and group\n    users.users.http2irc = {\n      isSystemUser = true;\n      home = \"/var/lib/http2irc\";\n      description = \"http2irc system user\";\n      group = \"http2irc\";\n      createHome = true;\n    };\n\n    users.groups.http2irc = {\n      name = \"http2irc\";\n    };\n\n    clan.core.vars.generators.\"http2irc\" = pinpox-utils.mkEnvGenerator [\n      \"IRC_SASL_PASS\"\n      \"IRC_SASL_USER\"\n      \"IRC_NICK\"\n      \"IRC_BOT_TOKEN\"\n    ];\n\n    # Service\n    systemd.services.http2irc = {\n      wantedBy = [ \"multi-user.target\" ];\n      after = [ \"network.target\" ];\n      description = \"Start http2irc\";\n      serviceConfig = {\n        EnvironmentFile = [\n          config.clan.core.vars.generators.http2irc.files.\"envfile\".path\n        ];\n        Environment = [\n          \"IRC_TEMPLATE='${templateFile}'\"\n          \"IRC_CHANNEL='#lounge-rocks'\"\n          \"IRC_DEBUG='false'\"\n          \"IRC_LISTEN=localhost:8989\"\n          \"IRC_NOTICE='true'\"\n          \"IRC_SERVER='irc.freenode.net:7000'\"\n        ];\n        WorkingDirectory = \"/var/lib/http2irc\";\n        User = \"http2irc\";\n        ExecStart = \"${http2irc}/bin/http2irc\";\n        Restart = \"on-failure\";\n        RestartSec = \"5s\";\n      };\n    };\n\n    # Reverse proxy\n  };\n}\n"
  },
  {
    "path": "modules/immich/default.nix",
    "content": "{\n  config,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.immich;\nin\n{\n\n  options.pinpox.services.immich = {\n    enable = mkEnableOption \"immich photo gallery\";\n\n    host = mkOption {\n      type = types.str;\n      default = \"photos.0cx.de\";\n      description = \"Host serving immich\";\n      example = \"pics.0cx.de\";\n    };\n\n  };\n\n  config = mkIf cfg.enable {\n\n    # services.immich-public-proxy.enable\n    # services.immich-public-proxy.immichUrl\n    # services.immich-public-proxy.openFirewall\n    # services.immich-public-proxy.package\n    # services.immich-public-proxy.port\n    # services.immich-public-proxy.settings\n\n    services.immich = {\n\n      enable = true;\n      host = \"127.0.0.1\";\n\n      # environment\n      # openFirewall\n      # secretsFile\n      mediaLocation = \"/mnt/storagebox/photos\";\n\n      # settings\n      # Configuration for Immich. See https://immich.app/docs/install/config-file/ or\n      # navigate to https://my.immich.app/admin/system-settings for options and\n      # defaults. Setting it to null allows configuring Immich in the web interface.\n      # You can load secret values from a file in this configuration by setting\n      # somevalue._secret = \"/path/to/file\" instead of setting somevalue directly.\n\n      settings = {\n\n        server.externalDomain = \"https://${cfg.host}\";\n        storageTemplate = {\n          enabled = true;\n          hashVerificationEnabled = true;\n          template = \"{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}\";\n        };\n\n      };\n      #   storageTemplate = {\n      #     enabled = true;\n      #     hashVerificationEnabled = true;\n      #     # template = \"{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}\";\n      #   };\n      #   #   passwordLogin.enabled = false;\n      #   #   oauth = { };\n      # };\n\n      # \"oauth\": {\n      #    \"autoLaunch\": false,\n      #    \"autoRegister\": true,\n      #    \"buttonText\": \"Login with OAuth\",\n      #    \"clientId\": \"\",\n      #    \"clientSecret\": \"\",\n      #    \"defaultStorageQuota\": null,\n      #    \"enabled\": false,\n      #    \"issuerUrl\": \"\",\n      #    \"mobileOverrideEnabled\": false,\n      #    \"mobileRedirectUri\": \"\",\n      #    \"profileSigningAlgorithm\": \"none\",\n      #    \"roleClaim\": \"immich_role\",\n      #    \"scope\": \"openid email profile\",\n      #    \"signingAlgorithm\": \"RS256\",\n      #    \"storageLabelClaim\": \"preferred_username\",\n      #    \"storageQuotaClaim\": \"immich_quota\",\n      #    \"timeout\": 30000,\n      #    \"tokenEndpointAuthMethod\": \"client_secret_post\"\n      #  },\n      #  },\n\n    };\n\n    # Reverse proxy\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"${cfg.host}\".extraConfig =\n        \"reverse_proxy 127.0.0.1:${toString config.services.immich.port}\";\n    };\n\n    # Mount storagebox\n    pinpox.defaults.storagebox = {\n      enable = true;\n      mountOnAccess = false;\n    };\n\n    # Add immich user to storage-users group for access to storagebox\n    users.users.${config.services.immich.user}.extraGroups = [ \"storage-users\" ];\n\n    # Ensure storagebox is mounted before immich starts\n    systemd.services.immich-server = {\n      requires = [ \"mnt-storagebox.mount\" ];\n      after = [ \"mnt-storagebox.mount\" ];\n    };\n  };\n}\n"
  },
  {
    "path": "modules/jitsi-matrix-presence/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  jitsi-matrix-presence,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.jitsi-matrix-presence;\n  pinpox-utils = import ../../utils { inherit pkgs; };\n  mkPres = JITSI_ROOMS: JITSI_SERVER: ROOM_ID: port: {\n\n    wantedBy = [ \"multi-user.target\" ];\n    environment = {\n      inherit JITSI_ROOMS JITSI_SERVER ROOM_ID;\n      HOMESERVER_URL = \"https://matrix.org\";\n      USER_ID = \"@alertus-maximus:matrix.org\";\n      LISTEN_ADDRESS = \"0.0.0.0:${port}\";\n    };\n\n    serviceConfig = {\n      EnvironmentFile = [\n        config.clan.core.vars.generators.\"jitsi-presence\".files.\"envfile\".path\n      ];\n      DynamicUser = true;\n      ExecStart = \"${jitsi-matrix-presence.packages.x86_64-linux.default}/bin/jitsi-presence\";\n      Restart = \"on-failure\";\n      RestartSec = \"5s\";\n    };\n  };\n\nin\n{\n\n  options.pinpox.services.jitsi-matrix-presence = {\n    enable = mkEnableOption \"Jitsi presence notification service\";\n  };\n\n  config = mkIf cfg.enable {\n\n    networking.firewall.allowedTCPPorts = [\n      8226\n      8227\n      8228\n    ];\n\n    clan.core.vars.generators.\"jitsi-presence\" = pinpox-utils.mkEnvGenerator [ \"ACCESS_TOKEN\" ];\n\n    systemd.services.jitsi-matrix-presence-krebs =\n      mkPres \"krebs,nixos\" \"https://jitsi.lassul.us\" \"!bohcSYPVoePqBDWlvE:hackint.org\"\n        \"8226\";\n  };\n}\n"
  },
  {
    "path": "modules/kf-homepage/default.nix",
    "content": "{ lib, config, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.kf-homepage;\nin\n{\n\n  options.pinpox.services.kf-homepage.enable = mkEnableOption \"Krosse Flagge Homepage\";\n\n  config = mkIf cfg.enable {\n    services.caddy = {\n      enable = true;\n      virtualHosts = {\n        \"0cx.de\".extraConfig = ''\n          root * ${./page}\n          encode zstd gzip\n          file_server\n        '';\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/kf-homepage/page/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Krosse Flagge</title>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  </head>\n  <body>\n    <style>\n      html,\n      body {\n        background-color: black;\n        height: 100%;\n      }\n\n      .parent {\n        height: 100%;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        flex-direction: column;\n      }\n\n      .button {\n        border: 5px solid #0f0;\n        box-shadow: 10px 10px #0f0;\n        color: #0f0;\n        padding: 10px;\n        text-align: center;\n        text-decoration: none;\n        display: inline-block;\n        font-size: 20px;\n        cursor: crosshair;\n        margin: 20px 20px;\n      }\n\n      .button:hover {\n        color: red;\n      }\n\n      .button:active {\n        box-shadow: 0 0;\n        transform: translate(10px, 10px);\n      }\n    </style>\n\n    <div class=\"parent\">\n      <img src=\"dance.gif\" />\n      <div>\n        <!-- <a href=\"https://pads.0cx.de\" class=\"button\">pads</a> -->\n        <a href=\"https://git.0cx.de\" class=\"button\">git</a>\n        <a href=\"https://irc.0cx.de\" class=\"button\">irc</a>\n        <a href=\"https://news.0cx.de\" class=\"button\">news</a>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "modules/locale/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.locale;\nin\n{\n\n  options.pinpox.defaults.locale = {\n    enable = mkEnableOption \"Locale defaults\";\n    automatic-timezone = mkEnableOption \"Automatic timezone based on location (for mobile machines)\";\n  };\n\n  config = mkIf cfg.enable (mkMerge [\n    {\n      # Set localization and tty options\n      i18n.defaultLocale = \"en_DK.UTF-8\";\n\n      i18n.supportedLocales = [\n        \"en_US.UTF-8/UTF-8\"\n        \"en_DK.UTF-8/UTF-8\"\n      ];\n\n      console = {\n        keyMap = \"colemak\";\n      };\n\n      time.timeZone = mkDefault \"Europe/Berlin\";\n    }\n\n    (mkIf cfg.automatic-timezone {\n      time.timeZone = null;\n      services.automatic-timezoned.enable = true;\n      services.geoclue2.geoProviderUrl = \"https://api.beacondb.net/v1/geolocate\";\n    })\n  ]);\n}\n"
  },
  {
    "path": "modules/lvm-grub/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.lvm-grub;\nin\n{\n\n  options.pinpox.defaults.lvm-grub = {\n    enable = mkEnableOption \"LVM/Grub defaults\";\n  };\n  config = mkIf cfg.enable {\n\n    # Use the grub2 boot loader.\n    boot = {\n\n      loader = {\n        grub.enable = true;\n\n        # Required for LVM\n        grub.device = \"nodev\";\n\n        # Use UEFI support\n        grub.efiSupport = true;\n        grub.efiInstallAsRemovable = true;\n        # efi.canTouchEfiVariables = true;\n        # useOSProber = true;\n      };\n\n      # /tmp is cleaned after each reboot\n      tmp.cleanOnBoot = true;\n    };\n  };\n}\n"
  },
  {
    "path": "modules/miniflux/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.miniflux;\n  oidcSecretPath = config.clan.core.vars.generators.\"miniflux-oidc\".files.client_secret.path;\nin\n{\n\n  options.pinpox.services.miniflux = {\n    enable = mkEnableOption \"miniflux RSS reader\";\n  };\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"miniflux\" = {\n      files.credentials = { };\n\n      runtimeInputs = with pkgs; [\n        coreutils\n        xkcdpass\n      ];\n\n      script = ''\n        mkdir -p $out\n        printf \"ADMIN_USERNAME=admin\\nADMIN_PASSWORD='%s'\" \"$(xkcdpass -d-)\" > $out/credentials\n      '';\n    };\n\n    clan.core.vars.generators.\"miniflux-oidc\" = {\n      share = true;\n      files.client_secret = { };\n      files.client_secret_hash = { };\n      runtimeInputs = with pkgs; [\n        coreutils\n        openssl\n        authelia\n        gnused\n      ];\n      script = ''\n        mkdir -p $out\n        openssl rand -hex 32 > $out/client_secret\n        authelia crypto hash generate argon2 --password \"$(cat $out/client_secret)\" \\\n          | sed 's/^Digest: //' > $out/client_secret_hash\n      '';\n    };\n\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"news.0cx.de\".extraConfig =\n        \"reverse_proxy ${config.services.miniflux.config.LISTEN_ADDR}\";\n    };\n\n    systemd.services.miniflux.serviceConfig.LoadCredential = [\n      \"oauth2_client_secret_file:${oidcSecretPath}\"\n    ];\n\n    services.miniflux = {\n      enable = true;\n      config = {\n        OAUTH2_USER_CREATION = \"1\";\n        DISABLE_LOCAL_AUTH = \"1\";\n        CLEANUP_FREQUENCY = \"48\";\n        LISTEN_ADDR = \"127.0.0.1:8787\";\n        OAUTH2_PROVIDER = \"oidc\";\n        OAUTH2_CLIENT_ID = \"miniflux\";\n        OAUTH2_CLIENT_SECRET_FILE = \"/run/credentials/miniflux.service/oauth2_client_secret_file\";\n        OAUTH2_REDIRECT_URL = \"https://news.0cx.de/oauth2/oidc/callback\";\n        OAUTH2_OIDC_DISCOVERY_ENDPOINT = \"https://auth.pablo.tools\";\n        OAUTH2_OIDC_PROVIDER_NAME = \"pablo.tools\";\n      };\n      adminCredentialsFile = config.clan.core.vars.generators.\"miniflux\".files.\"credentials\".path;\n    };\n\n  };\n}\n"
  },
  {
    "path": "modules/minio/default.nix",
    "content": "{\n  lib,\n  config,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.minio;\nin\n{\n\n  options.pinpox.services.minio = {\n    enable = mkEnableOption \"minio s3 config\";\n  };\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"minio\" = rec {\n      files.root-credentials = { };\n      validation.script = script;\n\n      runtimeInputs = with pkgs; [\n        coreutils\n        xkcdpass\n      ];\n\n      script = # sh\n        ''\n          mkdir -p $out\n          printf \"MINIO_ROOT_USER=admin\\nMINIO_ROOT_PASSWORD='%s'\" \"$(xkcdpass -d-)\" > $out/root-credentials\n        '';\n    };\n\n    networking.firewall.interfaces.wg-clan.allowedTCPPorts = [\n      9000\n      9001\n    ];\n\n    services.minio =\n\n      let\n        wg-clan-ip = builtins.elemAt (builtins.match \"(.*)/.*\" (builtins.elemAt config.networking.wireguard.interfaces.wg-clan.ips 0)) 0;\n\n      in\n      {\n        enable = true;\n        listenAddress = \"${wg-clan-ip}:9000\";\n        consoleAddress = \"${wg-clan-ip}:9001\";\n        region = \"eu-central-1\";\n        rootCredentialsFile = \"${config.clan.core.vars.generators.\"minio\".files.\"root-credentials\".path}\";\n        dataDir = [ \"/mnt/data/minio/data\" ];\n        configDir = \"/mnt/data/minio/config\";\n      };\n\n    systemd.services.minio = {\n\n      environment = {\n        MINIO_SERVER_URL = \"https://vpn.s3.pablo.tools\";\n        MINIO_BROWSER_REDIRECT_URL = \"https://vpn.minio.pablo.tools\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/minio/policies/nextcloud-external.json",
    "content": "{\n  \"ID\": \"NextcloudExternalRepoPolicy\",\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"AllowObjects\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:DeleteObject\", \"s3:GetObject\", \"s3:PutObject\"],\n      \"Resource\": [\"arn:aws:s3:::nextcloud-external/*\"]\n    },\n    {\n      \"Sid\": \"AllowRepo\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\"],\n      \"Resource\": [\"arn:aws:s3:::nextcloud-external\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "modules/minio/policies/restic.json",
    "content": "{\n  \"ID\": \"ResticRepoPolicy\",\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"AllowObjects\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:DeleteObject\", \"s3:GetObject\", \"s3:PutObject\"],\n      \"Resource\": [\"arn:aws:s3:::restic/*\"]\n    },\n    {\n      \"Sid\": \"AllowRepo\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\"],\n      \"Resource\": [\"arn:aws:s3:::restic\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "modules/networking/default.nix",
    "content": "{\n  config,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.networking;\nin\n{\n\n  options.pinpox.defaults.networking = {\n    enable = mkEnableOption \"Network defaults\";\n  };\n\n  config = mkIf cfg.enable {\n\n    networking = {\n\n      # Additional hosts to put in /etc/hosts\n      extraHosts =\n\n        let\n          wgIp =\n            host:\n            builtins.readFile (\n              config.clan.core.settings.directory + \"/vars/per-machine/${host}/wireguard-wg-clan-ip/ipv4/value\"\n            );\n          mkWgEntry = host: \"${wgIp host} ${host}.wireguard\";\n          porreeWgIp = wgIp \"porree\";\n        in\n        ''\n          # Wireguard\n          ${mkWgEntry \"porree\"}\n          ${mkWgEntry \"kartoffel\"}\n          ${mkWgEntry \"birne\"}\n          ${mkWgEntry \"kfbox\"}\n\n          # Public\n          94.16.114.42 porree-old.public\n          94.16.108.229 porree.public\n          46.38.242.17 kfbox.public\n          93.177.66.52 kfbox-old\n          5.181.48.121 mega.public\n\n          # VPN protected services (porree via wireguard)\n          ${porreeWgIp} vpn.motion.pablo.tools\n          ${porreeWgIp} vpn.alerts.pablo.tools\n          ${porreeWgIp} vpn.prometheus.pablo.tools\n          ${porreeWgIp} vpn.notify.pablo.tools\n          ${porreeWgIp} vpn.s3.pablo.tools\n          ${porreeWgIp} vpn.minio.pablo.tools\n        '';\n    };\n  };\n}\n"
  },
  {
    "path": "modules/nextcloud/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.nextcloud;\n\n  # Pin Nextcloud major version.\n  # Refer to upstream docs for updating major versions\n  package = pkgs.nextcloud33;\n\nin\n{\n\n  options.pinpox.services.nextcloud = {\n    enable = mkEnableOption \"Nextcloud\";\n  };\n\n  config = mkIf cfg.enable {\n\n    # Backup\n    pinpox.services.restic-client.backup-paths-onsite = [ \"/var/lib/nextcloud\" ];\n\n    pinpox.services.restic-client.backup-paths-offsite = [\n      # TODO Plan on how to backup nextcloud data\n      # \"${config.services.nextcloud.home}/data\"\n      \"${config.services.nextcloud.home}/config\"\n      # \"${config.services.nextcloud.home}/store-apps\"\n    ];\n\n    services.postgresql.package = pkgs.postgresql_17;\n\n    clan.core.vars.generators.\"nextcloud\" = {\n\n      files.admin-pass-file = {\n        owner = \"nextcloud\";\n        # path = \"/var/lib/nextcloud/admin-pass\";\n      };\n\n      runtimeInputs = with pkgs; [\n        coreutils\n        xkcdpass\n      ];\n\n      script = ''\n        mkdir -p $out\n        xkcdpass > $out/admin-pass-file\n      '';\n    };\n\n    clan.core.vars.generators.\"nextcloud-smtp\" = {\n      files.smtp-secret.owner = \"nextcloud\";\n      prompts.smtp-password.persist = true;\n\n      runtimeInputs = with pkgs; [ jq ];\n\n      script = ''\n        mkdir -p $out\n        jq -n --arg pw \"$(cat \"$prompts/smtp-password\")\" \\\n          '{\"mail_smtppassword\": $pw}' > $out/smtp-secret\n      '';\n    };\n\n    services.phpfpm.pools.nextcloud.settings = {\n      \"listen.owner\" = config.services.caddy.user;\n      \"listen.group\" = config.services.caddy.group;\n    };\n\n    services.nextcloud = {\n      caching.apcu = true;\n      caching.redis = true;\n      configureRedis = true;\n\n      phpOptions.\"opcache.interned_strings_buffer\" = \"64\";\n      # opcache.memory_consumption=256\n      # opcache.interned_strings_buffer=64\n      # opcache.max_accelerated_files=100000\n\n      secretFile = config.clan.core.vars.generators.\"nextcloud-smtp\".files.\"smtp-secret\".path;\n\n      settings = {\n        mail_smtpmode = \"smtp\";\n        mail_smtphost = \"r19.hallo.cloud\";\n        mail_smtpport = 587;\n        mail_smtpsecure = \"\";\n        mail_smtpauth = true;\n        mail_smtpauthtype = \"LOGIN\";\n        mail_smtpname = \"mail@0cx.de\";\n        mail_from_address = \"files\";\n        mail_domain = \"pablo.tools\";\n\n        maintenance_window_start = \"4\";\n\n        trusted_proxies = [\n          (builtins.readFile (\n            config.clan.core.settings.directory + \"/vars/per-machine/porree/wireguard-wg-clan-ip/ipv4/value\"\n          ))\n          \"94.16.108.229\"\n        ];\n\n        trusted_domains = [ \"birne.wireguard\" ];\n        default_phone_region = \"DE\";\n\n        enabledPreviewProviders = [\n          \"OC\\\\Preview\\\\BMP\"\n          \"OC\\\\Preview\\\\GIF\"\n          \"OC\\\\Preview\\\\JPEG\"\n          \"OC\\\\Preview\\\\Krita\"\n          \"OC\\\\Preview\\\\MarkDown\"\n          \"OC\\\\Preview\\\\MP3\"\n          \"OC\\\\Preview\\\\OpenDocument\"\n          \"OC\\\\Preview\\\\PNG\"\n          \"OC\\\\Preview\\\\TXT\"\n          \"OC\\\\Preview\\\\XBitmap\"\n          \"OC\\\\Preview\\\\HEIC\"\n          \"OC\\\\Preview\\\\Movie\"\n        ];\n      };\n\n      enable = true;\n\n      inherit package;\n\n      # Use HTTPS for links\n      https = true;\n      # overwriteProtocol = \"https\";\n      hostName = \"files.pablo.tools\";\n\n      # Disable adding apps from the app store, apps are only configured\n      # declaratively via nix\n      appstoreEnable = false;\n      extraApps = {\n        inherit (package.packages.apps)\n          mail\n          calendar\n          contacts\n\n          # TODO https://github.com/pulsejet/memories/issues/1625\n          memories\n\n          previewgenerator\n          # maps\n          twofactor_webauthn\n\n          # TODO re-enable after https://github.com/NixOS/nixpkgs/pull/400158\n          # recognize\n\n          music\n          # phonetrack\n          ;\n      };\n\n      # phpExtraExtensions = [];\n      home = \"/var/lib/nextcloud\";\n\n      poolSettings = {\n        pm = \"dynamic\";\n        \"pm.max_children\" = \"160\";\n        \"pm.max_requests\" = \"700\";\n        \"pm.max_spare_servers\" = \"120\";\n        \"pm.min_spare_servers\" = \"40\";\n        \"pm.start_servers\" = \"40\";\n      };\n\n      config = {\n\n        # Database\n        dbtype = \"pgsql\";\n        dbuser = \"nextcloud\";\n        dbhost = \"/run/postgresql\";\n        dbname = \"nextcloud\";\n\n        # Admin user\n        adminuser = \"pinpox\";\n        adminpassFile = \"${config.clan.core.vars.generators.\"nextcloud\".files.\"admin-pass-file\".path}\";\n      };\n\n    };\n\n    environment.systemPackages = with pkgs; [\n      exiftool\n      ffmpeg\n    ];\n\n    # To run nginx alongside caddy for nextcloud only\n    services.nginx.enable = false;\n    # services.nginx.virtualHosts.\"files.pablo.tools\".listen = [{ addr = \"0.0.0.0\"; port = 8080; }];\n\n    # reverse_proxy http://127.0.0.1:8080\n    services.caddy.virtualHosts = {\n\n      \"files.pablo.tools\".extraConfig = ''\n        encode zstd gzip\n\n        root * ${config.services.nginx.virtualHosts.\"files.pablo.tools\".root}\n        root /nix-apps/* ${config.services.nginx.virtualHosts.\"files.pablo.tools\".root}\n\n        redir /.well-known/carddav /remote.php/dav 301\n        redir /.well-known/caldav /remote.php/dav 301\n        redir /.well-known/* /index.php{uri} 301\n        redir /remote/* /remote.php{uri} 301\n\n        header {\n          Strict-Transport-Security max-age=31536000\n          Permissions-Policy interest-cohort=()\n          X-Content-Type-Options nosniff\n          X-Frame-Options SAMEORIGIN\n          Referrer-Policy strict-origin-when-cross-origin\n          X-XSS-Protection \"1; mode=block\"\n          X-Permitted-Cross-Domain-Policies none\n          X-Robots-Tag \"noindex, nofollow\"\n          -X-Powered-By\n        }\n\n        php_fastcgi unix//run/phpfpm/nextcloud.sock {\n          root ${config.services.nginx.virtualHosts.\"files.pablo.tools\".root}\n          env front_controller_active true\n          env modHeadersAvailable true\n        }\n\n        @forbidden {\n          path /build/* /tests/* /config/* /lib/* /3rdparty/* /templates/* /data/*\n          path /.* /autotest* /occ* /issue* /indie* /db_* /console*\n          not path /.well-known/*\n        }\n        error @forbidden 404\n\n        @immutable {\n          path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite\n          query v=*\n        }\n        header @immutable Cache-Control \"max-age=15778463, immutable\"\n\n        @static {\n          path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite\n          not query v=*\n        }\n        header @static Cache-Control \"max-age=15778463\"\n\n        @woff2 path *.woff2\n        header @woff2 Cache-Control \"max-age=604800\"\n\n        file_server\n      '';\n    };\n\n    # Fix for memories\n    # https://memories.gallery/troubleshooting/#trigger-compatibility-mode\n    systemd.services.nextcloud-cron = {\n      path = [ pkgs.perl ];\n    };\n\n    # Database configuration\n    services.postgresql.enable = true;\n\n    # Ensure that postgres is running *before* running the setup\n    systemd.services.\"nextcloud-setup\" = {\n      requires = [ \"postgresql.service\" ];\n      after = [ \"postgresql.service\" ];\n    };\n  };\n}\n"
  },
  {
    "path": "modules/nix-common/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  flake-self,\n  nixpkgs,\n  nix-index-database,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.nix;\nin\n{\n\n  options.pinpox.defaults.nix = {\n    enable = mkEnableOption \"Nix defaults\";\n  };\n\n  imports = [ nix-index-database.nixosModules.nix-index ];\n\n  config = mkIf cfg.enable {\n\n    boot.loader.grub.configurationLimit = 5;\n\n    _module.args.pinpox-utils = import ../../utils { inherit pkgs; };\n\n    # Use nix-index-database for comma\n    programs.nix-index-database.comma.enable = true;\n\n    # Generates a .prom file that can be scraped with prometheus to monitor the\n    # current nixpkgs version\n    environment.etc.\"nix/flake_inputs.prom\" = {\n      mode = \"0555\";\n      text = ''\n        # HELP flake_registry_last_modified Last modification date of flake input in unixtime\n        # TYPE flake_input_last_modified gauge\n        ${concatStringsSep \"\\n\" (\n          map (\n            i:\n            ''flake_input_last_modified{input=\"${i}\",${\n              concatStringsSep \",\" (\n                mapAttrsToList (n: v: ''${n}=\"${v}\"'') (\n                  filterAttrs (\n                    n: v: (builtins.tryEval (builtins.typeOf v == \"string\")).value or false\n                  ) flake-self.inputs.\"${i}\"\n                )\n              )\n            }} ${toString flake-self.inputs.\"${i}\".lastModified or 0}''\n          ) (attrNames flake-self.inputs)\n        )}\n      '';\n    };\n\n    # Set the $NIX_PATH entry for nixpkgs. This is necessary in\n    # this setup with flakes, otherwise commands like `nix-shell\n    # -p pkgs.htop` will keep using an old version of nixpkgs.\n    # With this entry in $NIX_PATH it is possible (and\n    # recommended) to remove the `nixos` channel for both users\n    # and root e.g. `nix-channel --remove nixos`. `nix-channel\n    # --list` should be empty for all users afterwards\n    nix.nixPath = [ \"nixpkgs=flake:nixpkgs\" ];\n    nixpkgs.overlays = [ flake-self.overlays.default ];\n\n    # Let 'nixos-version --json' know the Git revision of this flake.\n    system.configurationRevision = nixpkgs.lib.mkIf (flake-self ? rev) flake-self.rev;\n    nix.registry.nixpkgs.flake = nixpkgs;\n    nix.registry.pinpox.flake = flake-self;\n\n    # Allow unfree licenced packages\n    nixpkgs.config.allowUnfree = true;\n\n    clan.core.vars.generators.\"nix\" = {\n      prompts.nix-access-tokens.persist = true;\n      share = true;\n      files.\"nix-access-tokens\" = {\n        group = \"wheel\";\n        mode = \"0440\";\n      };\n    };\n\n    # Enable flakes\n    nix = {\n\n      # Enable flakes\n      package = pkgs.nixVersions.stable;\n\n      extraOptions = ''\n        fallback = true\n        connect-timeout = 100\n        stalled-download-timeout = 100\n        !include ${config.clan.core.vars.generators.\"nix\".files.\"nix-access-tokens\".path}\n      '';\n\n      settings = {\n\n        auto-allocate-uids = true;\n\n        system-features = [ \"uid-range\" ];\n\n        experimental-features = [\n          \"nix-command\"\n          \"flakes\"\n\n          \"auto-allocate-uids\"\n          \"cgroups\"\n        ];\n\n        trusted-users = [ \"@wheel\" ];\n\n        trusted-public-keys = [\n          \"nix-cache:4FILs79Adxn/798F8qk2PC1U8HaTlaPqptwNJrXNA1g=\"\n          \"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\"\n        ];\n\n        # nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\n\n        substituters = [\n          \"https://cache.nixos.org\"\n          \"https://nix-community.cachix.org\"\n          \"https://cache.lounge.rocks/nix-cache\"\n        ];\n\n        trusted-substituters = [\n          \"https://cache.nixos.org\"\n          \"https://cache.lounge.rocks\"\n        ];\n\n        # Save space by hardlinking store files\n        auto-optimise-store = true;\n\n        # Users allowed to run nix\n        allowed-users = [ \"root\" ];\n      };\n\n      # Clean up old generations after 30 days\n      gc = {\n        automatic = true;\n        dates = \"weekly\";\n        options = \"--delete-older-than 30d\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/ntfy-sh/default.nix",
    "content": "{ lib, config, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.ntfy-sh;\n  ntfy-port = \"8090\";\n  ntfy-host = \"push.pablo.tools\";\nin\n{\n\n  options.pinpox.services.ntfy-sh = {\n    enable = mkEnableOption \"ntfy-sh notification server\";\n  };\n\n  config = mkIf cfg.enable {\n\n    services.ntfy-sh = {\n      enable = true;\n      settings = {\n        behind-proxy = true;\n        listen-http = \"127.0.0.1:${ntfy-port}\";\n        base-url = \"https://${ntfy-host}\";\n        auth-file = \"/var/lib/ntfy-sh/user.db\";\n        auth-default-access = \"deny-all\";\n        upstream-base-url = \"https://ntfy.sh\";\n        # https://github.com/binwiederhier/ntfy/issues/459\n        web-root = \"disable\"; # Set to \"app\" to enable web UI\n      };\n    };\n\n    users.users.ntfy-sh = {\n      home = \"/var/lib/ntfy-sh\";\n      createHome = true;\n    };\n\n    services.caddy.virtualHosts.\"${ntfy-host}\".extraConfig = ''\n      reverse_proxy 127.0.0.1:${ntfy-port}\n    '';\n  };\n}\n"
  },
  {
    "path": "modules/opencloud/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.opencloud;\nin\n{\n\n  options.pinpox.services.opencloud = {\n    enable = mkEnableOption \"OpenCloud file sync and sharing\";\n\n    host = mkOption {\n      type = types.str;\n      default = \"cloud.pablo.tools\";\n      description = \"Host serving OpenCloud\";\n    };\n\n    port = mkOption {\n      type = types.port;\n      default = 9200;\n      description = \"Port OpenCloud listens on\";\n    };\n\n    oidcIssuer = mkOption {\n      type = types.str;\n      default = \"https://auth.pablo.tools\";\n      description = \"OIDC issuer URL (Authelia)\";\n    };\n\n    oidcClientId = mkOption {\n      type = types.str;\n      default = \"opencloud\";\n      description = \"OIDC client ID\";\n    };\n\n  };\n\n  config = mkIf cfg.enable {\n\n    # Backup\n    pinpox.services.restic-client.backup-paths-onsite = [ \"/var/lib/opencloud\" ];\n    pinpox.services.restic-client.backup-paths-offsite = [ \"/var/lib/opencloud\" ];\n\n    # Generate required secrets\n    clan.core.vars.generators.\"opencloud\" = {\n      files.envfile = {\n        owner = \"opencloud\";\n        group = \"opencloud\";\n      };\n\n      runtimeInputs = with pkgs; [\n        coreutils\n        openssl\n        util-linux\n      ];\n\n      script = ''\n        mkdir -p $out\n\n        # Generate required secrets (matching opencloud init output)\n        SERVICE_ACCOUNT_ID=$(uuidgen)\n        STORAGE_UUID=$(uuidgen)\n\n        # Generate LDAP passwords (same password used by multiple services)\n        REVA_PASSWORD=$(openssl rand -base64 24)\n        IDM_PASSWORD=$(openssl rand -base64 24)\n        IDP_PASSWORD=$(openssl rand -base64 24)\n        ADMIN_PASSWORD=$(openssl rand -base64 24)\n\n        {\n          echo \"OC_JWT_SECRET=$(openssl rand -base64 24)\"\n          echo \"OC_TRANSFER_SECRET=$(openssl rand -base64 24)\"\n          echo \"OC_MACHINE_AUTH_API_KEY=$(openssl rand -base64 24)\"\n          echo \"OC_SYSTEM_USER_ID=$(uuidgen)\"\n          echo \"OC_SYSTEM_USER_API_KEY=$(openssl rand -base64 24)\"\n          echo \"OC_ADMIN_USER_ID=$(uuidgen)\"\n          echo \"GRAPH_APPLICATION_ID=$(uuidgen)\"\n          echo \"OC_SERVICE_ACCOUNT_ID=$SERVICE_ACCOUNT_ID\"\n          echo \"OC_SERVICE_ACCOUNT_SECRET=$(openssl rand -base64 24)\"\n          echo \"STORAGE_USERS_MOUNT_ID=$STORAGE_UUID\"\n          echo \"GATEWAY_STORAGE_USERS_MOUNT_ID=$STORAGE_UUID\"\n          echo \"THUMBNAILS_TRANSFER_SECRET=$(openssl rand -base64 24)\"\n          echo \"COLLABORATION_WOPI_SECRET=$(openssl rand -base64 24)\"\n          echo \"OC_URL_SIGNING_SECRET=$(openssl rand -base64 24)\"\n\n          # LDAP bind passwords for internal services\n          echo \"IDM_ADMIN_PASSWORD=$ADMIN_PASSWORD\"\n          echo \"IDM_SVC_PASSWORD=$IDM_PASSWORD\"\n          echo \"IDM_REVASVC_PASSWORD=$REVA_PASSWORD\"\n          echo \"IDM_IDPSVC_PASSWORD=$IDP_PASSWORD\"\n          echo \"GRAPH_LDAP_BIND_PASSWORD=$IDM_PASSWORD\"\n          echo \"OC_LDAP_BIND_PASSWORD=$REVA_PASSWORD\"\n          echo \"LDAP_BIND_PASSWORD=$REVA_PASSWORD\"\n          echo \"AUTH_BASIC_LDAP_BIND_PASSWORD=$REVA_PASSWORD\"\n          echo \"GROUPS_LDAP_BIND_PASSWORD=$REVA_PASSWORD\"\n          echo \"USERS_LDAP_BIND_PASSWORD=$REVA_PASSWORD\"\n          echo \"IDP_LDAP_BIND_PASSWORD=$IDP_PASSWORD\"\n        } > $out/envfile\n      '';\n    };\n\n    services.opencloud = {\n      enable = true;\n      url = \"https://${cfg.host}\";\n      address = \"127.0.0.1\";\n      port = cfg.port;\n      environmentFile = config.clan.core.vars.generators.\"opencloud\".files.\"envfile\".path;\n\n      environment = {\n        OC_INSECURE = \"true\";\n        OC_LOG_LEVEL = \"warn\";\n\n        # Disable TLS on proxy - Caddy handles TLS termination\n        PROXY_TLS = \"false\";\n\n        # Disable built-in IDP since we use external OIDC (Authelia)\n        OC_EXCLUDE_RUN_SERVICES = \"idp\";\n\n        # External OIDC configuration\n        OC_OIDC_ISSUER = cfg.oidcIssuer;\n        PROXY_OIDC_ISSUER = cfg.oidcIssuer;\n        PROXY_OIDC_REWRITE_WELLKNOWN = \"false\";\n        PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD = \"none\";\n        PROXY_OIDC_SKIP_USER_INFO = \"false\";\n\n        # OIDC client ID for web\n        WEB_OIDC_CLIENT_ID = cfg.oidcClientId;\n\n        # Auto-provision accounts from OIDC\n        PROXY_AUTOPROVISION_ACCOUNTS = \"true\";\n        PROXY_AUTOPROVISION_CLAIM_USERNAME = \"preferred_username\";\n        PROXY_AUTOPROVISION_CLAIM_EMAIL = \"email\";\n        PROXY_AUTOPROVISION_CLAIM_DISPLAYNAME = \"name\";\n        PROXY_AUTOPROVISION_CLAIM_GROUPS = \"groups\";\n        PROXY_USER_OIDC_CLAIM = \"preferred_username\";\n        PROXY_USER_CS3_CLAIM = \"username\";\n        GRAPH_USERNAME_MATCH = \"none\";\n\n        # Avoid port conflicts with prometheus exporters (9115 = blackbox_exporter)\n        WEB_HTTP_ADDR = \"127.0.0.1:9105\";\n        WEBDAV_HTTP_ADDR = \"127.0.0.1:9116\";\n        COLLABORATION_HTTP_ADDR = \"127.0.0.1:9300\";\n        COLLABORATION_GRPC_ADDR = \"127.0.0.1:9301\";\n      };\n\n      # OpenCloud configuration (prevents init service from running)\n      settings = {\n        # Main opencloud config - non-empty value prevents opencloud init from running\n        opencloud = {\n          graph.spaces.insecure = true;\n          proxy.insecure_backends = true;\n        };\n\n        # Proxy config\n        proxy.csp_config_file_location = \"/etc/opencloud/csp.yaml\";\n\n        # CSP - allow connecting to external OIDC provider\n        csp.directives = {\n          \"connect-src\" = [\"https://${cfg.host}/\" cfg.oidcIssuer];\n          \"frame-src\" = [\"https://${cfg.host}/\" cfg.oidcIssuer];\n          \"script-src\" = [\"'self'\" \"'unsafe-inline'\" \"'unsafe-eval'\"];\n        };\n\n        # Web UI OIDC configuration\n        web.web.config = {\n          server = \"https://${cfg.host}\";\n          oidc = {\n            metadata_url = \"${cfg.oidcIssuer}/.well-known/openid-configuration\";\n            authority = cfg.oidcIssuer;\n            client_id = cfg.oidcClientId;\n            response_type = \"code\";\n            scope = \"openid offline_access profile email groups\";\n          };\n        };\n      };\n    };\n\n    # Caddy reverse proxy\n    services.caddy.virtualHosts = {\n      \"${cfg.host}\".extraConfig = ''\n        reverse_proxy 127.0.0.1:${toString cfg.port}\n      '';\n    };\n  };\n}\n"
  },
  {
    "path": "modules/opencrow/default.nix",
    "content": "{\n  opencrow,\n  mics-skills,\n  config,\n  lib,\n  pkgs,\n  pinpox-utils,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.opencrow;\n\n  stateDir = \"/var/lib/opencrow-claude\";\n  localStateDir = \"/var/lib/opencrow-local\";\n\n  # models.json for the local instance to discover ollama on spark1\n  localPiModelsJson = pkgs.writeText \"opencrow-local-models.json\" (\n    builtins.toJSON {\n      providers.ollama = {\n        baseUrl = \"http://100.96.100.103:11434/v1\";\n        api = \"openai-completions\";\n        apiKey = \"dummy\";\n        compat = {\n          supportsDeveloperRole = false;\n          supportsReasoningEffort = false;\n        };\n        models = [\n          {\n            id = \"gemma4:26b\";\n            reasoning = true;\n          }\n        ];\n      };\n    }\n  );\n\n  himalayaVars = config.clan.core.vars.generators.\"opencrow-himalaya\";\n\n  # Himalaya config for the mail fetcher (IMAP only, no SMTP)\n  himalayaFetcherConfig = pkgs.writeText \"himalaya-fetcher-config.toml\" ''\n    [accounts.\"mailbox.org\"]\n    default = true\n    display-name = \"opencrow-fetcher\"\n    downloads-dir = \"/tmp\"\n\n    backend.type = \"imap\"\n    backend.host = \"imap.mailbox.org\"\n    backend.port = 993\n    backend.encryption.type = \"tls\"\n    backend.auth.type = \"password\"\n    backend.auth.command = \"printenv EMAIL_PASSWORD\"\n\n    message.send.backend.type = \"none\"\n  '';\n\n  # Script that fetches starred/flagged emails to a directory\n  # NOTE: goimapnotify does not pass its environment to child scripts,\n  # so we source the envfile explicitly to get EMAIL_PASSWORD/EMAIL_LOGIN.\n  onChangedMailScript = pkgs.writeShellScript \"opencrow-fetch-starred\" ''\n    set -euo pipefail\n\n    set -a\n    source ${himalayaVars.files.\"envfile\".path}\n    set +a\n\n    HIMALAYA=\"${lib.getExe pkgs.himalaya} -c ${himalayaFetcherConfig} -c ${himalayaVars.files.\"config\".path}\"\n\n    MAIL_DIR=\"${stateDir}/mail-inbox\"\n    PIPE=\"${stateDir}/sessions/trigger.pipe\"\n    mkdir -p \"$MAIL_DIR\"\n\n    # List flagged messages, get their IDs\n    ids=$($HIMALAYA envelope list --folder INBOX --output json flag flagged | ${lib.getExe pkgs.jq} -r '.[].id')\n    [ -z \"$ids\" ] && exit 0\n\n    for id in $ids; do\n      $HIMALAYA message read \"$id\" > \"$MAIL_DIR/$(date +%s)-''${id}.txt\"\n      $HIMALAYA flag remove \"$id\" flagged\n      $HIMALAYA flag add \"$id\" crow-processed\n      $HIMALAYA message move Archive \"$id\"\n    done\n\n    # Notify the bot\n    [ -p \"$PIPE\" ] && echo \"New starred emails arrived. Read them from $MAIL_DIR\" > \"$PIPE\"\n  '';\n\n  # goimapnotify configuration\n  goimapnotifyConfig = pkgs.writeText \"goimapnotify-config.yaml\" (builtins.toJSON {\n    configurations = [\n      {\n        host = \"imap.mailbox.org\";\n        port = 993;\n        tls = true;\n        tlsOptions = {\n          rejectUnauthorized = true;\n        };\n        usernameCMD = \"${lib.getExe' pkgs.coreutils \"printenv\"} EMAIL_LOGIN\";\n        passwordCMD = \"${lib.getExe' pkgs.coreutils \"printenv\"} EMAIL_PASSWORD\";\n        boxes = [\n          {\n            mailbox = \"INBOX\";\n            onChangedMail = toString onChangedMailScript;\n          }\n        ];\n      }\n    ];\n  });\nin\n{\n\n  imports = [ opencrow.nixosModules.default ];\n\n  options.pinpox.services.opencrow.enable = mkEnableOption \"opencrow Matrix bot\";\n\n  config = mkIf cfg.enable {\n\n    # OpenCrow Matrix bot\n    clan.core.vars.generators.\"opencrow\" = pinpox-utils.mkEnvGenerator [\n      \"OPENCROW_MATRIX_ACCESS_TOKEN\"\n      \"OPENCROW_MATRIX_USER_ID\"\n    ];\n\n    # Nextcloud (personal)\n    clan.core.vars.generators.\"opencrow-nextcloud\" = pinpox-utils.mkEnvGenerator [\n      \"NEXTCLOUD_PASSWORD\"\n    ];\n\n    # Nextcloud (work)\n    clan.core.vars.generators.\"opencrow-nextcloud-work\" = pinpox-utils.mkEnvGenerator [\n      \"WORK_NEXTCLOUD_PASSWORD\"\n    ];\n\n    # Eversports\n    clan.core.vars.generators.\"opencrow-eversports\" = pinpox-utils.mkEnvGenerator [\n      \"EVERSPORTS_EMAIL\"\n      \"EVERSPORTS_PASSWORD\"\n    ];\n\n    # Himalaya email secrets (TOML config + env file for EMAIL_PASSWORD)\n    # Used by the goimapnotify fetcher service for IMAP credentials\n    clan.core.vars.generators.\"opencrow-himalaya\" = {\n      files.config = { };\n      files.envfile = { };\n      runtimeInputs = [ pkgs.coreutils ];\n      prompts.EMAIL_ADDRESS.persist = false;\n      prompts.EMAIL_LOGIN.persist = false;\n      prompts.EMAIL_PASSWORD.persist = false;\n      script = ''\n        mkdir -p $out\n        cat > $out/config << TOML\n        [accounts.\"mailbox.org\"]\n        email = \"$(cat $prompts/EMAIL_ADDRESS)\"\n        backend.login = \"$(cat $prompts/EMAIL_LOGIN)\"\n        TOML\n        cat > $out/envfile << ENV\n        EMAIL_LOGIN='$(cat $prompts/EMAIL_LOGIN)'\n        EMAIL_PASSWORD='$(cat $prompts/EMAIL_PASSWORD)'\n        ENV\n      '';\n    };\n\n    # Local instance matrix credentials (migrated from traube)\n    clan.core.vars.generators.\"opencrow-local\" = pinpox-utils.mkEnvGenerator [\n      \"OPENCROW_MATRIX_ACCESS_TOKEN\"\n    ];\n\n    # Mail inbox directory for fetched emails\n    # Deutschebahn skill symlinked from mics-skills flake\n    systemd.tmpfiles.rules = [\n      \"d ${stateDir}/mail-inbox 0777 root root -\"\n      \"L+ ${stateDir}/skills/deutschebahn - - - - ${mics-skills}/skills/db-cli\"\n      \"L+ ${localStateDir}/skills/deutschebahn - - - - ${mics-skills}/skills/db-cli\"\n      \"L+ ${localStateDir}/pi-agent/models.json - - - - ${localPiModelsJson}\"\n    ];\n\n    # goimapnotify service: watches for starred emails and fetches them\n    systemd.services.opencrow-goimapnotify = {\n      description = \"Watch IMAP for starred emails and fetch them for opencrow\";\n      after = [ \"network-online.target\" ];\n      wants = [ \"network-online.target\" ];\n      wantedBy = [ \"multi-user.target\" ];\n      path = with pkgs; [ coreutils bash himalaya jq ];\n      serviceConfig = {\n        ExecStart = \"${lib.getExe pkgs.goimapnotify} -conf ${goimapnotifyConfig}\";\n        EnvironmentFile = himalayaVars.files.\"envfile\".path;\n        Restart = \"on-failure\";\n        RestartSec = \"10s\";\n      };\n    };\n\n    services.opencrow.instances.claude = {\n      enable = true;\n      piPackage = pkgs.pi;\n      environmentFiles = [\n        config.clan.core.vars.generators.\"opencrow\".files.\"envfile\".path\n        config.clan.core.vars.generators.\"opencrow-nextcloud\".files.\"envfile\".path\n        config.clan.core.vars.generators.\"opencrow-nextcloud-work\".files.\"envfile\".path\n        config.clan.core.vars.generators.\"opencrow-eversports\".files.\"envfile\".path\n      ];\n      extraPackages = [\n        pkgs.pi\n        pkgs.curl\n        pkgs.jq\n        mics-skills.packages.${pkgs.system}.db-cli\n      ];\n      environment = {\n        NEXTCLOUD_URL = \"https://files.pablo.tools\";\n        NEXTCLOUD_USER = \"pinpox\";\n        NEXTCLOUD_CALENDAR = \"personal\";\n\n        WORK_NEXTCLOUD_URL = \"https://nextcloud.clan.lol\";\n        WORK_NEXTCLOUD_USER = \"pinpox\";\n        WORK_NEXTCLOUD_CALENDAR = \"personal\";\n\n        OPENCROW_MATRIX_HOMESERVER = \"https://matrix.org\";\n        OPENCROW_ALLOWED_USERS = \"@pinpox:matrix.org\";\n        OPENCROW_HEARTBEAT_INTERVAL = \"30m\";\n        OPENCROW_PI_SKILLS_DIR = \"${stateDir}/skills\";\n      };\n    };\n\n    services.opencrow.instances.local = {\n      enable = true;\n      piPackage = pkgs.pi;\n      environmentFiles = [\n        config.clan.core.vars.generators.\"opencrow-local\".files.\"envfile\".path\n        config.clan.core.vars.generators.\"opencrow-nextcloud\".files.\"envfile\".path\n        config.clan.core.vars.generators.\"opencrow-nextcloud-work\".files.\"envfile\".path\n        config.clan.core.vars.generators.\"opencrow-eversports\".files.\"envfile\".path\n      ];\n      extraPackages = [\n        pkgs.pi\n        pkgs.curl\n        pkgs.jq\n        mics-skills.packages.${pkgs.system}.db-cli\n      ];\n\n      environment = {\n        NEXTCLOUD_URL = \"https://files.pablo.tools\";\n        NEXTCLOUD_USER = \"pinpox\";\n        NEXTCLOUD_CALENDAR = \"personal\";\n\n        WORK_NEXTCLOUD_URL = \"https://nextcloud.clan.lol\";\n        WORK_NEXTCLOUD_USER = \"pinpox\";\n        WORK_NEXTCLOUD_CALENDAR = \"personal\";\n\n        OPENCROW_MATRIX_HOMESERVER = \"https://matrix.org\";\n        OPENCROW_MATRIX_USER_ID = \"@c.h.i.m.p.:matrix.org\";\n        OPENCROW_ALLOWED_USERS = \"@pinpox:matrix.org\";\n        OPENCROW_PI_PROVIDER = \"ollama\";\n        OPENCROW_PI_MODEL = \"gemma4:26b\";\n        OPENCROW_PI_SKILLS_DIR = \"${localStateDir}/skills\";\n        OPENCROW_HEARTBEAT_INTERVAL = \"30m\";\n        OPENCROW_PI_IDLE_TIMEOUT = \"12h\";\n        OPENCROW_LOG_LEVEL = \"debug\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/openssh/ca.pub",
    "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAPcS8NMzwYLvKFOXeTZwX/W6ua0zIzs4zA0PW0xz62i user-ca\n"
  },
  {
    "path": "modules/openssh/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  pinpox-keys,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.openssh;\nin\n{\n\n  options.pinpox.services.openssh = {\n    enable = mkEnableOption \"OpenSSH server\";\n  };\n\n  config = mkIf cfg.enable {\n\n    # Enable the OpenSSH daemon.\n    services.openssh = {\n      enable = true;\n      startWhenNeeded = true;\n      settings = {\n        PasswordAuthentication = false;\n        KbdInteractiveAuthentication = false;\n      };\n    };\n\n    # Block anything that is not HTTP(s) or SSH.\n    networking.firewall = {\n      enable = true;\n      allowPing = true;\n      allowedTCPPorts = [ 22 ];\n    };\n\n    users.users.root.openssh.authorizedKeys.keyFiles = [ pinpox-keys ];\n\n    services.openssh.extraConfig = \"TrustedUserCAKeys ${./ca.pub}\";\n  };\n}\n"
  },
  {
    "path": "modules/owncast/default.nix",
    "content": "{ config, lib, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.owncast;\nin\n{\n  options.pinpox.services.owncast = {\n    enable = mkEnableOption \"owncast server\";\n    host = mkOption {\n      type = types.str;\n      default = \"stream.0cx.de\";\n      description = \"Host serving owncast\";\n      example = \"stream.0cx.de\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    services.owncast = {\n      enable = true;\n      port = 9768;\n      rtmp-port = 1935;\n    };\n\n    networking.firewall.allowedTCPPorts = [ config.services.owncast.rtmp-port ];\n\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"${cfg.host}\".extraConfig =\n        \"reverse_proxy 127.0.0.1:${builtins.toString config.services.owncast.port}\";\n    };\n  };\n}\n"
  },
  {
    "path": "modules/paperless/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.paperless;\n  paperlessPermsApp = ./django-apps;\n  paperlessCfg = config.services.paperless;\n  paperlessPythonPath = \"${paperlessCfg.package.python.pkgs.makePythonPath paperlessCfg.package.propagatedBuildInputs}:${paperlessCfg.package}/lib/paperless-ngx/src:${paperlessPermsApp}\";\nin\n{\n\n  options.pinpox.services.paperless = {\n    enable = mkEnableOption \"paperless-ngx document management\";\n\n    host = mkOption {\n      type = types.str;\n      default = \"paper.pablo.tools\";\n      description = \"Host serving paperless-ngx\";\n      example = \"paper.pablo.tools\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"paperless\" = {\n      files.password = { };\n      runtimeInputs = with pkgs; [\n        coreutils\n        xkcdpass\n      ];\n      script = ''\n        mkdir -p $out\n        xkcdpass -d- > $out/password\n      '';\n    };\n\n    services.paperless = {\n      enable = true;\n      address = \"127.0.0.1\";\n      port = 28981;\n      passwordFile = config.clan.core.vars.generators.\"paperless\".files.\"password\".path;\n      settings = {\n        PAPERLESS_URL = \"https://${cfg.host}\";\n        PAPERLESS_OCR_LANGUAGE = \"deu+eng\";\n        PAPERLESS_CONSUMER_RECURSIVE = true;\n        PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = true;\n        PAPERLESS_ADMIN_USER = \"pinpox\";\n        PAPERLESS_ENABLE_HTTP_REMOTE_USER = true;\n        PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME = \"HTTP_REMOTE_USER\";\n        PAPERLESS_LOGOUT_REDIRECT_URL = \"/\";\n        PAPERLESS_APPS = \"paperless_perms.apps.PaperlessPermsConfig\";\n      };\n    };\n\n    # Add the perms Django app to PYTHONPATH for all paperless services\n    systemd.services.paperless-web.environment.PYTHONPATH = lib.mkForce paperlessPythonPath;\n    systemd.services.paperless-task-queue.environment.PYTHONPATH = paperlessPythonPath;\n    systemd.services.paperless-consumer.environment.PYTHONPATH = paperlessPythonPath;\n    systemd.services.paperless-scheduler.environment.PYTHONPATH = paperlessPythonPath;\n\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"${cfg.host}\".extraConfig = ''\n        forward_auth http://127.0.0.1:9091 {\n          uri /api/authz/forward-auth\n          copy_headers Remote-User Remote-Groups Remote-Name Remote-Email\n        }\n        reverse_proxy ${config.services.paperless.address}:${toString config.services.paperless.port}\n      '';\n    };\n\n    pinpox.services.restic-client.backup-paths-offsite = [\n      \"${config.services.paperless.dataDir}\"\n    ];\n  };\n}\n"
  },
  {
    "path": "modules/paperless/django-apps/paperless_perms/__init__.py",
    "content": ""
  },
  {
    "path": "modules/paperless/django-apps/paperless_perms/apps.py",
    "content": "\"\"\"Auto-manage permissions for Remote-User accounts in Paperless-ngx.\n\nLoaded as a Django app via PAPERLESS_APPS. On startup it:\n\n1. Creates an \"editors\" group with full CRUD permissions on all models.\n2. Adds all non-service Remote-User accounts to the editors group.\n3. Installs a post_save signal so new accounts are handled automatically.\n\"\"\"\n\nimport logging\n\nfrom django.apps import AppConfig\nfrom django.db.models.signals import post_save\n\nlogger = logging.getLogger(__name__)\n\nSERVICE_ACCOUNTS = {\"consumer\", \"AnonymousUser\"}\n\n\ndef _setup_editors_group():\n    from django.contrib.auth.models import Group, Permission\n    from django.contrib.contenttypes.models import ContentType\n\n    editors_group, _ = Group.objects.get_or_create(name=\"editors\")\n    permissions = []\n    for ct in ContentType.objects.all():\n        permissions.extend(\n            Permission.objects.filter(\n                content_type=ct,\n                codename__regex=r\"^(view|change|add|delete)_\",\n            )\n        )\n    editors_group.permissions.set(permissions)\n    return editors_group\n\n\ndef _promote_user(user, editors_group):\n    if not user.groups.filter(name=\"editors\").exists():\n        user.groups.add(editors_group)\n        logger.info(\"Added user %s to editors group\", user.username)\n\n\ndef _on_user_created(sender, instance, created, **kwargs):\n    if not created or instance.username in SERVICE_ACCOUNTS:\n        return\n    from django.contrib.auth.models import Group\n\n    try:\n        editors = Group.objects.get(name=\"editors\")\n    except Group.DoesNotExist:\n        return\n    _promote_user(instance, editors)\n\n\nclass PaperlessPermsConfig(AppConfig):\n    name = \"paperless_perms\"\n    verbose_name = \"Paperless permission management\"\n\n    def ready(self):\n        from django.contrib.auth.models import User\n\n        post_save.connect(_on_user_created, sender=User)\n        try:\n            editors_group = _setup_editors_group()\n            for user in User.objects.exclude(username__in=SERVICE_ACCOUNTS):\n                _promote_user(user, editors_group)\n        except Exception:\n            logger.exception(\"Failed initial permission setup\")\n"
  },
  {
    "path": "modules/radio/default.nix",
    "content": "{\n  config,\n  lib,\n  radio,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.radio;\nin\n{\n\n  options.pinpox.services.radio = {\n    enable = mkEnableOption \"web radio streamer\";\n\n    host = mkOption {\n      type = types.str;\n      default = \"radio.0cx.de\";\n      description = \"Host serving the radio\";\n      example = \"radio.0cx.de\";\n    };\n\n  };\n\n  config = mkIf cfg.enable {\n\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"${cfg.host}\".extraConfig = \"reverse_proxy 127.0.0.1:7000\";\n    };\n\n    systemd.services.radio =\n      let\n        stationsfile = pkgs.writeTextFile {\n          name = \"stations.ini\";\n          text = ''\n            [Hirschmilch Psytrance]\n            url = \"https://hirschmilch.de:7000/psytrance.mp3\"\n\n            [Hirschmilch Progressive]\n            url = \"https://hirschmilch.de:7000/progressive.mp3\"\n\n            [Lassulus Radio]\n            url = \"https://radio.lassul.us/radio.mp3\"\n\n            [Hirschmilch chillout]\n            url = \"https://hirschmilch.de:7000/chillout.mp3\"\n\n            [Nightride FM]\n            url = https://stream.rekt.network/nightride.ogg\n\n            [Rekt Network]\n            url = https://stream.rekt.network/rekt.ogg\n          '';\n        };\n\n      in\n      {\n        wantedBy = [ \"multi-user.target\" ];\n        environment = {\n          RADIO_ADDRESS = \"127.0.0.1:7000\";\n          RADIO_STATIONFILE = stationsfile;\n          GIN_MODE = \"release\";\n        };\n        serviceConfig = {\n          DynamicUser = true;\n          ExecStart = \"${radio.packages.x86_64-linux.default}/bin/radio\";\n          Restart = \"on-failure\";\n          RestartSec = \"5s\";\n        };\n      };\n  };\n}\n"
  },
  {
    "path": "modules/restic/default.nix",
    "content": "{\n  lib,\n  pkgs,\n  config,\n  pinpox-utils,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.restic-client;\nin\n{\n\n  options.pinpox.services.restic-client = {\n    enable = mkEnableOption \"restic backups\";\n\n    backup-paths-onsite = mkOption {\n      type = types.listOf types.str;\n      default = [ ];\n      example = [ \"/home/pinpox/Notes\" ];\n      description = \"Paths to backup to onsite storage\";\n    };\n\n    backup-paths-offsite = mkOption {\n      type = types.listOf types.str;\n      default = [ ];\n      example = [ \"/home/pinpox/Notes\" ];\n      description = \"Paths to backup to offsite storage\";\n    };\n\n    backup-paths-exclude = mkOption {\n      type = types.listOf types.str;\n      default = [\n        \"*.pyc\"\n        \"*/.BurpSuite\"\n        \"*/.arduino15/packages\"\n        \"*/.cache\"\n        \"*/.cargo\"\n        \"*/.coc\"\n        \"*/.config/Nextcloud/logs\"\n        \"*/.config/Signal\"\n        \"*/.config/chromium\"\n        \"*/.config/discord\"\n        \"*/.config/retroarch\"\n        \"*/.container-diff\"\n        \"*/.go/pkg\"\n        \"*/.gvfs/\"\n        \"*/.local/share/Steam\"\n        \"*/.local/share/Trash\"\n        \"*/.local/share/tor-browser\"\n        \"*/.local/share/typeracer\"\n        \"*/.local/share/virtualenv\"\n        \"*/.local/state/NvChad/\"\n        \"*/.mozilla/firefox\"\n        \"*/.nextcloud\"\n        \"*/.npm\"\n        \"*/.npm/_cacache\"\n        \"*/.platformio\"\n        \"*/.rustup\"\n        \"*/.thumbnails\"\n        \"*/.ts3client\"\n        \"*/.vagrant.d\"\n        \"*/.vim\"\n        \"*/.vimtemp\"\n        \"*/Cache\"\n        \"*/Downloads\"\n        \"*/Seafile\"\n        \"*/VirtualBox VMs\"\n        \"*/cache2\"\n        \"*/code\"\n        \"/var/lib/docker\"\n        \"discord/Cache\"\n        \"tags\"\n      ];\n      example = [ \"/home/pinpox/cache\" ];\n      description = \"Paths to exclude from backup\";\n    };\n  };\n\n  config =\n    let\n      s3-generator =\n        (pinpox-utils.mkEnvGenerator [\n          \"AWS_ACCESS_KEY_ID\"\n          \"AWS_SECRET_ACCESS_KEY\"\n          \"NTFY_USER\"\n          \"NTFY_PASS\"\n        ])\n        // {\n          share = true;\n        };\n    in\n\n    mkIf cfg.enable {\n\n      clan.core.vars.generators.\"restic-credentials\" = s3-generator;\n      clan.core.vars.generators.\"restic-credentials-backblaze\" = s3-generator;\n\n      clan.core.vars.generators.\"restic\" = {\n        files.repo-pw = { };\n        runtimeInputs = with pkgs; [\n          coreutils\n          xkcdpass\n        ];\n        script = ''\n          mkdir -p $out\n          xkcdpass -d- > $out/repo-pw\n        '';\n      };\n\n      clan.core.vars.generators.\"restic-offsite\" = {\n        prompts.repo-pw.persist = true;\n        share = true;\n      };\n\n      services.restic.backups =\n        let\n          script-post = host: site: ''\n            if [ $EXIT_STATUS -ne 0 ]; then\n              ${pkgs.curl}/bin/curl -u $NTFY_USER:$NTFY_PASS \\\n              -H 'Title: Backup (${site}) on ${host} failed!' \\\n              -H 'Tags: backup,restic,${host},${site}' \\\n              -d \"Restic (${site}) backup error on ${host}!\" 'https://push.pablo.tools/pinpox_backups'\n            else\n              ${pkgs.curl}/bin/curl -u $NTFY_USER:$NTFY_PASS \\\n              -H 'Title: Backup (${site}) on ${host} successful!' \\\n              -H 'Tags: backup,restic,${host},${site}' \\\n              -d \"Restic (${site}) backup success on ${host}!\" 'https://push.pablo.tools/pinpox_backups'\n            fi\n          '';\n\n          restic-ignore-file = pkgs.writeTextFile {\n            name = \"restic-ignore-file\";\n            text = builtins.concatStringsSep \"\\n\" cfg.backup-paths-exclude;\n          };\n\n          pruneOpts = [\n            \"--keep-daily 7\"\n            \"--keep-weekly 5\"\n            \"--keep-monthly 12\"\n            \"--keep-yearly 75\"\n          ];\n\n          extraBackupArgs = [\n            \"--exclude-file=${restic-ignore-file}\"\n            \"--one-file-system\"\n            \"-vv\"\n          ];\n\n        in\n        {\n          s3-offsite = {\n            paths = cfg.backup-paths-offsite;\n            repository = \"s3:https://s3.us-east-005.backblazeb2.com/pinpox-restic\";\n            environmentFile = \"${config.clan.core.vars.generators.\"restic-credentials-backblaze\".files.\"envfile\".path\n            }\";\n            passwordFile = \"${config.clan.core.vars.generators.\"restic-offsite\".files.\"repo-pw\".path}\";\n            backupCleanupCommand = script-post config.networking.hostName \"backblaze\";\n            inherit pruneOpts extraBackupArgs;\n          };\n\n          s3-onsite = {\n            paths = cfg.backup-paths-onsite;\n            repository = \"s3:https://vpn.s3.pablo.tools/restic\";\n            environmentFile = \"${config.clan.core.vars.generators.\"restic-credentials\".files.\"envfile\".path}\";\n            passwordFile = \"${config.clan.core.vars.generators.\"restic\".files.\"repo-pw\".path}\";\n            backupCleanupCommand = script-post config.networking.hostName \"NAS\";\n            inherit pruneOpts extraBackupArgs;\n          };\n        };\n    };\n}\n"
  },
  {
    "path": "modules/screego/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nlet\n  cfg = config.pinpox.services.screego;\nin\n{\n  options.pinpox.services.screego = {\n    enable = lib.mkEnableOption \"screego server\";\n\n    domain = lib.mkOption {\n      type = lib.types.str;\n      default = \"0cx.de\";\n      description = \"Domain to create the sudomains unders\";\n    };\n\n  };\n\n  config = lib.mkIf cfg.enable {\n\n    services.caddy = {\n      enable = true;\n      virtualHosts = {\n        \"screen.${cfg.domain}\".extraConfig = \"reverse_proxy 127.0.0.1:5050\";\n        \"turn.${cfg.domain}\".extraConfig = \"reverse_proxy 127.0.0.1:5050\";\n      };\n    };\n\n    clan.core.vars.generators.\"screego\" = {\n\n      files.envfile = { };\n      files.users = { };\n      files.prometheus-pass = { };\n\n      runtimeInputs = with pkgs; [\n        coreutils\n        screego\n        xkcdpass\n      ];\n\n      script = ''\n        echo \"SCREEGO_SECRET=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 40)\" > $out/envfile\n        xkcdpass -n 4 -d - > $out/prometheus-pass\n        cat $out/prometheus-pass | screego hash --name \"prometheus\" --pass - > $out/users\n      '';\n    };\n\n    systemd.services.screego.serviceConfig.LoadCredential = [\n      \"users:${config.clan.core.vars.generators.screego.files.\"users\".path}\"\n    ];\n\n    services.screego = {\n      enable = true;\n      openFirewall = true;\n      environmentFile = \"${config.clan.core.vars.generators.screego.files.\"envfile\".path}\";\n      settings = {\n        # SCREEGO_EXTERNAL_IP = \"46.38.242.17\";\n        SCREEGO_EXTERNAL_IP = \"dns:screen.${cfg.domain}\";\n        SCREEGO_SERVER_TLS = \"false\";\n        SCREEGO_CORS_ALLOWED_ORIGINS = \"https://screen.${cfg.domain}\";\n        SCREEGO_USERS_FILE = \"%d/users\";\n        SCREEGO_PROMETHEUS = \"true\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/sound/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.sound;\nin\n{\n\n  options.pinpox.defaults.sound = {\n    enable = mkEnableOption \"sound defaults\";\n  };\n  config = mkIf cfg.enable {\n\n    environment.systemPackages = [ pkgs.qjackctl ];\n\n    services.pipewire = {\n      enable = true;\n\n      # Use pipeware to emulate jack and pulseaudio\n      jack.enable = true;\n      pulse.enable = true;\n      alsa.enable = true;\n\n      configPackages =\n\n        let\n          rnnoiseFilter = {\n            nodes = [\n              {\n                type = \"ladspa\";\n                name = \"rnnoise\";\n                plugin = \"${pkgs.rnnoise-plugin}/lib/ladspa/librnnoise_ladspa.so\";\n                label = \"noise_suppressor_mono\";\n                control = {\n                  \"VAD Threshold (%)\" = 75.0;\n                  \"VAD Grace Period (ms)\" = 200;\n                  \"Retroactive VAD Grace (ms)\" = 100;\n                };\n              }\n            ];\n          };\n\n          mkFilterChain =\n            {\n              name,\n              capture,\n              playback,\n            }:\n            {\n              name = \"libpipewire-module-filter-chain\";\n\n              args = {\n                \"node.description\" = name;\n                \"media.name\" = name;\n\n                \"filter.graph\" = rnnoiseFilter;\n\n                \"capture.props\" = {\n                  \"audio.rate\" = 48000;\n                }\n                // capture;\n\n                \"playback.props\" = {\n                  \"audio.rate\" = 48000;\n                }\n                // playback;\n              };\n            };\n\n          inputFilter = mkFilterChain {\n            name = \"Noise Cancelling Source\";\n\n            capture = {\n              # > indicate that a link is passive and does not cause the graph to be runnable.\n              # https://docs.pipewire.org/group__pw__keys.html#gafcd3d133168b9353c89c1c5f2de6954e\n              \"node.passive\" = true;\n              \"node.name\" = \"capture.rnnoise_source\";\n              # Explicitly capture from Scarlett Input 1 (left XLR)\n              \"node.target\" = \"alsa_input.usb-Focusrite_Scarlett_2i2_USB_Y87MV6G157830B-00.HiFi__Mic1__source\";\n            };\n            playback = {\n              \"node.name\" = \"rnnoise_source\";\n              \"media.class\" = \"Audio/Source\";\n            };\n          };\n\n          outputFilter = mkFilterChain {\n            name = \"Noise Cancelling Sink\";\n\n            capture = {\n              \"node.name\" = \"capture.rnnoise_sink\";\n              \"media.class\" = \"Audio/Sink\";\n            };\n            playback = {\n              \"node.passive\" = true;\n              \"node.name\" = \"rnnoise_sink\";\n              \"media.class\" = \"Stream/Output/Audio\";\n            };\n          };\n\n          config = {\n            \"context.modules\" = [\n              inputFilter\n              outputFilter\n            ];\n          };\n        in\n        [\n          (pkgs.writeTextDir \"share/pipewire/pipewire.conf.d/99-input-denoising.conf\" (\n            builtins.toJSON config\n          ))\n        ];\n\n    };\n\n    # Set Scarlett 2i2 Input 1 to Inst mode when the device appears\n    services.udev.extraRules = ''\n      ACTION==\"add\", SUBSYSTEM==\"sound\", ATTR{id}==\"USB\", RUN+=\"${pkgs.alsa-utils}/bin/amixer -c USB cset name='Line In 1 Level Capture Enum' 'Inst'\"\n    '';\n\n    # Hide \"Monitor of ...\" sources from applications like pavucontrol\n    services.pipewire.wireplumber.extraConfig.\"50-hide-monitors\" = {\n      \"wireplumber.settings\" = {\n        \"node.features.audio.monitor-ports\" = false;\n      };\n    };\n\n    # Force Scarlett 2i2 to use the \"Direct\" profile\n    services.pipewire.wireplumber.extraConfig.\"50-scarlett-profile\" = {\n      \"monitor.alsa.rules\" = [\n        {\n          matches = [\n            { \"device.name\" = \"alsa_card.usb-Focusrite_Scarlett_2i2_USB_Y87MV6G157830B-00\"; }\n          ];\n          actions = {\n            update-props = {\n              \"device.profile\" = \"Direct\";\n            };\n          };\n        }\n      ];\n    };\n\n    # Use noisetorch (RNnoise) to create a virtual source with noise removal\n    programs.noisetorch.enable = true;\n\n    # services.pipewire.wireplumber.enable = true;\n\n    # environment.etc.\"wireplumber/main.lua.d/90-suspend-timeout.lua\" = {\n    #   text = ''\n    #     session.suspend-timeout-seconds = 0\n    #   '';\n    # };\n  };\n}\n"
  },
  {
    "path": "modules/storagebox/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.storagebox;\nin\n{\n\n  options.pinpox.defaults.storagebox = {\n\n    enable = mkEnableOption \"storagebox access\";\n\n    mountOnAccess = mkOption {\n      type = types.bool;\n      default = false;\n      description = \"Whether to mount on access, instead of permanently\";\n      example = true;\n    };\n    mountPoint = mkOption {\n      type = types.str;\n      default = \"/mnt/storagebox\";\n      description = \"Where to mount the storage\";\n      example = \"/mnt/music\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    # Hard-code an unsused gid for the group\n    users.groups.storage-users.gid = 982;\n\n    # SSH keypair generator for Hetzner Storage Box\n    clan.core.vars.generators.\"storagebox-ssh\" = {\n      share = true;\n      files.ssh-private-key = { };\n      files.ssh-public-key.secret = false;\n      runtimeInputs = with pkgs; [ openssh ];\n      script = ''\n        mkdir -p $out\n        ssh-keygen -t ed25519 -f $out/ssh-private-key -N \"\" -C \"kiwi-storagebox\"\n        mv $out/ssh-private-key.pub $out/ssh-public-key\n      '';\n    };\n\n    # Add rclone to system packages for mount helper support\n    environment.systemPackages = [ pkgs.rclone ];\n\n    # Hetzner Storage Box mount with rclone - using proper mount helper\n    # Create cache directory for rclone\n    systemd.tmpfiles.rules = [\n      \"d /var/cache/rclone-storagebox 0750 root storage-users -\"\n    ];\n\n    fileSystems.\"${cfg.mountPoint}\" = {\n      device = \":sftp:\";\n      fsType = \"rclone\";\n      options = [\n        \"rw\"\n        \"nofail\"\n        \"_netdev\"\n        \"x-systemd.mount-timeout=120s\"\n        \"args2env\"\n        \"config=/dev/null\"\n        \"vfs_cache_mode=full\"\n        \"cache_dir=/var/cache/rclone-storagebox\"\n        \"checkers=8\"\n        \"gid=${toString config.users.groups.storage-users.gid}\"\n        \"umask=007\"\n        \"allow_other\"\n        \"allow_non_empty\"\n        \"links\"\n        \"sftp_host=u515095.your-storagebox.de\"\n        \"sftp_user=u515095\"\n        \"sftp_port=23\"\n        \"sftp_key_file=${config.clan.core.vars.generators.\"storagebox-ssh\".files.\"ssh-private-key\".path}\"\n        \"vfs_cache_max_size=2G\"\n        \"vfs_cache_max_age=5m\"\n        \"vfs_read_ahead=128M\"\n        \"buffer_size=64M\"\n        \"dir_cache_time=30s\"\n        \"log_level=INFO\"\n        \"log_systemd=true\"\n      ]\n      ++ optionals cfg.mountOnAccess [\n        \"noauto\"\n        \"x-systemd.automount\"\n        \"x-systemd.idle-timeout=600\"\n      ];\n    };\n  };\n}\n"
  },
  {
    "path": "modules/twitch-first/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  pinpox-utils,\n  twitch-first,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.twitch-first;\nin\n{\n\n  options.pinpox.services.twitch-first = {\n    enable = mkEnableOption \"Twitch 'first' channel point redemption tracker\";\n  };\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"twitch-first\" = pinpox-utils.mkEnvGenerator [\n      \"TWITCH_CLIENT_ID\"\n      \"TWITCH_CLIENT_SECRET\"\n      \"TWITCH_CHANNEL\"\n      \"TWITCH_REWARD_ID\"\n    ];\n\n    systemd.services.twitch-first = {\n      description = \"Twitch first channel point tracker\";\n      wantedBy = [ \"multi-user.target\" ];\n      after = [ \"network-online.target\" ];\n      wants = [ \"network-online.target\" ];\n\n      serviceConfig = {\n        ExecStart = \"${twitch-first.packages.${pkgs.system}.twitch-first}/bin/twitch-first\";\n        EnvironmentFile = config.clan.core.vars.generators.\"twitch-first\".files.\"envfile\".path;\n        DynamicUser = true;\n        StateDirectory = \"twitch-first\";\n        WorkingDirectory = \"/var/lib/twitch-first\";\n        Restart = \"on-failure\";\n        RestartSec = 10;\n      };\n\n      environment = {\n        TWITCH_FIRST_DB = \"/var/lib/twitch-first/firsts.db\";\n        TWITCH_TOKEN_FILE = \"/var/lib/twitch-first/token.json\";\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/unbound-desktop/default.nix",
    "content": "{ lib, config, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.unbound-desktop;\nin\n{\n\n  options.pinpox.services.unbound-desktop = {\n    enable = mkEnableOption \"local unbound for desktops\";\n  };\n\n  config = mkIf cfg.enable {\n\n    services.avahi = {\n      enable = true;\n      nssmdns4 = true;\n      nssmdns6 = true;\n      openFirewall = true;\n      publish = {\n        enable = true;\n        addresses = true;\n        workstation = true;\n        userServices = true;\n        domain = true;\n      };\n    };\n\n    networking.networkmanager.insertNameservers = config.services.unbound.settings.server.interface;\n    # networking.networkmanager.dns = \"unbound\";\n    # services.resolved.enable = false;\n    networking.search = [ \"fritz.box\" ];\n\n    services.unbound = {\n      enable = true;\n      settings = {\n\n        server = {\n          interface = [ \"127.0.0.1\" ];\n\n          # include = [\n          #   \"\\\"${dns-overwrites-config}\\\"\"\n          #   \"\\\"${flake-self.inputs.adblock-unbound.packages.${pkgs.system}.unbound-adblockStevenBlack}\\\"\"\n          # ];\n\n          access-control = [ \"127.0.0.0/8 allow\" ];\n        };\n\n        domain-insecure = [ \"fritz.box\" ];\n        stub-zone = [\n          {\n            name = \"fritz.box\";\n            stub-addr = \"192.168.101.1\";\n          }\n        ];\n\n        forward-zone = [\n          {\n            name = \"google.*.\";\n            forward-addr = [\n              \"8.8.8.8@853#dns.google\"\n              \"8.8.8.4@853#dns.google\"\n            ];\n            forward-tls-upstream = \"yes\";\n          }\n          {\n            name = \".\";\n            forward-addr = [\n              \"1.1.1.1@853#cloudflare-dns.com\"\n              \"1.0.0.1@853#cloudflare-dns.com\"\n              \"192.168.101.1\"\n            ];\n            forward-tls-upstream = \"yes\";\n          }\n        ];\n        # remote-control.control-enable = true;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/unifi-controller/default.nix",
    "content": "{\n  lib,\n  config,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.unifi-controller;\nin\n{\n\n  options.pinpox.services.unifi-controller.enable = mkEnableOption \"unifi controller (docker)\";\n\n  config = mkIf cfg.enable {\n\n    users.users.unifi = {\n      isSystemUser = true;\n      description = \"unifi user\";\n      extraGroups = [ \"unifi\" ];\n      group = \"unifi\";\n      createHome = true;\n      home = \"/var/lib/unifi\";\n    };\n\n    users.groups.unifi.name = \"unifi\";\n\n    # Access locally via:\n    # https://birne:8443/manage/\n    # Set inform via ssh:\n    # set-inform http://birne:8080/inform\n    virtualisation.oci-containers.containers = {\n      unifi-contoller = {\n        autoStart = true;\n        image = \"linuxserver/unifi-controller:version-5.6.42\";\n\n        environment = {\n          \"PUID\" = toString config.users.users.unifi.uid;\n          \"PGID\" = toString config.users.groups.unifi.gid;\n          # \"TZ\" = \"Etc/UTC\";\n          # \"MEM_LIMIT\" = \"4096\";\n          # \"MEM_STARTUP\" = \"4096\";\n        };\n\n        ports = [\n          \"3478:3478/udp\"\n          \"8080:8080\"\n          \"8081:8081\"\n          \"8443:8443\"\n          \"8843:8843\"\n          \"8880:8880\"\n        ];\n\n        volumes = [ \"${config.users.users.unifi.home}/config:/config\" ];\n      };\n    };\n\n    networking.firewall = {\n\n      allowedUDPPorts = [\n        3478 # Unifi UDP port used for STUN.\n        10001 # Unifi UDP port used for device discovery.\n      ];\n\n      allowedTCPPorts = [\n        8080 # Unifi port for UAP to inform controller.\n        8880 # Unifi port for HTTP portal redirect, if guest portal is enabled.\n        8843 # Unifi port for HTTPS portal redirect, ditto.\n        6789 # Unifi port for UniFi mobile speed test.\n      ];\n    };\n\n  };\n}\n"
  },
  {
    "path": "modules/vaultwarden/default.nix",
    "content": "{\n  config,\n  lib,\n  pinpox-utils,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.vaultwarden;\nin\n{\n\n  options.pinpox.services.vaultwarden = {\n    enable = mkEnableOption \"vaultwarden password manager\";\n\n    host = mkOption {\n      type = types.str;\n      default = \"pass.pablo.tools\";\n      description = \"Host serving vaultwarden\";\n      example = \"pass.pablo.tools\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"${cfg.host}\".extraConfig = ''\n        reverse_proxy 127.0.0.1:${builtins.toString config.services.vaultwarden.config.ROCKET_PORT}\n      '';\n    };\n\n    systemd.services.backup-vaultwarden.serviceConfig.StateDirectory = \"vaultwarden-backups\";\n\n    services.vaultwarden = {\n      enable = true;\n      dbBackend = \"sqlite\"; # Still in /var/lib/bitwarde_rs\n      backupDir = \"/var/lib/vaultwarden-backups\"; # backup its persistent data\n      config = {\n        DOMAIN = \"https://${cfg.host}\";\n        SIGNUPS_ALLOWED = false;\n        INVITATIONS_ALLOWED = \"true\";\n        ROCKET_PORT = 8222;\n        EXPERIMENTAL_CLIENT_FEATURE_FLAGS = \"ssh-key-vault-item,ssh-agent\";\n      };\n\n      environmentFile = \"${config.clan.core.vars.generators.\"vaultwarden\".files.\"envfile\".path}\";\n    };\n\n    clan.core.vars.generators.\"vaultwarden\" = pinpox-utils.mkEnvGenerator [\n      \"YUBICO_CLIENT_ID\"\n      \"YUBICO_SECRET_KEY\"\n      \"ADMIN_TOKEN\"\n    ];\n\n    # Backup DB and persistent data (e.g. attachments)\n    pinpox.services.restic-client.backup-paths-offsite = [\n      \"${config.services.vaultwarden.backupDir}\"\n      \"/var/lib/bitwarden_rs\"\n    ];\n  };\n}\n"
  },
  {
    "path": "modules/vikunja/default.nix",
    "content": "{\n  config,\n  lib,\n  pinpox-utils,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.vikunja;\nin\n{\n\n  options.pinpox.services.vikunja = {\n    enable = mkEnableOption \"vikunja config\";\n    host = mkOption {\n      type = types.str;\n      default = \"todo.0cx.de\";\n      description = \"Host serving vikunja\";\n      example = \"tasks.0cx.de\";\n    };\n  };\n\n  config = mkIf cfg.enable {\n\n    services.caddy.virtualHosts.\"${cfg.host}\".extraConfig =\n      \"reverse_proxy localhost:${toString config.services.vikunja.port}\";\n\n    clan.core.vars.generators.\"vikunja\" = pinpox-utils.mkEnvGenerator [\n      \"VIKUNJA_AUTH_OPENID_PROVIDERS_DEX_CLIENTID\"\n      \"VIKUNJA_AUTH_OPENID_PROVIDERS_DEX_CLIENTSECRET\"\n      \"VIKUNJA_METRIC_PASSWORD\"\n      \"VIKUNJA_MAILER_PASSWORD\"\n    ];\n\n    services.vikunja = {\n      enable = true;\n      port = 3456;\n      environmentFiles = [ config.clan.core.vars.generators.\"vikunja\".files.\"envfile\".path ];\n\n      frontendScheme = \"https\";\n      frontendHostname = cfg.host;\n\n      settings = {\n\n        service.timezone = \"Europe/Berlin\";\n        files.basepath = \"/var/lib/vikunja/files\";\n\n        defaultsettings = {\n          discoverable_by_name = true;\n          discoverable_by_email = true;\n          email_reminders_enabled = true;\n          overdue_tasks_reminders_enabled = true;\n          overdue_tasks_reminders_time = \"10:00\";\n          week_start = \"1\";\n        };\n\n        mailer = {\n          enabled = true;\n          host = \"smtp.sendgrid.net\";\n          username = \"apikey\";\n          frommail = \"todo@0cx.de\";\n          port = \"587\";\n          authtype = \"plain\";\n          skiptlsverify = \"false\";\n          forcessl = true;\n        };\n\n        metrics = {\n          enabled = true;\n          username = \"prometheus\";\n        };\n\n        auth = {\n          local.enabled = false;\n          openid = {\n            enabled = true;\n            redirect_url = \"https://todo.0cx.de/auth/openid/\";\n            providers.dex = {\n              authurl = \"https://login.0cx.de\";\n              name = \"dex\";\n            };\n          };\n        };\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/virtualisation/default.nix",
    "content": "{\n  config,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.virtualisation;\nin\n{\n\n  options.pinpox.virtualisation = {\n    docker.enable = mkEnableOption \"Docker virtualisation\";\n    virtualbox.enable = mkEnableOption \"VirtualBox virtualisation\";\n    virt-manager.enable = mkEnableOption \"Virt-Manager virtualisation\";\n  };\n\n  config = mkMerge [\n    (mkIf cfg.docker.enable {\n      users.users.pinpox.extraGroups = [ \"docker\" ];\n      virtualisation.docker.enable = true;\n    })\n\n    (mkIf cfg.virt-manager.enable {\n      boot.kernelModules = [ \"kvm-amd\" ];\n      virtualisation.libvirtd.enable = true;\n      programs.virt-manager.enable = true;\n    })\n\n    (mkIf cfg.virtualbox.enable {\n      users.extraGroups.vboxusers.members = [ \"pinpox\" ];\n      virtualisation.virtualbox.host.enable = true;\n      # virtualisation.virtualbox.host.enableKvm = true;\n      # virtualisation.virtualbox.host.addNetworkInterface = false;\n      # virtualisation.virtualbox.host.enableExtensionPack = true;\n    })\n  ];\n}\n"
  },
  {
    "path": "modules/wastebin/default.nix",
    "content": "{\n  config,\n  lib,\n  pkgs,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.wastebin;\nin\n{\n\n  options.pinpox.services.wastebin.enable = mkEnableOption \"wastebin server\";\n\n  config = mkIf cfg.enable {\n\n    clan.core.vars.generators.\"wastebin\" = {\n      files.envfile = { };\n      runtimeInputs = [ pkgs.coreutils ];\n      script = ''\n        echo \"WASTEBIN_PASSWORD_SALT=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 80)\" >> $out/envfile\n        echo \"WASTEBIN_SIGNING_KEY=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 80)\" >> $out/envfile\n      '';\n    };\n\n    # Create system user and group\n    services.wastebin = {\n      enable = true;\n\n      secretFile = config.clan.core.vars.generators.\"wastebin\".files.\"envfile\".path;\n\n      settings = {\n        WASTEBIN_ADDRESS_PORT = \"127.0.0.1:8088\";\n        WASTEBIN_BASE_URL = \"https://paste.0cx.de\";\n        WASTEBIN_HTTP_TIMEOUT = 7;\n        WASTEBIN_MAX_BODY_SIZE = 16384;\n        WASTEBIN_TITLE = \"wastebin\";\n        RUST_LOG = \"warning\";\n      };\n    };\n\n    # Reverse proxy\n    services.caddy.virtualHosts.\"paste.0cx.de\".extraConfig =\n      \"reverse_proxy ${config.services.wastebin.settings.WASTEBIN_ADDRESS_PORT}\";\n  };\n}\n"
  },
  {
    "path": "modules/wayland/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.services.wayland;\nin\n{\n\n  options.pinpox.services.wayland = {\n    enable = mkEnableOption \"wayland configuration\";\n  };\n\n  config = mkIf cfg.enable {\n\n    # Wayland/sway\n    programs.sway.enable = true;\n\n    # Turn on wayland support for some electron apps\n    environment.sessionVariables = {\n      LIBVA_DRIVER_NAME = \"iHD\";\n      NIXOS_OZONE_WL = \"1\";\n    };\n\n    # Extra portals (screensharing)\n    xdg.portal = {\n      enable = true;\n      config.common.default = [\n        \"wlr\"\n        \"gtk\"\n      ];\n      wlr.enable = true;\n      extraPortals = [ pkgs.xdg-desktop-portal-gtk ];\n    };\n\n    environment.systemPackages = with pkgs; [\n      xdg-desktop-portal\n      wdisplays # Configure screen placement\n    ];\n  };\n}\n"
  },
  {
    "path": "modules/web-vm/default.nix",
    "content": "{ lib, config, ... }:\nwith lib;\nlet\n  cfg = config.pinpox.services.web-vm;\nin\n{\n\n  options.pinpox.services.web-vm.enable = mkEnableOption \"Web VM\";\n\n  config = mkIf cfg.enable {\n    services.caddy = {\n      enable = true;\n      virtualHosts.\"vm.0cx.de\".extraConfig = ''\n        root * /var/www/vm-test\n        encode zstd gzip\n        header Cross-Origin-Embedder-Policy require-corp\n        header Cross-Origin-Opener-Policy same-origin\n        file_server\n      '';\n    };\n  };\n}\n"
  },
  {
    "path": "modules/yubikey/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  age-plugin-picohsm,\n  passage-secret-service,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.yubikey;\nin\n{\n\n  options.pinpox.defaults.yubikey.enable = mkEnableOption \"yubikey defaults\";\n\n  config = mkIf cfg.enable {\n\n    # OpenSC PIN caching (per process)\n    # environment.etc.\"opensc.conf\".text = ''\n    #   app default {\n    #     framework pkcs15 {\n    #       use_pin_caching = true;\n    #       pin_cache_counter = 10;\n    #       pin_cache_ignore_user_consent = true;\n    #     }\n    #   }\n    # '';\n\n  services.pcscd.enable = true;\n\n\n  # Allow pcscd access for SSH sessions (not just graphical)\n  security.polkit.extraConfig = ''\n    polkit.addRule(function(action, subject) {\n      if (action.id == \"org.debian.pcsc-lite.access_pcsc\" ||\n          action.id == \"org.debian.pcsc-lite.access_card\") {\n        return polkit.Result.YES;\n      }\n    });\n  '';\n\n  environment.systemPackages = [\n    age-plugin-picohsm.packages.${pkgs.system}.default\n    pkgs.age\n    pkgs.opensc\n    pkgs.keyutils\n  ];\n\n  environment.sessionVariables = {\n    PICOHSM_ASKPASS = \"${age-plugin-picohsm.packages.${pkgs.system}.default}/bin/picohsm-askpass\";\n    PICOHSM_ASKPASS_BACKEND = lib.getExe pkgs.noctalia-askpass;\n  };\n\n\n    security.tpm2.enable = true;\n    security.tpm2.pkcs11.enable = true; # expose /run/current-system/sw/lib/libtpm2_pkcs11.so\n    security.tpm2.pkcs11.package = pkgs.tpm2-pkcs11-esapi;\n    security.tpm2.tctiEnvironment.enable = true; # TPM2TOOLS_TCTI and TPM2_PKCS11_TCTI env variables\n    users.users.pinpox.extraGroups = [ config.security.tpm2.tssGroup ]; # tss group has access to TPM devices\n\n    programs.ssh.startAgent = true;\n    # OpenSSH 10.2+ uses comma-separated list (not colon) for -P whitelist\n    programs.ssh.agentPKCS11Whitelist = \"${config.security.tpm2.pkcs11.package}/lib/libtpm2_pkcs11.so,${pkgs.opensc}/lib/opensc-pkcs11.so\";\n\n    # services.yubikey-agent.enable = false;\n    services.udev.packages = [ pkgs.yubikey-personalization ];\n\n    systemd.user.services.passage-secret-service = {\n      description = \"passage-backed D-Bus Secret Service\";\n      partOf = [ \"graphical-session.target\" ];\n      after = [ \"graphical-session.target\" ];\n      wantedBy = [ \"graphical-session.target\" ];\n      serviceConfig = {\n        Type = \"simple\";\n        ExecStart = lib.getExe passage-secret-service.packages.${pkgs.system}.passage-secret-service;\n        Restart = \"on-failure\";\n        RestartSec = 3;\n      };\n    };\n  };\n}\n"
  },
  {
    "path": "modules/zsh/default.nix",
    "content": "{\n  config,\n  pkgs,\n  lib,\n  ...\n}:\nwith lib;\nlet\n  cfg = config.pinpox.defaults.zsh;\nin\n{\n\n  options.pinpox.defaults.zsh = {\n    enable = mkEnableOption \"ZSH defaults\";\n  };\n  config = mkIf cfg.enable {\n\n    environment.systemPackages = with pkgs; [ zsh ];\n\n    # Needed for yubikey to work\n    environment.shellInit = ''\n      export ZDOTDIR=$HOME/.config/zsh\n    '';\n\n    programs.zsh = {\n      enable = true;\n      shellAliases = {\n        vim = \"nvim\";\n      };\n      enableCompletion = true;\n      autosuggestions.enable = true;\n    };\n\n    # Needed for zsh completion of system packages, e.g. systemd\n    environment.pathsToLink = [ \"/share/zsh\" ];\n  };\n}\n"
  },
  {
    "path": "overlays/default.nix",
    "content": "inputs: flake-self: pinpox-utils:\nlet\n  # Pass flake inputs to overlay so we can use the sources pinned in flake.lock\n  # instead of having to keep sha256 hashes in each package for src\n  inherit inputs;\n\n  # Pass flake itself, so we can build woodpecker-pipeline and manual\n  inherit flake-self;\nin\nself: super: {\n\n  manual = super.callPackage ../packages/manual {\n    inherit inputs;\n    inherit pinpox-utils;\n    inherit flake-self;\n  };\n\n  woodpecker-pipeline = super.callPackage ../packages/woodpecker-pipeline {\n    inherit inputs;\n    inherit flake-self;\n  };\n\n  # Override unfree src with flake input\n  # ndi = super.ndi.overrideAttrs (old: {\n  #   src = inputs.ndi-linux;\n  #   unpackPhase = ''\n  #     echo y | $src;\n  #     sourceRoot=\"NDI SDK for Linux\";\n  #   '';\n  # });\n\n  # TODO remove when fixed upsteam\n  zynaddsubfx = super.zynaddsubfx.overrideAttrs (old: {\n    CXXFLAGS = [\n      # GCC 13: error: 'uint8_t' does not name a type\n      \"-include cstdint\"\n    ];\n  });\n\n  # To override packages from master input do:\n  # pamixer = inputs.nixpkgs-master.legacyPackages.\"${super.system}\".pamixer;\n\n  # intel-graphics-compiler =\n  #   inputs.nixpkgs-master.legacyPackages.\"${super.system}\".intel-graphics-compiler;\n\n  # Override tpm2-pytss from master for all python versions\n  # https://github.com/NixOS/nixpkgs/issues/417992\n  # python3 = super.python3.override {\n  #   packageOverrides = python-self: python-super: {\n  #     tpm2-pytss = inputs.nixpkgs-master.legacyPackages.\"${super.system}\".python3Packages.tpm2-pytss;\n  #   };\n  # };\n  #\n\n  inherit (inputs.llm-agents.packages.${super.stdenv.hostPlatform.system})\n    pi\n    openspec\n    claude-code\n    but\n    gitbutler\n    ;\n\n  rio = inputs.rio.packages.${super.stdenv.hostPlatform.system}.default;\n\n  # TODO: Remove once merged upstream in nixpkgs\n  groups-relay = super.callPackage ../packages/groups-relay { };\n\n  # Example package, used only for tests\n  hello-custom = super.callPackage ../packages/hello-custom { };\n  # river-luatile = super.callPackage ../packages/river-luatile { };\n  fritzbox_exporter = super.callPackage ../packages/fritzbox_exporter { };\n  mqtt2prometheus = super.callPackage ../packages/mqtt2prometheus { };\n\n  # Custom packages. Will be made available on all machines and used where\n  # needed.\n  smartmon-script = super.callPackage ../packages/smartmon-script { };\n  noctalia-askpass = super.callPackage ../packages/noctalia-askpass { };\n  machine-report = super.callPackage ../packages/machine-report { };\n\n  # Use custom neovim in standalone flake\n  neovim = inputs.pinpox-neovim.packages.${super.stdenv.hostPlatform.system}.pinpox-neovim;\n\n  # ZSH plugins\n  zsh-abbrev-alias = super.callPackage ../packages/zsh-abbrev-alias { inputs = inputs; };\n  zsh-colored-man-pages = super.callPackage ../packages/zsh-colored-man-pages { inputs = inputs; };\n  zsh-async = super.callPackage ../packages/zsh-async { inputs = inputs; };\n\n}\n"
  },
  {
    "path": "overlays/nextcloud.patch",
    "content": "From 3c3e45f0ad4b4f10161197f70daf46c8888ba91e Mon Sep 17 00:00:00 2001\nFrom: Josh <josh.t.richards@gmail.com>\nDate: Fri, 23 Feb 2024 12:55:58 -0500\nSubject: [PATCH] fix(Files): Change how scanner diffs for changed metadata\n\nFixes #43408\n\nSigned-off-by: Josh <josh.t.richards@gmail.com>\n---\n lib/private/Files/Cache/Scanner.php | 49 +++++++++++++++++++++++++++--\n 1 file changed, 47 insertions(+), 2 deletions(-)\n\ndiff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php\nindex 1c66f3af8d2b0..4aef73b9b2522 100644\n--- a/lib/private/Files/Cache/Scanner.php\n+++ b/lib/private/Files/Cache/Scanner.php\n@@ -221,8 +221,9 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData =\n \t\t\t\t\t\t}\n \n \t\t\t\t\t\t// Only update metadata that has changed\n-\t\t\t\t\t\t$newData = array_diff_assoc($data, $cacheData->getData());\n-\n+\t\t\t\t\t\t// i.e. get all the values in $data that are not present in the cache already\n+\t\t\t\t\t\t$newData = $this->array_diff_assoc_multi($data, $cacheData->getData());\n+\t\t\t\t\t\t\n \t\t\t\t\t\t// make it known to the caller that etag has been changed and needs propagation\n \t\t\t\t\t\tif (isset($newData['etag'])) {\n \t\t\t\t\t\t\t$data['etag_changed'] = true;\n@@ -369,6 +370,50 @@ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $loc\n \t\treturn $data;\n \t}\n \n+\t/**\n+\t * Compares $array1 against $array2 and returns all the values in $array1 that are not in $array2\n+\t * Note this is a one-way check - i.e. we don't care about things that are in $array2 that aren't in $array1\n+\t *\n+\t * Supports multi-dimensional arrays\n+\t * Also checks keys/indexes\n+\t * Comparisons are strict just like array_diff_assoc\n+\t * Order of keys/values does not matter\n+\t *\n+\t * @param array $array1\n+\t * @param array $array2\n+\t * @return array with the differences between $array1 and $array1\n+\t * @throws \\InvalidArgumentException if $array1 isn't an actual array\n+\t *\n+\t */\n+\tprotected function array_diff_assoc_multi(array $array1, array $array2) {\n+\t\t\n+\t\t$result = [];\n+\n+\t\tforeach ($array1 as $key => $value) {\n+\t\t\n+\t\t\t// if $array2 doesn't have the same key, that's a result\n+\t\t\tif (!array_key_exists($key, $array2)) {\n+\t\t\t\t$result[$key] = $value;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\n+\t\t\t// if $array2's value for the same key is different, that's a result\n+\t\t\tif ($array2[$key] !== $value && !is_array($value)) {\n+\t\t\t\t$result[$key] = $value;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\n+\t\t\tif (is_array($value)) {\n+\t\t\t\t$nestedDiff = $this->array_diff_assoc_multi($value, $array2[$key]);\n+\t\t\t\tif (!empty($nestedDiff)) {\n+\t\t\t\t\t$result[$key] = $nestedDiff;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\treturn $result;\n+\t}\n+\n \t/**\n \t * Get the children currently in the cache\n \t *\n"
  },
  {
    "path": "packages/fritzbox_exporter/default.nix",
    "content": "{\n  lib,\n  fetchFromGitHub,\n  buildGoModule,\n  pkgs,\n}:\n\nbuildGoModule rec {\n  pname = \"fritzbox_exporter\";\n  version = \"latest\";\n\n  # vendorHash = null;\n  vendorHash = \"sha256-jcHJNTdiYRQcjJr9VcABY5Ark4bmzqsJcn1iMW09Xl0=\";\n\n  nativeBuildInputs = with pkgs; [ pkg-config ];\n\n  # Updated 2022-01-11\n  src = fetchFromGitHub {\n    owner = \"sberk42\";\n    repo = \"fritzbox_exporter\";\n    rev = \"baa6961be43256af0d904642492e016a35f2a135\";\n    sha256 = \"sha256-ANK8sIHn2vx5+XJ0c6U2uQQiDBYhTfQ65RASdXPtF7w=\";\n  };\n\n  meta = with lib; {\n    maintainers = with maintainers; [ pinpox ];\n    license = licenses.asl20;\n    description = \"Fritzbox exporter for prometheus\";\n  };\n}\n"
  },
  {
    "path": "packages/groups-relay/default.nix",
    "content": "{\n  lib,\n  buildGoModule,\n  fetchFromGitHub,\n}:\n\nbuildGoModule {\n  pname = \"groups-relay\";\n  version = \"0-unstable-2025-05-10\";\n\n  src = fetchFromGitHub {\n    owner = \"max21dev\";\n    repo = \"groups-relay\";\n    rev = \"f7f81d4daf9b2d5fb12e04e9081cf2e307763508\";\n    hash = \"sha256-2my17kgpL+5dROB3iQN2SAe9jTPDRHPvW0QIwFun0vg=\";\n  };\n\n  vendorHash = \"sha256-oZgku/7KA/V3IiSH7LnJPSk2mBy3Y3RmypuGDo1d7VA=\";\n\n  meta = {\n    description = \"NIP-29 group chat relay for Nostr, built on khatru and relay29\";\n    homepage = \"https://github.com/max21dev/groups-relay\";\n    license = lib.licenses.mit;\n    mainProgram = \"groups-relay\";\n  };\n}\n"
  },
  {
    "path": "packages/hello-custom/default.nix",
    "content": "{\n  lib,\n  stdenv,\n  fetchurl,\n}:\n\nstdenv.mkDerivation rec {\n  pname = \"hello\";\n  version = \"2.10\";\n\n  src = fetchurl {\n    url = \"mirror://gnu/hello/${pname}-${version}.tar.gz\";\n    sha256 = \"0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i\";\n  };\n\n  doCheck = true;\n\n  meta = with lib; {\n    description = \"A program that produces a familiar, friendly greeting\";\n    longDescription = ''\n      GNU Hello is a program that prints \"Hello, world!\" when you run it.\n      It is fully customizable.\n    '';\n    homepage = \"https://www.gnu.org/software/hello/manual/\";\n    changelog = \"https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${version}\";\n    license = licenses.gpl3Plus;\n    maintainers = [ maintainers.eelco ];\n    platforms = platforms.all;\n  };\n}\n"
  },
  {
    "path": "packages/machine-report/default.nix",
    "content": "{\n  stdenv,\n  lib,\n  makeWrapper,\n  procps,\n  coreutils,\n  gawk,\n  gnused,\n  gnugrep,\n  util-linux,\n  iproute2,\n  net-tools,\n  hostname,\n  acpi,\n  ...\n}:\nstdenv.mkDerivation {\n  pname = \"machine-report\";\n  version = \"0.1.0\";\n\n  src = ./.;\n\n  nativeBuildInputs = [ makeWrapper ];\n\n  installPhase = ''\n    mkdir -p $out/bin\n    cp machine_report.sh $out/bin/machine-report\n    chmod +x $out/bin/machine-report\n\n    wrapProgram $out/bin/machine-report \\\n      --set LC_NUMERIC C \\\n      --prefix PATH : ${\n        lib.makeBinPath [\n          procps\n          coreutils\n          gawk\n          gnused\n          gnugrep\n          util-linux\n          iproute2\n          net-tools\n          hostname\n          acpi\n        ]\n      }\n  '';\n}\n"
  },
  {
    "path": "packages/machine-report/machine_report.sh",
    "content": "#!/usr/bin/env bash\n# TR-100 Machine Report\n# Copyright © 2024, U.S. Graphics, LLC. BSD-3-Clause License.\n\n# Global variables\nMIN_NAME_LEN=5\nMAX_NAME_LEN=13\n\nMIN_DATA_LEN=20\nMAX_DATA_LEN=32\n\nBORDERS_AND_PADDING=7\n\n# Basic configuration, change as needed\nreport_title=\"PINPOX INFORMATION SYSTEMS\"\nlast_login_ip_present=0\nzfs_present=0\nzfs_filesystem=\"zroot/ROOT/os\"\n\n# Utilities\nmax_length() {\n    local max_len=0\n    local len\n\n    for str in \"$@\"; do\n        len=${#str}\n        if (( len > max_len )); then\n            max_len=$len\n        fi\n    done\n\n    if [ $max_len -lt $MAX_DATA_LEN ]; then\n        printf '%s' \"$max_len\"\n    else\n        printf '%s' \"$MAX_DATA_LEN\"\n    fi\n}\n\n# All data strings must go here\nset_current_len() {\n    CURRENT_LEN=$(max_length                                     \\\n        \"$report_title\"                                          \\\n        \"$os_name\"                                               \\\n        \"$os_kernel\"                                             \\\n        \"$net_hostname\"                                          \\\n        \"$net_machine_ip\"                                        \\\n        \"$net_client_ip\"                                         \\\n        \"$net_current_user\"                                      \\\n        \"$cpu_model\"                                             \\\n        \"$cpu_cores_per_socket vCPU(s) / $cpu_sockets Socket(s)\" \\\n        \"$cpu_hypervisor\"                                        \\\n        \"$cpu_freq GHz\"                                          \\\n        \"$cpu_1min_bar_graph\"                                    \\\n        \"$cpu_5min_bar_graph\"                                    \\\n        \"$cpu_15min_bar_graph\"                                   \\\n        \"$zfs_used_gb/$zfs_available_gb GB [$disk_percent%]\"     \\\n        \"$disk_bar_graph\"                                        \\\n        \"$zfs_health\"                                            \\\n        \"$root_used_gb/$root_total_gb GB [$disk_percent%]\"       \\\n        \"${mem_used_gb}/${mem_total_gb} GiB [${mem_percent}%]\"   \\\n        \"${mem_bar_graph}\"                                       \\\n        \"$last_login_time\"                                       \\\n        \"$last_login_ip\"                                         \\\n        \"$last_login_ip\"                                         \\\n        \"$sys_uptime\"                                            \\\n        \"$nixos_gen_info\"                                        \\\n        \"$bat_info\"                                              \\\n    )\n}\n\nPRINT_HEADER() {\n    local length=$((CURRENT_LEN+MAX_NAME_LEN+BORDERS_AND_PADDING))\n\n    local top=\"┌\"\n    local bottom=\"├\"\n    for (( i = 0; i < length - 2; i++ )); do\n        top+=\"┬\"\n        bottom+=\"┴\"\n    done\n    top+=\"┐\"\n    bottom+=\"┤\"\n\n    printf '%s\\n' \"$top\"\n    printf '%s\\n' \"$bottom\"\n}\n\nPRINT_CENTERED_DATA() {\n    local max_len=$((CURRENT_LEN+MAX_NAME_LEN-BORDERS_AND_PADDING))\n    local text=\"$1\"\n    local total_width=$((max_len + 12))\n\n    local text_len=${#text}\n    local padding_left=$(( (total_width - text_len) / 2 ))\n    local padding_right=$(( total_width - text_len - padding_left ))\n\n    printf \"│%${padding_left}s%s%${padding_right}s│\\n\" \"\" \"$text\" \"\"\n}\n\nPRINT_DIVIDER() {\n    # either \"top\" or \"bottom\", no argument means middle divider\n    local side=\"$1\"\n    case \"$side\" in\n        \"top\")\n            local left_symbol=\"├\"\n            local middle_symbol=\"┬\"\n            local right_symbol=\"┤\"\n            ;;\n        \"bottom\")\n            local left_symbol=\"└\"\n            local middle_symbol=\"┴\"\n            local right_symbol=\"┘\"\n            ;;\n        *)\n            local left_symbol=\"├\"\n            local middle_symbol=\"┼\"\n            local right_symbol=\"┤\"\n    esac\n\n    local length=$((CURRENT_LEN+MAX_NAME_LEN+BORDERS_AND_PADDING))\n    local divider=\"$left_symbol\"\n    for (( i = 0; i < length - 3; i++ )); do\n        divider+=\"─\"\n        if [ \"$i\" -eq 14 ]; then\n            divider+=\"$middle_symbol\"\n        fi\n    done\n    divider+=\"$right_symbol\"\n    printf '%s\\n' \"$divider\"\n}\n\nPRINT_DATA() {\n    local name=\"$1\"\n    local data=\"$2\"\n    local max_data_len=$CURRENT_LEN\n\n    # Pad name\n    local name_len=${#name}\n    if (( name_len < MIN_NAME_LEN )); then\n        name=$(printf \"%-${MIN_NAME_LEN}s\" \"$name\")\n    elif (( name_len > MAX_NAME_LEN )); then\n        name=$(echo \"$name\" | cut -c 1-$((MAX_NAME_LEN-3)))...\n    else\n        name=$(printf \"%-${MAX_NAME_LEN}s\" \"$name\")\n    fi\n\n    # Truncate or pad data\n    local data_len=${#data}\n    if (( data_len >= MAX_DATA_LEN || data_len == MAX_DATA_LEN-1 )); then\n        data=$(echo \"$data\" | cut -c 1-$((MAX_DATA_LEN-3-2)))...\n    else\n        data=$(printf \"%-${max_data_len}s\" \"$data\")\n    fi\n\n    printf \"│ %-${MAX_NAME_LEN}s │ %s │\\n\" \"$name\" \"$data\"\n}\n\nPRINT_FOOTER() {\n    local length=$((CURRENT_LEN+MAX_NAME_LEN+BORDERS_AND_PADDING))\n    local footer=\"└\"\n    for (( i = 0; i < length - 3; i++ )); do\n        footer+=\"─\"\n        if [ \"$i\" -eq 14 ]; then\n            footer+=\"┴\"\n        fi\n    done\n    footer+=\"┘\"\n    printf '%s\\n' \"$footer\"\n}\n\nbar_graph() {\n    local percent\n    local num_blocks\n    local width=$CURRENT_LEN\n    local graph=\"\"\n    local used=$1\n    local total=$2\n\n    if (( total == 0 )); then\n        percent=0\n    else\n        percent=$(awk -v used=\"$used\" -v total=\"$total\" 'BEGIN { printf \"%.2f\", (used / total) * 100 }')\n    fi\n\n    num_blocks=$(awk -v percent=\"$percent\" -v width=\"$width\" 'BEGIN { printf \"%d\", (percent / 100) * width }')\n\n    for (( i = 0; i < num_blocks; i++ )); do\n        graph+=\"█\"\n    done\n    for (( i = num_blocks; i < width; i++ )); do\n        graph+=\"░\"\n    done\n    printf \"%s\" \"${graph}\"\n}\n\nget_ip_addr() {\n    # Initialize variables\n    ipv4_address=\"\"\n    ipv6_address=\"\"\n\n    # Check if ifconfig command exists\n    if command -v ifconfig &> /dev/null; then\n        # Try to get IPv4 address using ifconfig\n        ipv4_address=$(ifconfig | awk '\n            /^[a-z]/ {iface=$1}\n            iface != \"lo:\" && iface !~ /^docker/ && /inet / && !found_ipv4 {found_ipv4=1; print $2}')\n\n        # If IPv4 address not available, try IPv6 using ifconfig\n        if [ -z \"$ipv4_address\" ]; then\n            ipv6_address=$(ifconfig | awk '\n                /^[a-z]/ {iface=$1}\n                iface != \"lo:\" && iface !~ /^docker/ && /inet6 / && !found_ipv6 {found_ipv6=1; print $2}')\n        fi\n    elif command -v ip &> /dev/null; then\n        # Try to get IPv4 address using ip addr\n        ipv4_address=$(ip -o -4 addr show | awk '\n            $2 != \"lo\" && $2 !~ /^docker/ {split($4, a, \"/\"); if (!found_ipv4++) print a[1]}')\n\n        # If IPv4 address not available, try IPv6 using ip addr\n        if [ -z \"$ipv4_address\" ]; then\n            ipv6_address=$(ip -o -6 addr show | awk '\n                $2 != \"lo\" && $2 !~ /^docker/ {split($4, a, \"/\"); if (!found_ipv6++) print a[1]}')\n        fi\n    fi\n\n    # If neither IPv4 nor IPv6 address is available, assign \"No IP found\"\n    if [ -z \"$ipv4_address\" ] && [ -z \"$ipv6_address\" ]; then\n        ip_address=\"No IP found\"\n    else\n        # Prioritize IPv4 if available, otherwise use IPv6\n        ip_address=\"${ipv4_address:-$ipv6_address}\"\n    fi\n\n    printf '%s' \"$ip_address\"\n}\n\n# Operating System Information\nsource /etc/os-release\nos_name=\"${ID^} ${VERSION} ${VERSION_CODENAME^}\"\nos_kernel=$({ uname; uname -r; } | tr '\\n' ' ')\n\n# NixOS Generation (conditional)\nnixos_gen_present=0\nif [ -L /nix/var/nix/profiles/system ]; then\n    nixos_generation=$(readlink /nix/var/nix/profiles/system | sed 's/system-\\([0-9]*\\)-link/\\1/')\n    nixos_gen_date=$(stat -c '%y' /nix/var/nix/profiles/system | awk '{print $1, $2}' | cut -d. -f1)\n    nixos_gen_info=\"Gen ${nixos_generation} / ${nixos_gen_date}\"\n    nixos_gen_present=1\nfi\n\n# Battery Information (conditional)\nbat_present=0\nif command -v acpi &> /dev/null; then\n    bat_line=$(acpi -b 2>/dev/null | head -n 1)\n    if [ -n \"$bat_line\" ] && ! echo \"$bat_line\" | grep -q \"No support\"; then\n        bat_status=$(echo \"$bat_line\" | awk -F': ' '{print $2}' | cut -d',' -f1)\n        bat_percent=$(echo \"$bat_line\" | grep -oP '[0-9]+(?=%)')\n        bat_detail=$(echo \"$bat_line\" | awk -F', ' '{if (NF>=3) print $3}')\n\n        # Compute wattage from /sys if possible\n        bat_watts=\"\"\n        for bat_dir in /sys/class/power_supply/BAT*; do\n            if [ -d \"$bat_dir\" ]; then\n                if [ -f \"$bat_dir/voltage_now\" ] && [ -f \"$bat_dir/current_now\" ]; then\n                    bat_watts=$(awk -v v=\"$(cat \"$bat_dir/voltage_now\")\" -v c=\"$(cat \"$bat_dir/current_now\")\" \\\n                        'BEGIN { printf \"%.1f\", (v * c) / 1e12 }')\n                elif [ -f \"$bat_dir/power_now\" ]; then\n                    bat_watts=$(awk -v p=\"$(cat \"$bat_dir/power_now\")\" 'BEGIN { printf \"%.1f\", p / 1e6 }')\n                fi\n                break\n            fi\n        done\n\n        if [ -n \"$bat_watts\" ] && [ \"$bat_watts\" != \"0.0\" ]; then\n            bat_info=\"${bat_percent}% ${bat_status} [${bat_watts}W]\"\n        else\n            bat_info=\"${bat_percent}% ${bat_status}\"\n        fi\n        bat_present=1\n    fi\nfi\n\n# Network Information\nnet_current_user=$(whoami)\nif ! [ \"$(command -v hostname)\" ]; then\n    net_hostname=$(grep -w \"$(uname -n)\" /etc/hosts | awk '{print $2}' | head -n 1)\nelse\n    net_hostname=$(hostname -f)\nfi\n\nif [ -z \"$net_hostname\" ]; then net_hostname=\"Not Defined\"; fi\n\nnet_machine_ip=$(get_ip_addr)\nnet_client_ip=$(who am i | awk '{print $5}' | tr -d '()')\nif [ -z \"$net_client_ip\" ]; then\n    net_client_ip=\"Not connected\"\nfi\nnet_dns_ip=($(grep '^nameserver [0-9.]' /etc/resolv.conf | awk '{print $2}'))\n\n# CPU Information\ncpu_model=\"$(lscpu | grep 'Model name' | grep -v 'BIOS' | cut -f 2 -d ':' | awk '{print $1 \" \"  $2 \" \" $3 \" \" $4}')\"\ncpu_hypervisor=\"$(lscpu | grep 'Hypervisor vendor' | cut -f 2 -d ':' | awk '{$1=$1}1')\"\nif [ -z \"$cpu_hypervisor\" ]; then\n    cpu_hypervisor=\"Bare Metal\"\nfi\n\ncpu_cores=\"$(nproc --all)\"\ncpu_cores_per_socket=\"$(lscpu | grep 'Core(s) per socket' | cut -f 2 -d ':'| awk '{$1=$1}1')\"\ncpu_sockets=\"$(lscpu | grep 'Socket(s)' | cut -f 2 -d ':' | awk '{$1=$1}1')\"\ncpu_freq=\"$(grep 'cpu MHz' /proc/cpuinfo | cut -f 2 -d ':' | awk 'NR==1 { printf \"%.2f\", $1 / 1000 }')\" # Convert from M to G units\n\nload_avg_1min=$(uptime | awk -F'load average: ' '{print $2}' | cut -d ',' -f1 | tr -d ' ')\nload_avg_5min=$(uptime | awk -F'load average: ' '{print $2}' | cut -d ',' -f2 | tr -d ' ')\nload_avg_15min=$(uptime| awk -F'load average: ' '{print $2}' | cut -d ',' -f3 | tr -d ' ')\n\n# Memory Information\nmem_total=$(grep 'MemTotal' /proc/meminfo | awk '{print $2}')\nmem_available=$(grep 'MemAvailable' /proc/meminfo | awk '{print $2}')\nmem_used=$((mem_total - mem_available))\nmem_percent=$(awk -v used=\"$mem_used\" -v total=\"$mem_total\" 'BEGIN { printf \"%.2f\", (used / total) * 100 }')\nmem_percent=$(printf \"%.2f\" \"$mem_percent\")\nmem_total_gb=$(echo \"$mem_total\" | awk '{ printf \"%.2f\", $1 / (1024 * 1024) }') # (From Ki to Gi units)\nmem_available_gb=$(echo \"$mem_available\" | awk '{ printf \"%.2f\", $1 / (1024 * 1024) }') # (From Ki to Gi units) Not used currently\nmem_used_gb=$(echo \"$mem_used\" | awk '{ printf \"%.2f\", $1 / (1024 * 1024) }')\n\n# Disk Information\nif [ \"$(command -v zfs)\" ] && [ \"$(grep -q \"zfs\" /proc/mounts)\" ]; then\n    zfs_present=1\n    zfs_health=$(zpool status -x zroot | grep -q \"is healthy\" && echo  \"HEALTH O.K.\")\n    zfs_available=$(zfs get -o value -Hp available \"$zfs_filesystem\")\n    zfs_used=$(zfs get -o value -Hp used \"$zfs_filesystem\")\n    zfs_available_gb=$(echo \"$zfs_available\" | awk '{ printf \"%.2f\", $1 / (1024 * 1024 * 1024) }') # (To G units)\n    zfs_used_gb=$(echo \"$zfs_used\" | awk '{ printf \"%.2f\", $1 / (1024 * 1024 * 1024) }') # (To G units)\n    disk_percent=$(awk -v used=\"$zfs_used\" -v available=\"$zfs_available\" 'BEGIN { printf \"%.2f\", (used / available) * 100 }')\nelse\n    # Thanks https://github.com/AnarchistHoneybun\n    root_partition=\"/\"\n    root_used=$(df -m \"$root_partition\" | awk 'NR==2 {print $3}')\n    root_total=$(df -m \"$root_partition\" | awk 'NR==2 {print $2}')\n    root_total_gb=$(awk -v total=\"$root_total\" 'BEGIN { printf \"%.2f\", total / 1024 }')\n    root_used_gb=$(awk -v used=\"$root_used\" 'BEGIN { printf \"%.2f\", used / 1024 }')\n    disk_percent=$(awk -v used=\"$root_used\" -v total=\"$root_total\" 'BEGIN { printf \"%.2f\", (used / total) * 100 }')\nfi\n\n# Last login and Uptime\nif command -v lastlog &> /dev/null; then\n    last_login=$(lastlog -u \"$USER\")\n    last_login_ip=$(echo \"$last_login\" | awk 'NR==2 {print $3}')\n\n    # Check if last_login_ip is an IP address\n    if [[ \"$last_login_ip\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n        last_login_ip_present=1\n        last_login_time=$(echo \"$last_login\" | awk 'NR==2 {print $6, $7, $10, $8}')\n    else\n        last_login_time=$(echo \"$last_login\" | awk 'NR==2 {print $4, $5, $8, $6}')\n        # Check for **Never logged in** edge case\n        if [ \"$last_login_time\" = \"in**\" ]; then\n            last_login_time=\"Never logged in\"\n        fi\n    fi\nelse\n    # Fallback to `last` for systems without lastlog (e.g. NixOS)\n    last_entry=$(last -n 1 \"$USER\" 2>/dev/null | head -n 1)\n    if [ -n \"$last_entry\" ] && ! [[ \"$last_entry\" =~ ^[[:space:]]*$ ]]; then\n        last_login_ip=$(echo \"$last_entry\" | awk '{print $3}')\n        if [[ \"$last_login_ip\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n            last_login_ip_present=1\n        fi\n        last_login_time=$(echo \"$last_entry\" | awk '{print $4, $5, $6, $7}')\n    else\n        last_login_time=\"Never logged in\"\n    fi\nfi\n\nif uptime -p &> /dev/null; then\n    sys_uptime=$(uptime -p | sed 's/up\\s*//; s/\\s*day\\(s*\\)/d/; s/\\s*hour\\(s*\\)/h/; s/\\s*minute\\(s*\\)/m/')\nelse\n    # Fallback: parse uptime output for systems without -p (e.g. coreutils uptime)\n    sys_uptime=$(uptime | sed 's/.*up\\s*//; s/,\\s*[0-9]* user.*//; s/\\s\\+/ /g')\nfi\n\n# Set current length before graphs get calculated\nset_current_len\n\n# Create graphs\ncpu_1min_bar_graph=$(bar_graph \"$load_avg_1min\" \"$cpu_cores\")\ncpu_5min_bar_graph=$(bar_graph \"$load_avg_5min\" \"$cpu_cores\")\ncpu_15min_bar_graph=$(bar_graph \"$load_avg_15min\" \"$cpu_cores\")\n\nmem_bar_graph=$(bar_graph \"$mem_used\" \"$mem_total\")\n\nif [ $zfs_present -eq 1 ]; then\n    disk_bar_graph=$(bar_graph \"$zfs_used\" \"$zfs_available\")\nelse\n    disk_bar_graph=$(bar_graph \"$root_used\" \"$root_total\")\nfi\n\nif [ $bat_present -eq 1 ]; then\n    bat_bar_graph=$(bar_graph \"$bat_percent\" \"100\")\nfi\n\n# Machine Report\nPRINT_HEADER\nPRINT_CENTERED_DATA \"$report_title\"\nPRINT_CENTERED_DATA \"$(echo \"${net_hostname%%.*}\" | tr '[:lower:]' '[:upper:]') MACHINE REPORT\"\nPRINT_DIVIDER \"top\"\nPRINT_DATA \"OS\" \"$os_name\"\nPRINT_DATA \"KERNEL\" \"$os_kernel\"\nif [ $nixos_gen_present -eq 1 ]; then\n    PRINT_DATA \"GENERATION\" \"$nixos_gen_info\"\nfi\nPRINT_DIVIDER\nPRINT_DATA \"HOSTNAME\" \"$net_hostname\"\nPRINT_DATA \"MACHINE IP\" \"$net_machine_ip\"\nPRINT_DATA \"CLIENT  IP\" \"$net_client_ip\"\n\nfor dns_num in \"${!net_dns_ip[@]}\"; do\n    PRINT_DATA \"DNS  IP $(($dns_num + 1))\" \"${net_dns_ip[dns_num]}\"\ndone\n\nPRINT_DATA \"USER\" \"$net_current_user\"\nPRINT_DIVIDER\nPRINT_DATA \"PROCESSOR\" \"$cpu_model\"\nPRINT_DATA \"CORES\" \"$cpu_cores_per_socket vCPU(s) / $cpu_sockets Socket(s)\"\nPRINT_DATA \"HYPERVISOR\" \"$cpu_hypervisor\"\nPRINT_DATA \"CPU FREQ\" \"$cpu_freq GHz\"\nPRINT_DATA \"LOAD  1m\" \"$cpu_1min_bar_graph\"\nPRINT_DATA \"LOAD  5m\" \"$cpu_5min_bar_graph\"\nPRINT_DATA \"LOAD 15m\" \"$cpu_15min_bar_graph\"\n\nif [ $zfs_present -eq 1 ]; then\n    PRINT_DIVIDER\n    PRINT_DATA \"VOLUME\" \"$zfs_used_gb/$zfs_available_gb GB [$disk_percent%]\"\n    PRINT_DATA \"DISK USAGE\" \"$disk_bar_graph\"\n    PRINT_DATA \"ZFS HEALTH\" \"$zfs_health\"\nelse\n    PRINT_DIVIDER\n    PRINT_DATA \"VOLUME\" \"$root_used_gb/$root_total_gb GB [$disk_percent%]\"\n    PRINT_DATA \"DISK USAGE\" \"$disk_bar_graph\"\nfi\n\nPRINT_DIVIDER\nPRINT_DATA \"MEMORY\" \"${mem_used_gb}/${mem_total_gb} GiB [${mem_percent}%]\"\nPRINT_DATA \"USAGE\" \"${mem_bar_graph}\"\nif [ $bat_present -eq 1 ]; then\n    PRINT_DIVIDER\n    PRINT_DATA \"BATTERY\" \"$bat_info\"\n    PRINT_DATA \"USAGE\" \"$bat_bar_graph\"\nfi\nPRINT_DIVIDER\nPRINT_DATA \"LAST LOGIN\" \"$last_login_time\"\n\nif [ $last_login_ip_present -eq 1 ]; then\n    PRINT_DATA \"\" \"$last_login_ip\"\nfi\n\nPRINT_DATA \"UPTIME\" \"$sys_uptime\"\nPRINT_DIVIDER \"bottom\"\n"
  },
  {
    "path": "packages/manual/default.nix",
    "content": "{\n  stdenvNoCC,\n  pkgs,\n  flake-self,\n  inputs,\n  pinpox-utils,\n}:\n\nstdenvNoCC.mkDerivation rec {\n  pname = \"flake-manual\";\n  version = \"latest\";\n  src = ./.;\n  dontConfigure = true;\n  dontUnpack = true;\n\n  buildPhase =\n\n    let\n      options-json =\n        let\n\n          isValidOpt = a: (builtins.hasAttr \"_type\" a) && (a._type == \"option\");\n\n          getOptionValues =\n            opt: path:\n            if builtins.typeOf opt == \"set\" then\n              if isValidOpt opt then\n                {\n                  inherit path;\n                  name = builtins.concatStringsSep \".\" path;\n                  example = if builtins.hasAttr \"example\" opt then opt.example else \"\";\n                  description = if builtins.hasAttr \"description\" opt then opt.description else \"\";\n                  default =\n                    if builtins.hasAttr \"defaultText\" opt then\n                      opt.defaultText\n                    else\n                      let\n                        defaultEval = builtins.tryEval (if builtins.hasAttr \"default\" opt then opt.default else \"\");\n                      in\n                      if defaultEval.success then defaultEval.value else \"<evaluation failed>\";\n                  type = opt.type.description;\n                  documentedOption = true;\n                }\n              else\n                # it is a set, but has no \"default\", recurse\n                builtins.mapAttrs (name: value: getOptionValues value (path ++ [ \"${name}\" ])) opt\n            else\n              { }; # it is no set\n        in\n        pkgs.writeTextFile {\n          name = \"options.json\";\n          text = builtins.toJSON {\n            options = pkgs.lib.attrsets.collect (o: o ? \"documentedOption\") (\n              pkgs.lib.attrsets.mapAttrs (\n                name: value:\n                let\n                  allopts = getOptionValues (value (\n                    {\n                      inherit (inputs) flake-self;\n                      inherit pkgs;\n                      inherit pinpox-utils;\n                      lib = pkgs.lib;\n                      config = { };\n                    }\n                    // inputs\n                  )) [ ];\n                in\n                if\n                  # Filter out everything that has no \".options.pinpox\"\n                  builtins.hasAttr \"options\" allopts\n                then\n                  if builtins.hasAttr \"pinpox\" allopts.options then allopts.options.pinpox else null\n                else\n                  null\n              ) flake-self.nixosModules\n            );\n          };\n        };\n    in\n    ''\n      cat ${options-json} | ${pkgs.mustache-go}/bin/mustache --allow-missing-variables=false ${src}/template.html > index.html\n    '';\n\n  installPhase = ''\n    mkdir -p \"$out\"\n    cp index.html \"$out\"\n  '';\n\n  meta = {\n    description = \"Manual for this flake as package\";\n    homepage = \"https://github.com/pinpox/nixos\";\n  };\n}\n"
  },
  {
    "path": "packages/manual/template.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <title>Module Options</title>\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css\"\n    />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\"\n      crossorigin=\"anonymous\"\n    />\n  </head>\n  <body>\n    <section class=\"section\">\n      <div class=\"container\">\n        <h1 class=\"title\">Module options</h1>\n        <p class=\"subtitle\">\n          Options provided by modules in\n          <strong>\n            <a href=\"https://github.com/pinpox/nixos\" target=\"_blank\"\n              >github.com/pinpox/nixos</a\n            ></strong\n          >\n        </p>\n\n        <div class=\"block\">\n          <p class=\"control has-icons-left\">\n            <input class=\"input\" type=\"text\" placeholder=\"Search\" id=\"input\" />\n            <span class=\"icon is-small is-left\">\n              <i class=\"fa fa-search\"></i>\n            </span>\n          </p>\n        </div>\n        <div class=\"results block\" id=\"list\">\n          {{#options}}\n          <div class=\"nixoption block\" id=\"{{name}}\">\n            <div class=\"level\">\n              <div class=\"level-left\">\n                <div class=\"level-item\">\n                  <h3 class=\"title is-3\">\n                    <a href=\"#{{name}}\"># </a> {{name}}\n                  </h3>\n                </div>\n              </div>\n              <div class=\"level-right\">\n                <div class=\"level-item\">\n                  <span class=\"tag is-medium is-info\">{{type}}</span>\n                </div>\n              </div>\n            </div>\n\n            <p class=\"subtitle\">{{description}}</p>\n\n            <div class=\"block\">\n              <p class=\"block\">\n                <strong>Default:</strong> <code>{{default}}</code>\n              </p>\n              <p class=\"block\">\n                <strong>Example:</strong> <code>{{example}}</code>\n              </p>\n            </div>\n          </div>\n          {{/options}}\n        </div>\n      </div>\n    </section>\n  </body>\n</html>\n\n<script charset=\"utf-8\">\n  const input = document.querySelector(\"#input\");\n  const list = document.querySelector(\"#list\");\n  const nixOptions = document.getElementsByClassName(\"nixoption\");\n\n  // Slice node list to be an array\n  const nixOptionsList = [].slice.call(nixOptions);\n\n  // Add event listener for keyup in input\n  input.addEventListener(\"keyup\", (e) => {\n    // Set regex to be global and case insensitive (gi)\n    const match = new RegExp(`${e.target.value}`, \"gi\");\n\n    // Create an array of results that gets passed any matching text\n    let results = nixOptionsList.filter((opt) => match.test(opt.innerText));\n\n    // Remove all html inside the list\n    list.innerHTML = \"\";\n\n    // Create a for loop to loop through your results\n    for (let i = 0; i < results.length; i++) {\n      // Append each result to it's container\n      list.appendChild(results[i]);\n    }\n  });\n</script>\n"
  },
  {
    "path": "packages/mqtt2prometheus/default.nix",
    "content": "{\n  lib,\n  fetchFromGitHub,\n  buildGoModule,\n  pkgs,\n}:\n\n# https://github.com/hikhvar/mqtt2prometheus\n\nbuildGoModule rec {\n  pname = \"mqtt2prometheus\";\n  version = \"latest\";\n\n  # vendorHash = null;\n  vendorHash = \"sha256-5DIU1NUEVI7Fz6UHhC6trva9qd47DwdFNw1OxY6M37s=\";\n\n  nativeBuildInputs = with pkgs; [ pkg-config ];\n\n  # Updated 2022-01-11\n  src = fetchFromGitHub {\n    owner = \"hikhvar\";\n    repo = \"mqtt2prometheus\";\n    rev = \"v0.1.6\";\n    sha256 = \"sha256-55WAuu6n2h0IPIjt8iTJzNSF1Fe7roxiIS8MUXmu5Tc=\";\n  };\n\n  meta = with lib; {\n    maintainers = with maintainers; [ pinpox ];\n    license = licenses.mit;\n    description = \"TODO\";\n  };\n}\n"
  },
  {
    "path": "packages/noctalia-askpass/default.nix",
    "content": "{\n  writeShellApplication,\n  noctalia-shell,\n  procps,\n  coreutils,\n}:\nwriteShellApplication {\n  name = \"noctalia-askpass\";\n  runtimeInputs = [\n    noctalia-shell\n    procps\n    coreutils\n  ];\n  text = ''\n    # noctalia-askpass — askpass backend that uses noctalia to show\n    # a password/PIN entry dialog. Implements the standard askpass\n    # contract: prompt as $1, password printed to stdout, exit 1 on cancel.\n    #\n    # Usage:\n    #   SSH_ASKPASS=/path/to/noctalia-askpass\n    #   SUDO_ASKPASS=/path/to/noctalia-askpass\n    #   PICOHSM_ASKPASS_BACKEND=/path/to/noctalia-askpass\n\n    PROMPT=\"''${1:-Enter password}\"\n\n    # Figure out what program is requesting the password by walking the process tree\n    CALLER=\"\"\n    if [ -n \"''${PPID:-}\" ]; then\n      PID=\"$PPID\"\n      for _ in 1 2 3 4 5; do\n        if [ \"$PID\" -le 1 ] 2>/dev/null; then break; fi\n        CMD=$(ps -o comm= \"$PID\" 2>/dev/null || true)\n        if [ -n \"$CMD\" ] && [ \"$CMD\" != \"bash\" ] && [ \"$CMD\" != \"sh\" ] && [ \"$CMD\" != \"picohsm-askpass\" ] && [ \"$CMD\" != \"noctalia-askpass\" ]; then\n          if [ -n \"$CALLER\" ]; then\n            CALLER=\"$CMD → $CALLER\"\n          else\n            CALLER=\"$CMD\"\n          fi\n        fi\n        PID=$(ps -o ppid= \"$PID\" 2>/dev/null | tr -d ' ' || true)\n        if [ -z \"$PID\" ]; then break; fi\n      done\n    fi\n\n    # Create a named pipe for the response\n    FIFO=$(mktemp -u /tmp/noctalia-askpass-XXXXXX)\n    mkfifo -m 600 \"$FIFO\"\n    trap 'rm -f \"$FIFO\"' EXIT\n\n    # Ask noctalia to show the password dialog\n    noctalia-shell ipc call plugin:noctalia-askpass prompt \"$PROMPT\" \"$CALLER\" \"$FIFO\" &\n\n    # Block until the plugin writes the password (or empty on cancel)\n    PASSWORD=$(cat \"$FIFO\")\n\n    if [ -z \"$PASSWORD\" ]; then\n      exit 1\n    fi\n\n    printf '%s' \"$PASSWORD\"\n  '';\n}\n"
  },
  {
    "path": "packages/raspi-image",
    "content": "\n        # nix build '.#base-image'\n        raspi-image =\n          let\n            system = \"aarch64-linux\";\n          in\n          import \"${nixpkgs}/nixos/lib/make-disk-image.nix\" {\n            pkgs = nixpkgs.legacyPackages.\"${system}\";\n            lib = nixpkgs.lib;\n            config =\n              (nixpkgs.lib.nixosSystem {\n                inherit system;\n                modules = [ ./images/raspi.nix ];\n              }).config;\n            format = \"qcow2\";\n            diskSize = 4096;\n            name = \"raspi-image\";\n          };\n"
  },
  {
    "path": "packages/river-luatile/default.nix",
    "content": "{\n  lib,\n  fetchFromGitHub,\n  openssl,\n  luajit,\n  pkg-config,\n  rustPlatform,\n}:\n\nrustPlatform.buildRustPackage rec {\n  pname = \"river-luatile\";\n  version = \"0.1.0\";\n\n  src = fetchFromGitHub {\n    owner = \"MaxVerevkin\";\n    repo = pname;\n    fetchSubmodules = true;\n    rev = \"v${version}\";\n    sha256 = \"sha256-A8vx8jN4XUUI970ZsWLKBCd5lO9p3w63b9EiGwk/rCU=\";\n  };\n\n  cargoSha256 = \"sha256-udfsd1iONlDSQ/7mzzRNNhoJHmXJsxWdhqeKK/onx+4=\";\n\n  buildInputs = [ luajit ];\n  nativeBuildInputs = [ pkg-config ];\n  PKG_CONFIG_PATH = \"${openssl.dev}/lib/pkgconfig\";\n\n  meta = with lib; {\n    homepage = \"https://github.com/MaxVerevkin/river-luatile\";\n    description = \"Write your own river layout generator in lua\";\n    license = licenses.gpl3;\n    platforms = platforms.linux;\n    maintainers = with maintainers; [ pinpox ];\n  };\n}\n"
  },
  {
    "path": "packages/smartmon-script/default.nix",
    "content": "{\n  stdenv,\n  smartmontools,\n  python3,\n  ...\n}:\nstdenv.mkDerivation {\n  name = \"smartmon-script\";\n  buildInputs = [\n    python3\n    smartmontools\n  ];\n  unpackPhase = \"true\";\n  installPhase = ''\n    mkdir -p $out/bin\n    cp ${./smartmon.py} $out/bin/smartmon-script\n    chmod +x $out/bin/smartmon-script\n  '';\n}\n"
  },
  {
    "path": "packages/smartmon-script/smartmon.py",
    "content": "#!/usr/bin/env python3\nimport argparse\nimport collections\nimport csv\nimport datetime\nimport decimal\nimport re\nimport shlex\nimport subprocess\nimport sys\n\ndevice_info_re = re.compile(r'^(?P<k>[^:]+?)(?:(?:\\sis|):)\\s*(?P<v>.*)$')\n\nata_error_count_re = re.compile(\n    r'^Error (\\d+) \\[\\d+\\] occurred', re.MULTILINE)\n\nself_test_re = re.compile(r'^SMART.*(PASSED|OK)$', re.MULTILINE)\n\ndevice_info_map = {\n    'Vendor': 'vendor',\n    'Product': 'product',\n    'Revision': 'revision',\n    'Logical Unit id': 'lun_id',\n    'Model Family': 'model_family',\n    'Device Model': 'device_model',\n    'Serial Number': 'serial_number',\n    'Firmware Version': 'firmware_version',\n}\n\nsmart_attributes_whitelist = {\n    'airflow_temperature_cel',\n    'command_timeout',\n    'current_pending_sector',\n    'end_to_end_error',\n    'erase_fail_count_total',\n    'g_sense_error_rate',\n    'hardware_ecc_recovered',\n    'host_reads_mib',\n    'host_reads_32mib',\n    'host_writes_mib',\n    'host_writes_32mib',\n    'load_cycle_count',\n    'media_wearout_indicator',\n    'wear_leveling_count',\n    'nand_writes_1gib',\n    'offline_uncorrectable',\n    'power_cycle_count',\n    'power_on_hours',\n    'program_fail_count',\n    'raw_read_error_rate',\n    'reallocated_event_count',\n    'reallocated_sector_ct',\n    'reported_uncorrect',\n    'sata_downshift_count',\n    'seek_error_rate',\n    'spin_retry_count',\n    'spin_up_time',\n    'start_stop_count',\n    'temperature_case',\n    'temperature_celsius',\n    'temperature_internal',\n    'total_lbas_read',\n    'total_lbas_written',\n    'udma_crc_error_count',\n    'unsafe_shutdown_count',\n    'workld_host_reads_perc',\n    'workld_media_wear_indic',\n    'workload_minutes',\n}\n\nMetric = collections.namedtuple('Metric', 'name labels value')\n\nSmartAttribute = collections.namedtuple('SmartAttribute', [\n    'id', 'name', 'flag', 'value', 'worst', 'threshold', 'type', 'updated',\n    'when_failed', 'raw_value',\n])\n\n\nclass Device(collections.namedtuple('DeviceBase', 'path opts')):\n    \"\"\"Representation of a device as found by smartctl --scan output.\"\"\"\n\n    @property\n    def type(self):\n        return self.opts.type\n\n    @property\n    def base_labels(self):\n        return {'device': self.path, 'disk': self.type.partition('+')[2] or '0'}\n\n    def smartctl_select(self):\n        return ['--device', self.type, self.path]\n\n\ndef metric_key(metric, prefix=''):\n    return '{prefix}{metric.name}'.format(prefix=prefix, metric=metric)\n\n\ndef metric_format(metric, prefix=''):\n    key = metric_key(metric, prefix)\n    labels = ','.join(\n        '{k}=\"{v}\"'.format(k=k, v=v.replace('\"', '\\\\\"')) for k, v in metric.labels.items())\n    value = decimal.Decimal(metric.value)\n\n    return '{key}{{{labels}}} {value}'.format(\n        key=key, labels=labels, value=value)\n\n\ndef metric_print_meta(metric, prefix=''):\n    key = metric_key(metric, prefix)\n    print('# HELP {key} SMART metric {metric.name}'.format(\n        key=key, metric=metric))\n    print('# TYPE {key} gauge'.format(key=key))\n\n\ndef metric_print(metric, prefix=''):\n    print(metric_format(metric, prefix))\n\n\ndef smart_ctl(*args, check=True):\n    \"\"\"Wrapper around invoking the smartctl binary.\n    Returns:\n        (str) Data piped to stdout by the smartctl subprocess.\n    \"\"\"\n    return subprocess.run(\n        ['smartctl', *args], stdout=subprocess.PIPE, check=check\n    ).stdout.decode('utf-8')\n\n\ndef smart_ctl_version():\n    return smart_ctl('-V').split('\\n')[0].split()[1]\n\n\ndef find_devices():\n    \"\"\"Find SMART devices.\n    Yields:\n        (Device) Single device found by smartctl.\n    \"\"\"\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-d', '--device', dest='type')\n\n    devices = smart_ctl('--scan-open')\n\n    for device in devices.split('\\n'):\n        device = device.strip()\n        if not device:\n            continue\n\n        tokens = shlex.split(device, comments=True)\n        if not tokens:\n            continue\n\n        yield Device(tokens[0], parser.parse_args(tokens[1:]))\n\n\ndef device_is_active(device):\n    \"\"\"Returns whenever the given device is currently active or not.\n    Args:\n        device: (Device) Device in question.\n    Returns:\n        (bool) True if the device is active and False otherwise.\n    \"\"\"\n    try:\n        smart_ctl('--nocheck', 'standby', *device.smartctl_select())\n    except subprocess.CalledProcessError:\n        return False\n\n    return True\n\n\ndef device_info(device):\n    \"\"\"Query device for basic model information.\n    Args:\n        device: (Device) Device in question.\n    Returns:\n        (generator): Generator yielding:\n            key (str): Key describing the value.\n            value (str): Actual value.\n    \"\"\"\n    info_lines = smart_ctl(\n        '--info', *device.smartctl_select()\n    ).strip().split('\\n')[3:]\n\n    matches = (device_info_re.match(line) for line in info_lines)\n    return (m.groups() for m in matches if m is not None)\n\n\ndef device_smart_capabilities(device):\n    \"\"\"Returns SMART capabilities of the given device.\n    Args:\n        device: (Device) Device in question.\n    Returns:\n        (tuple): tuple containing:\n            (bool): True whenever SMART is available, False otherwise.\n            (bool): True whenever SMART is enabled, False otherwise.\n    \"\"\"\n    groups = device_info(device)\n\n    state = {\n        g[1].split(' ', 1)[0]\n        for g in groups if g[0] == 'SMART support'}\n\n    smart_available = 'Available' in state\n    smart_enabled = 'Enabled' in state\n\n    return smart_available, smart_enabled\n\n\ndef collect_device_info(device):\n    \"\"\"Collect basic device information.\n    Args:\n        device: (Device) Device in question.\n    Yields:\n        (Metric) metrics describing general device information.\n    \"\"\"\n    values = dict(device_info(device))\n    yield Metric('device_info', {\n        **device.base_labels,\n        **{v: values[k] for k, v in device_info_map.items() if k in values}\n    }, True)\n\n\ndef collect_device_health_self_assessment(device):\n    \"\"\"Collect metric about the device health self assessment.\n    Args:\n        device: (Device) Device in question.\n    Yields:\n        (Metric) Device health self assessment.\n    \"\"\"\n    out = smart_ctl('--health', *device.smartctl_select(), check=False)\n\n    self_assessment_passed = bool(self_test_re.search(out))\n\n    yield Metric(\n        'device_smart_healthy', device.base_labels, self_assessment_passed)\n\n\ndef collect_ata_metrics(device):\n    # Fetch SMART attributes for the given device.\n    attributes = smart_ctl(\n        '--attributes', *device.smartctl_select()\n    )\n\n    # replace multiple occurrences of whitespace with a single whitespace\n    # so that the CSV Parser recognizes individual columns properly.\n    attributes = re.sub(r'[\\t\\x20]+', ' ', attributes)\n\n    # Turn smartctl output into a list of lines and skip to the table of\n    # SMART attributes.\n    attribute_lines = attributes.strip().split('\\n')[7:]\n\n    # Some attributes have multiple IDs but have the same name.  Don't\n    # yield attributes that already have been reported before.\n    seen = set()\n\n    reader = csv.DictReader(\n        (line.strip() for line in attribute_lines),\n        fieldnames=SmartAttribute._fields[:-1],\n        restkey=SmartAttribute._fields[-1], delimiter=' ')\n    for entry in reader:\n        # We're only interested in the SMART attributes that are\n        # whitelisted here.\n        entry['name'] = entry['name'].lower()\n        if entry['name'] not in smart_attributes_whitelist:\n            continue\n\n        # Ensure that only the numeric parts are fetched from the raw_value.\n        # Attributes such as 194 Temperature_Celsius reported by my SSD\n        # are in the format of \"36 (Min/Max 24/40)\" which can't be expressed\n        # properly as a prometheus metric.\n        m = re.match(r'^(\\d+)', ' '.join(entry['raw_value']))\n        if not m:\n            continue\n        entry['raw_value'] = m.group(1)\n\n        # Some device models report \"---\" in the threshold value where most\n        # devices would report \"000\". We do the substitution here because\n        # downstream code expects values to be convertable to integer.\n        if entry['threshold'] == '---':\n            entry['threshold'] = '0'\n\n        if entry['name'] in smart_attributes_whitelist and entry['name'] not in seen:\n            labels = {\n                'name': entry['name'],\n                **device.base_labels,\n            }\n\n            for col in 'value', 'worst', 'threshold', 'raw_value':\n                yield Metric(\n                    'attr_{col}'.format(col=col),\n                    labels, entry[col])\n\n            seen.add(entry['name'])\n\n\ndef collect_ata_error_count(device):\n    \"\"\"Inspect the device error log and report the amount of entries.\n    Args:\n        device: (Device) Device in question.\n    Yields:\n        (Metric) Device error count.\n    \"\"\"\n    error_log = smart_ctl(\n        '-l', 'xerror,1', *device.smartctl_select(), check=False)\n\n    m = ata_error_count_re.search(error_log)\n\n    error_count = m.group(1) if m is not None else 0\n\n    yield Metric('device_errors', device.base_labels, error_count)\n\n\ndef collect_disks_smart_metrics(wakeup_disks):\n    now = int(datetime.datetime.utcnow().timestamp())\n\n    for device in find_devices():\n        yield Metric('smartctl_run', device.base_labels, now)\n\n        is_active = device_is_active(device)\n\n        yield Metric('device_active', device.base_labels, is_active)\n\n        # Skip further metrics collection to prevent the disk from\n        # spinning up.\n        if not is_active and not wakeup_disks:\n            continue\n\n        yield from collect_device_info(device)\n\n        smart_available, smart_enabled = device_smart_capabilities(device)\n\n        yield Metric(\n            'device_smart_available', device.base_labels, smart_available)\n        yield Metric(\n            'device_smart_enabled', device.base_labels, smart_enabled)\n\n        # Skip further metrics collection here if SMART is disabled\n        # on the device.  Further smartctl invocations would fail\n        # anyways.\n        if not smart_available:\n            continue\n\n        yield from collect_device_health_self_assessment(device)\n\n        if device.type.startswith('sat'):\n            yield from collect_ata_metrics(device)\n\n            yield from collect_ata_error_count(device)\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-s', '--wakeup-disks', dest='wakeup_disks', action='store_true')\n    args = parser.parse_args(sys.argv[1:])\n\n    version_metric = Metric('smartctl_version', {\n        'version': smart_ctl_version()\n    }, True)\n    metric_print_meta(version_metric, 'smartmon_')\n    metric_print(version_metric, 'smartmon_')\n\n    metrics = list(collect_disks_smart_metrics(args.wakeup_disks))\n    metrics.sort(key=lambda i: i.name)\n\n    previous_name = None\n    for m in metrics:\n        if m.name != previous_name:\n            metric_print_meta(m, 'smartmon_')\n\n            previous_name = m.name\n\n        metric_print(m, 'smartmon_')\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "packages/woodpecker-pipeline/default.nix",
    "content": "# nix run .\\#woodpecker-pipeline\n{\n  pkgs,\n  lib,\n  flake-self,\n  ...\n}:\nwith pkgs;\nlet\n  supportedSystems = [\n    # \"aarch64-linux\"\n    \"x86_64-linux\"\n  ];\n  forAllSystems = lib.genAttrs supportedSystems;\n  pipelineFor = forAllSystems (\n    system:\n    writeText \"pipeline\" (\n      builtins.toJSON {\n        configs =\n          let\n            # Map platform names between woodpecker and nix\n            woodpecker-platforms = {\n              \"aarch64-linux\" = \"linux/arm64\";\n              \"x86_64-linux\" = \"linux/amd64\";\n            };\n            nixFlakeShowStep = {\n              name = \"Nix flake show\";\n              image = \"bash\";\n              commands = [ \"nix flake show\" ];\n            };\n            atticSetupStep = {\n              name = \"Setup Attic\";\n              image = \"bash\";\n              commands = [\n                \"attic login lounge-rocks https://cache.lounge.rocks $ATTIC_KEY --set-default\"\n              ];\n              environment = {\n                ATTIC_KEY.from_secret = \"attic_key\";\n              };\n            };\n            buildAndCacheStep = {\n              name = \"Build all machines and push to cache\";\n              image = \"bash\";\n              commands = [\n                ''nix-fast-build --no-nom --skip-cached --attic-cache lounge-rocks:nix-cache --flake \".#ciBuilds.${system}\"''\n              ];\n            };\n          in\n          pkgs.lib.lists.flatten [\n            (map\n              (arch: {\n                name = \"Hosts with arch: ${arch}\";\n                data = (\n                  builtins.toJSON {\n                    labels = {\n                      backend = \"local\";\n                      platform = woodpecker-platforms.\"${arch}\";\n                    };\n                    when = [\n                      { event = \"manual\"; }\n                      {\n                        event = \"push\";\n                        branch = \"main\";\n                      }\n                    ];\n                    steps = pkgs.lib.lists.flatten (\n                      [ nixFlakeShowStep ]\n                      ++ [ atticSetupStep ]\n                      ++ [ buildAndCacheStep ]\n                      ++ (map (\n                        host:\n                        # Skip hosts with CISkip set or wrong architecture\n                        if\n                          flake-self.nixosConfigurations.${host}.config.pinpox.defaults.CISkip\n                          || (flake-self.nixosConfigurations.${host}.pkgs.stdenv.hostPlatform.system != arch)\n                        then\n                          [ ]\n                        else\n                          [\n                            {\n                              name = \"Build ${host}\";\n                              image = \"bash\";\n                              failure = \"ignore\";\n                              commands = [\n                                \"nix build --print-out-paths '.#ciBuilds.${system}.${host}' -o 'result-${host}'\"\n                              ];\n                            }\n                            {\n                              name = \"Show ${host} info\";\n                              image = \"bash\";\n                              failure = \"ignore\";\n                              commands = [\n                                \"nix path-info --closure-size -h $(readlink -f 'result-${host}')\"\n                              ];\n                            }\n                          ]\n                      ) (builtins.attrNames flake-self.nixosConfigurations))\n                    );\n                  }\n                );\n              })\n              [\n                \"${system}\"\n              ]\n            )\n          ];\n      }\n    )\n  );\nin\npkgs.writeShellScriptBin \"woodpecker-pipeline\" ''\n  # make sure .woodpecker folder exists\n  mkdir -p .woodpecker\n\n  # empty content of .woodpecker folder\n  rm -rf .woodpecker/*\n\n  # copy pipelines to .woodpecker folder\n  ${lib.concatMapStringsSep \"\\n\" (system:\n    let\n      name = builtins.replaceStrings [ \"_\" ] [ \"-\" ] (builtins.head (lib.splitString \"-\" system));\n      arch = builtins.elemAt (lib.splitString \"-\" system) 1;\n    in\n    \"cat ${pipelineFor.${system}} | ${pkgs.jq}/bin/jq '.configs[].data' -r | ${pkgs.jq}/bin/jq > .woodpecker/${name}-${arch}.yaml\"\n  ) supportedSystems}\n''\n"
  },
  {
    "path": "packages/zsh-abbrev-alias/default.nix",
    "content": "{\n  stdenvNoCC,\n  lib,\n  fetchFromGitHub,\n  inputs,\n}:\n\nstdenvNoCC.mkDerivation rec {\n  pname = \"zsh-abbrev-alias\";\n  version = \"latest\";\n\n  src = inputs.zsh-abbrev-alias;\n\n  dontConfigure = true;\n  dontBuild = true;\n\n  installPhase = ''\n    plugindir=\"$out/share/zsh-abbrev-alias\"\n    mkdir -p \"$plugindir\"\n    cp -r * \"$plugindir\"/\n  '';\n\n  meta = with lib; {\n    description = \"ZSH plugin with functionality similar to Vim's abbreviation expansion.\";\n    homepage = \"https://github.com/momo-lab/zsh-abbrev-alias\";\n    license = licenses.mit;\n    platforms = platforms.unix;\n  };\n}\n"
  },
  {
    "path": "packages/zsh-async/default.nix",
    "content": "{\n  stdenvNoCC,\n  lib,\n  fetchFromGitHub,\n  inputs,\n}:\n\nstdenvNoCC.mkDerivation rec {\n  pname = \"zsh-async\";\n  version = \"latest\";\n\n  src = inputs.zsh-async;\n\n  dontConfigure = true;\n  dontBuild = true;\n\n  installPhase = ''\n    plugindir=\"$out/share/zsh-async\"\n    mkdir -p \"$plugindir\"\n    cp -r * \"$plugindir\"/\n  '';\n\n  meta = with lib; {\n    description = \"Because your terminal should be able to perform tasks asynchronously without external tools!\";\n    homepage = \"https://github.com/mafredri/zsh-async\";\n    license = licenses.mit;\n    platforms = platforms.unix;\n  };\n}\n"
  },
  {
    "path": "packages/zsh-colored-man-pages/default.nix",
    "content": "{\n  stdenvNoCC,\n  lib,\n  fetchFromGitHub,\n  inputs,\n}:\n\nstdenvNoCC.mkDerivation rec {\n  pname = \"zsh-colored-man-pages\";\n  version = \"latest\";\n\n  src = inputs.zsh-colored-man-pages;\n\n  dontConfigure = true;\n  dontBuild = true;\n\n  installPhase = ''\n    plugindir=\"$out/share/zsh-colored-man-pages\"\n    mkdir -p \"$plugindir\"\n    cp -r * \"$plugindir\"/\n  '';\n\n  meta = with lib; {\n    description = \"ZSH plugin that colorifies man page\";\n    homepage = \"https://github.com/ael-code/zsh-colored-man-pages\";\n    license = licenses.gpl3;\n    platforms = platforms.unix;\n  };\n}\n"
  },
  {
    "path": "templates/default/flake.nix",
    "content": "{\n  description = \"A simple Go package\";\n\n  # Nixpkgs / NixOS version to use.\n  inputs.nixpkgs.url = \"nixpkgs/nixos-unstable\";\n\n  outputs =\n    { self, nixpkgs }:\n    let\n\n      # to work with older version of flakes\n      lastModifiedDate = self.lastModifiedDate or self.lastModified or \"19700101\";\n\n      # Generate a user-friendly version number.\n      version = builtins.substring 0 8 lastModifiedDate;\n\n      # Helper function to generate an attrset '{ x86_64-linux = f \"x86_64-linux\"; ... }'.\n      forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;\n\n      # Nixpkgs instantiated for supported system types.\n      nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });\n\n    in\n    {\n\n      # Provide some binary packages for selected system types.\n      packages = forAllSystems (\n        system:\n        let\n          pkgs = nixpkgsFor.${system};\n        in\n        {\n          default = pkgs.buildGoModule {\n            pname = \"go-hello\";\n            inherit version;\n            # In 'nix develop', we don't need a copy of the source tree\n            # in the Nix store.\n            src = ./.;\n\n            # This hash locks the dependencies of this package. It is\n            # necessary because of how Go requires network access to resolve\n            # VCS.  See https://www.tweag.io/blog/2021-03-04-gomod2nix/ for\n            # details. Normally one can build with a fake hash and rely on native Go\n            # mechanisms to tell you what the hash should be or determine what\n            # it should be \"out-of-band\" with other tooling (eg. gomod2nix).\n            # To begin with it is recommended to set this, but one must\n            # remember to bump this hash when your dependencies change.\n            # vendorHash = pkgs.lib.fakeHash;\n\n            vendorHash = \"sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=\";\n          };\n        }\n      );\n\n      # Add dependencies that are only needed for development\n      devShells = forAllSystems (\n        system:\n        let\n          pkgs = nixpkgsFor.${system};\n        in\n        {\n          default = pkgs.mkShell {\n            buildInputs = with pkgs; [\n              go\n              gopls\n              gotools\n              go-tools\n            ];\n          };\n        }\n      );\n    };\n}\n"
  },
  {
    "path": "users/pinpox-wraps/chromium/default.nix",
    "content": "{ wlib, lib }:\n\n{\n  apply =\n    {\n      pkgs,\n      extensions ? [ ],\n      profileName ? \"wrapped\",\n      ...\n    }:\n    let\n      # Normalize extensions to ensure updateUrl is present\n      normalizedExtensions = map (\n        ext:\n        ext\n        // {\n          updateUrl = ext.updateUrl or \"https://clients2.google.com/service/update2/crx\";\n        }\n      ) extensions;\n\n      # Create extension JSON files\n      extensionFiles = map (ext: {\n        name = \"External Extensions/${ext.id}.json\";\n        path = pkgs.writeText \"${ext.id}.json\" (builtins.toJSON { external_update_url = ext.updateUrl; });\n      }) normalizedExtensions;\n\n      # Create config directory template with extension settings\n      extensionsDir = pkgs.linkFarm \"chromium-extensions\" extensionFiles;\n\n      preHookScript = ''\n        PROFILE_DIR=\"$HOME/.config/chromium-${profileName}\"\n        EXT_DIR=\"$PROFILE_DIR/External Extensions\"\n\n        # Create profile directory\n        mkdir -p \"$EXT_DIR\"\n\n        # Symlink extension configs from Nix store\n        ${lib.concatMapStringsSep \"\\n\" (ext: ''\n          ln -sf \"${extensionsDir}/External Extensions/${ext.id}.json\" \"$EXT_DIR/${ext.id}.json\"\n        '') normalizedExtensions}\n      '';\n\n      wrappedChromium = wlib.wrapPackage {\n        inherit pkgs;\n        package = pkgs.chromium;\n        binName = \"chromium-wrapped\";\n        preHook = preHookScript;\n        flagSeparator = \"=\";\n        flags.\"--user-data-dir\" = \"$HOME/.config/chromium-${profileName}\";\n      };\n    in\n    {\n      wrapper = wrappedChromium;\n    };\n}\n"
  },
  {
    "path": "users/pinpox-wraps/ffmpeg/default.nix",
    "content": "{ wlib, lib }:\n\nwlib.wrapModule (\n  {\n    config,\n    # wlib,\n    ...\n  }:\n  {\n    options = {\n      profile = lib.mkOption {\n        type = lib.types.enum [\n          \"fast\"\n          \"quality\"\n        ];\n        default = \"fast\";\n        description = \"Encoding profile to use\";\n      };\n      outputDir = lib.mkOption {\n        type = lib.types.str;\n        default = \"./output\";\n        description = \"Directory for output files\";\n      };\n    };\n\n    config.package = config.pkgs.ffmpeg;\n    config.flags = {\n      \"-preset\" = if config.profile == \"fast\" then \"veryfast\" else \"slow\";\n    };\n    config.env = {\n      FFMPEG_OUTPUT_DIR = config.outputDir;\n    };\n  }\n)\n"
  },
  {
    "path": "users/pinpox.nix",
    "content": "{\n  pkgs,\n  pinpox-keys,\n  wrappers,\n  ...\n}:\nlet\n  # Extend wrappers with custom modules by importing all subdirectories from pinpox-wraps\n  myWrappers = wrappers // {\n    wrapperModules =\n      wrappers.wrapperModules\n      // (builtins.listToAttrs (\n        map (name: {\n          inherit name;\n          value = import (./pinpox-wraps + \"/${name}\") {\n            wlib = wrappers.lib;\n            lib = pkgs.lib;\n          };\n        }) (builtins.attrNames (builtins.readDir ./pinpox-wraps))\n      ));\n  };\n\n  # Instantiate the wrapped ffmpeg with custom options\n  # ffmpeg-wrapped =\n  #   (myWrappers.wrapperModules.ffmpeg.apply {\n  #     inherit pkgs;\n  #     profile = \"quality\"; # or \"fast\"\n  #     outputDir = \"/home/pinpox/videos\"; # customize as needed\n  #   }).wrapper;\n\n  # Example using a built-in wrapper module\n  # mpv-wrapped =\n  #   (myWrappers.wrapperModules.mpv.apply {\n  #     inherit pkgs;\n  #     scripts = [ pkgs.mpvScripts.mpris ];\n  #   }).wrapper;\n\n  # Chromium with extensions configured\n  chromium-wrapped =\n    (myWrappers.wrapperModules.chromium.apply {\n      inherit pkgs;\n      extensions = [\n        # { id = \"clngdbkpkpeebahjckkjfobafhncgmne\"; } # Stylus - adds a paintbrush icon to toolbar\n        { id = \"nngceckbapebfimnlniiiahkandclblb\"; } # Bitwarden\n        { id = \"cjpalhdlnbpafiamejdnhcphjbkeiagm\"; } # Ublock Origin\n        { id = \"gcbommkclmclpchllfjekcdonpmejbdp\"; } # HTTPS everywhere\n        { id = \"mmpokgfcmbkfdeibafoafkiijdbfblfg\"; } # Merge windows\n        { id = \"agldajbhchobfgjcmmigehfdcjbmipne\"; } # Blank Dark New Tab\n      ];\n    }).wrapper;\nin\n{\n\n  # Define a user account. Don't forget to set a password with 'passwd'.\n  users = {\n\n    # For Virtualbox\n    extraGroups.vboxusers.members = [ \"pinpox\" ];\n\n    # Shell is set to zsh for all users as default.\n    defaultUserShell = pkgs.zsh;\n\n    users.pinpox = {\n\n      packages = [\n        # ffmpeg-wrapped\n        # mpv-wrapped\n        # chromium-wrapped\n      ];\n\n      isNormalUser = true;\n      home = \"/home/pinpox\";\n      description = \"Pablo Ovelleiro Corral\";\n      extraGroups = [\n        \"plugdev\"\n        \"docker\"\n        \"wheel\"\n        \"networkmanager\"\n        \"audio\"\n        \"libvirtd\"\n        \"tty\"\n        \"dialout\"\n        \"video\"\n        \"storage-users\"\n      ];\n      shell = pkgs.zsh;\n\n      # Public ssh-keys that are authorized for the user. Fetched from github\n      openssh.authorizedKeys.keyFiles = [ pinpox-keys ];\n    };\n  };\n\n  # Allow to run nix\n  nix.settings.allowed-users = [ \"pinpox\" ];\n}\n"
  },
  {
    "path": "users/root.nix",
    "content": "{\n  pinpox-keys,\n  ...\n}:\n{\n  users.users.root.openssh.authorizedKeys.keyFiles = [ pinpox-keys ];\n\n  # Allow to run nix\n  nix.settings.allowed-users = [ \"root\" ];\n}\n"
  },
  {
    "path": "utils/default.nix",
    "content": "{ pkgs, ... }:\n{\n\n  mkEnvGenerator = envs: rec {\n    files.envfile = { };\n    runtimeInputs = [ pkgs.coreutils ];\n    prompts = pkgs.lib.genAttrs envs (name: {\n      persist = false;\n    });\n\n    # Invalidate on env change\n    validation.script = script;\n\n    script = ''\n      mkdir -p $out\n      cat <<EOT >> $out/envfile\n      ${builtins.concatStringsSep \"\\n\" (map (e: \"${e}='$(cat $prompts/${e})'\") envs)}\n      EOT\n    '';\n  };\n\n  renderMustache =\n    name: template: data:\n    # Render handlebars `template` called `name` by converting `data` to JSON\n    pkgs.stdenv.mkDerivation {\n\n      name = \"${name}\";\n\n      # Disable phases which are not needed. In particular the unpackPhase will\n      # fail, if no src attribute is set\n      nativeBuildInpts = [ pkgs.mustache-go ];\n\n      # Pass Json as file to avoid escaping\n      passAsFile = [ \"jsonData\" ];\n      jsonData = builtins.toJSON data;\n\n      phases = [\n        \"buildPhase\"\n        \"installPhase\"\n      ];\n\n      buildPhase = ''\n        ${pkgs.mustache-go}/bin/mustache $jsonDataPath ${template} > rendered_file\n      '';\n      installPhase = ''\n        cp rendered_file $out\n      '';\n    };\n}\n"
  },
  {
    "path": "vars/per-machine/birne/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHZgywFZxESjcz/U2cMGHCMOUEYtehXYTzPFRLeUE4BI=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/birne/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWBotbaPAYLi7r3Ew57SYVcdKYdbhWgjbVSmjJADvB8Xso"
  },
  {
    "path": "vars/per-machine/birne/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEArIvSEhBWHj5nR6MrJBZ93vKckmQb+UFR4kurJnzkBac=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/birne/minio/.validation-hash",
    "content": "254338cffe84c5c7b887f86fe600d1af1c074de339a69e3fba9ea94154f6c099"
  },
  {
    "path": "vars/per-machine/birne/restic-server/.validation-hash",
    "content": "b40bd3130574d1d8e07160248cb6f48dcde7cf6aa948fda48bbedf8d0fcb6516"
  },
  {
    "path": "vars/per-machine/birne/state-version/version/value",
    "content": "20.03"
  },
  {
    "path": "vars/per-machine/birne/wireguard/publickey/value",
    "content": "jvUOwDHp6tWMhfoRbM+0BkPqQVIPHqR4R7KU11i38Bs=\n"
  },
  {
    "path": "vars/per-machine/birne/wireguard-wg-clan/ipv4/value",
    "content": "192.168.8.4"
  },
  {
    "path": "vars/per-machine/birne/wireguard-wg-clan/publickey/value",
    "content": "fjn10ctGFiw8jP/nm9tez5IyOkYsQkRatuiz1utqMwk=\n"
  },
  {
    "path": "vars/per-machine/birne/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.4"
  },
  {
    "path": "vars/per-machine/birne/yggdrasil/address/value",
    "content": "201:f0e:8ac:ac78:64c7:c317:de6e:7392"
  },
  {
    "path": "vars/per-machine/birne/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAfDx91NTh5s4POghkYxtocgA9KCFz/yIHkPuxJGRoCPQ=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/clementine/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEATmflTMXHndP850khQlJqYikVOuwkPiTnP0egsBnHn/0=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/clementine/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWF6RrYtCUzuneQ1B2uMP9gw9afw691HgQ3GF8GkQDDazp"
  },
  {
    "path": "vars/per-machine/clementine/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA3gdeUUAQUeGTwo6MRnDjObS3qrtqJxsiBmLd6N96g+M=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/clementine/punchcard/.validation-hash",
    "content": "ae90f978bdc90b84f055e02edb2f029ebd880c0fc1d831cc8f5cd1fffbcee743"
  },
  {
    "path": "vars/per-machine/clementine/punchcard2/.validation-hash",
    "content": "ae90f978bdc90b84f055e02edb2f029ebd880c0fc1d831cc8f5cd1fffbcee743"
  },
  {
    "path": "vars/per-machine/clementine/state-version/version/value",
    "content": "26.05"
  },
  {
    "path": "vars/per-machine/clementine/trippy-track/.validation-hash",
    "content": "dc6b3b5bd2268b79a49157f951774ca8f86d44843c09727c334162a76eb089b8"
  },
  {
    "path": "vars/per-machine/clementine/twitch-first/.validation-hash",
    "content": "8cf5f6d31bdd4e28f9bbea5da1eede12fddb6caf180b2368bd9eaed9565b47d7"
  },
  {
    "path": "vars/per-machine/clementine/wireguard-wg-clan/publickey/value",
    "content": "wE8QiS8iYDNezgDcsIcx4vvB9nc7ioU/EOUjJU09JEM=\n"
  },
  {
    "path": "vars/per-machine/clementine/wireguard-wg-clan-ip/ipv4/value",
    "content": "10.100.0.3"
  },
  {
    "path": "vars/per-machine/clementine/wireguard-wg-tunnel/publickey/value",
    "content": "XFe2p24MB+PspGaTXIRNci4qdUjcSIQu8UF4o4TexxQ=\n"
  },
  {
    "path": "vars/per-machine/clementine/wireguard-wg-tunnel-ip/ipv4/value",
    "content": "10.100.0.1"
  },
  {
    "path": "vars/per-machine/clementine/yggdrasil/address/value",
    "content": "201:6d63:e4ff:d74e:7ee1:db69:5962:ac51"
  },
  {
    "path": "vars/per-machine/clementine/yggdrasil/publicKey/value",
    "content": "64a706c00a2c60478925a9a754eba88472ed4ccd911cb6d888e4509afabdc928"
  },
  {
    "path": "vars/per-machine/clementine/zerotier/zerotier-ip/value",
    "content": "fd0c:cb9f:98da:6865:9c99:930c:cb9f:98da"
  },
  {
    "path": "vars/per-machine/clementine/zerotier/zerotier-network-id/value",
    "content": "0ccb9f98da68659c"
  },
  {
    "path": "vars/per-machine/fichte/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAbu8AdNl8NGR6T5ynplprqQgc/6i/IxrYmUq1hjoCVK8=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/fichte/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWHHQPa45WXxnzDHNxecQKNf8wHWgxtGZDC9RFYeqkH74z"
  },
  {
    "path": "vars/per-machine/fichte/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAAYCUUUjHByMhB6oByo7EfpzJEAPq8v425hQXTBECWgs=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/fichte/state-version/version/value",
    "content": "25.11"
  },
  {
    "path": "vars/per-machine/fichte/yggdrasil/address/value",
    "content": "200:a68a:bcfc:f1ca:9c55:aac1:81ea:de25"
  },
  {
    "path": "vars/per-machine/fichte/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEArLqhgYcasdUqnz8KkO0NLnuTHtMgHgoOFP06GzEXgE0=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kartoffel/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA4rXbykPjSf/G1Ya5TqwGDkOPoqDwcm3bCTSJqdUuM/E=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kartoffel/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWR5M9NUjNX97gU1L1LQCpm1Y3iuoqzxtAvp675CznSxvU"
  },
  {
    "path": "vars/per-machine/kartoffel/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA24rRO7Vpsgchmi4aprNVm3UkzIWbBGX7SdwrHqAH3f8=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kartoffel/state-version/version/value",
    "content": "20.03"
  },
  {
    "path": "vars/per-machine/kartoffel/wireguard/publickey/value",
    "content": "HM7c/n99iblET+6myP8fG3L79L8nWWHHwaH+Oz0OPEE=\n"
  },
  {
    "path": "vars/per-machine/kartoffel/wireguard-wg-clan/publickey/value",
    "content": "0MEAQID/ekklJgxAFr7iSWqVNbptJ+TP1y9B/yuC3D8=\n"
  },
  {
    "path": "vars/per-machine/kartoffel/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.3"
  },
  {
    "path": "vars/per-machine/kartoffel/yggdrasil/address/value",
    "content": "200:34c1:9a28:814a:c9da:ddfb:b815:618c"
  },
  {
    "path": "vars/per-machine/kartoffel/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA5Z8y679amxKRAiP1TznLbmb2DZvWh6k+JjpNUy0V4U0=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/caddy/.validation-hash",
    "content": "9aec7258b9d9753deca55cf69724c322103ff822c72fa6614fb3e5c41e45cdf8"
  },
  {
    "path": "vars/per-machine/kfbox/cert-irc/.validation-hash",
    "content": "2aaa4ab651853fc3e5127042322459c42a8746ac6453164f85289e09a4e43e83"
  },
  {
    "path": "vars/per-machine/kfbox/cert-irc/irc.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFDTCCAvWgAwIBAgIUK5hrP+Hfe3HsE4pEf9OXCtP567QwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwODExNDIwMVoXDTI3\nMDIwODExNDIwMVowEjEQMA4GA1UEAwwHaXJjLnBpbjCCAiIwDQYJKoZIhvcNAQEB\nBQADggIPADCCAgoCggIBAK4Nd4TbgTVCjCfp1I38BwIYRsBg+jJQPT/J23YmWi+l\nHGpNDv20U7wVDBwtxpvuUIiBiazT1u5p2lNU4inryO4bxr1htbdw/hZg9W5Ky3zS\nZZVXj+O9ZSaHBfMS/LLjqc8vflaUi35XW7pwLqtARDdkjWpTgqrDW7RrT5kW/vSD\nurS672HuuIUGJmeTH/YQ9lhcFsDytrGaw2sHr1sXhEkti+4mp8+fzTjnn609z41d\nhPqaok1ul+YpUX81wEGtuG2aeKMdeOx/9eNnh0020TzG2jrgQkU2T/qx8DVstuRv\nuArQUzam2MyvjhQ6emjyyOTC1bE6m5AJJaJ8WC6bc9Nuxr1UfmN9ZxUqJKJGolz8\neAAFN4BmbJzYex02CkPviTPi9g86PV2uWBQ5RR1pA4hFQaNIjppy6SdPHT98wvKP\nGxc0JuVk5NxI8FoPqez7cITylbQzgEU52mhgxuBx7OtiFMJIeEZbmLDkBKY+ayZe\nBqbrE4pWQOKJZtybZnrr2hcyWodBrPDd25mszUV65EjvkoxE2xumGf9FKclexS7j\nx3FAnY2NzDi9u4c64UzsuI+IlHYZQYweo5BDnpTeQu6CJQbTjwKK272KbyPys0Pm\nvWma3FBb3sGawHpV9NflGAEXlKVRWTZ3nfl5lba7ustWOGCLxmXL0FcMZnxZRE5L\nAgMBAAGjVjBUMBIGA1UdEQQLMAmCB2lyYy5waW4wHQYDVR0OBBYEFDncIbBM+vGm\n/1HcszLcYKAM4WWiMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA0G\nCSqGSIb3DQEBCwUAA4ICAQAX9i4sjZoMGyOvOvzypK8Z21ecfaP6WQFopa0zMMLF\nHpFvvxy358Hh2eqqVoKgu34JB1aBYLpYnDoarAgkh9jEpL+9RmgFbZgVdBVW5dZz\nCpNYdvnVqOk7Ko8BoQ15Q7Ha5z+h+0yTOJ4L7SogQawMadwgLk7atSEzgldzqJyW\nxWpqGN9kNNsOHOYxWxeTZVYF2Zvt2qnFv86Cl8+WpfyXBhuWEpcgaUvt2xNO8wOy\ntii+T98PTNNj0JWlN2l8qodW2GsbNn9S4xcE6lzffhxBM0ogAvKUwj/5RvQ1Ki8L\n99lKTlP01ubSjygShmgpC/HKbphKncvlss41GoMEhd8E/EeUCF2uhNVuErGkpUXO\nHzAKUfXqBVNkcP8a0jkyTyDbngEKOIv7kukDxaPaMQekLBtawCNwJ+Vv9ZlF4WOl\ngr1ULzuuw7mexppoaOk61d+GRMR9/cXPS84qhXdu/wT+PBlC4udJQAMwahqPCmqv\nvnKLP1Anv/Qkb6CnBNTpKLg/pAnfnJ3tH9yZ2FoeNmyjsomKmwDc6JrG8aNBKoPL\n07pOmya5SZMYgAmrRusiaE8kwdLlXFHAPFP5LnqsQjcgHGAe3ZDk2SPscI0sxZG7\ncB/h22Z5X9ZRqdfEAxidILFfpF/O+9ej1DKm353jRkxRizOzZcrzGn5klr9II3vO\nIg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/cert-irc/irc.fullchain.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFDTCCAvWgAwIBAgIUK5hrP+Hfe3HsE4pEf9OXCtP567QwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwODExNDIwMVoXDTI3\nMDIwODExNDIwMVowEjEQMA4GA1UEAwwHaXJjLnBpbjCCAiIwDQYJKoZIhvcNAQEB\nBQADggIPADCCAgoCggIBAK4Nd4TbgTVCjCfp1I38BwIYRsBg+jJQPT/J23YmWi+l\nHGpNDv20U7wVDBwtxpvuUIiBiazT1u5p2lNU4inryO4bxr1htbdw/hZg9W5Ky3zS\nZZVXj+O9ZSaHBfMS/LLjqc8vflaUi35XW7pwLqtARDdkjWpTgqrDW7RrT5kW/vSD\nurS672HuuIUGJmeTH/YQ9lhcFsDytrGaw2sHr1sXhEkti+4mp8+fzTjnn609z41d\nhPqaok1ul+YpUX81wEGtuG2aeKMdeOx/9eNnh0020TzG2jrgQkU2T/qx8DVstuRv\nuArQUzam2MyvjhQ6emjyyOTC1bE6m5AJJaJ8WC6bc9Nuxr1UfmN9ZxUqJKJGolz8\neAAFN4BmbJzYex02CkPviTPi9g86PV2uWBQ5RR1pA4hFQaNIjppy6SdPHT98wvKP\nGxc0JuVk5NxI8FoPqez7cITylbQzgEU52mhgxuBx7OtiFMJIeEZbmLDkBKY+ayZe\nBqbrE4pWQOKJZtybZnrr2hcyWodBrPDd25mszUV65EjvkoxE2xumGf9FKclexS7j\nx3FAnY2NzDi9u4c64UzsuI+IlHYZQYweo5BDnpTeQu6CJQbTjwKK272KbyPys0Pm\nvWma3FBb3sGawHpV9NflGAEXlKVRWTZ3nfl5lba7ustWOGCLxmXL0FcMZnxZRE5L\nAgMBAAGjVjBUMBIGA1UdEQQLMAmCB2lyYy5waW4wHQYDVR0OBBYEFDncIbBM+vGm\n/1HcszLcYKAM4WWiMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA0G\nCSqGSIb3DQEBCwUAA4ICAQAX9i4sjZoMGyOvOvzypK8Z21ecfaP6WQFopa0zMMLF\nHpFvvxy358Hh2eqqVoKgu34JB1aBYLpYnDoarAgkh9jEpL+9RmgFbZgVdBVW5dZz\nCpNYdvnVqOk7Ko8BoQ15Q7Ha5z+h+0yTOJ4L7SogQawMadwgLk7atSEzgldzqJyW\nxWpqGN9kNNsOHOYxWxeTZVYF2Zvt2qnFv86Cl8+WpfyXBhuWEpcgaUvt2xNO8wOy\ntii+T98PTNNj0JWlN2l8qodW2GsbNn9S4xcE6lzffhxBM0ogAvKUwj/5RvQ1Ki8L\n99lKTlP01ubSjygShmgpC/HKbphKncvlss41GoMEhd8E/EeUCF2uhNVuErGkpUXO\nHzAKUfXqBVNkcP8a0jkyTyDbngEKOIv7kukDxaPaMQekLBtawCNwJ+Vv9ZlF4WOl\ngr1ULzuuw7mexppoaOk61d+GRMR9/cXPS84qhXdu/wT+PBlC4udJQAMwahqPCmqv\nvnKLP1Anv/Qkb6CnBNTpKLg/pAnfnJ3tH9yZ2FoeNmyjsomKmwDc6JrG8aNBKoPL\n07pOmya5SZMYgAmrRusiaE8kwdLlXFHAPFP5LnqsQjcgHGAe3ZDk2SPscI0sxZG7\ncB/h22Z5X9ZRqdfEAxidILFfpF/O+9ej1DKm353jRkxRizOzZcrzGn5klr9II3vO\nIg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2\nMDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8\nX4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ\nvOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg\n/usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k\nY4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe\n+bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ\nafEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t\nMQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE\n6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR\ncblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA\n1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i\nBDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G\nA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up\nSsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9\n4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD\nRf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3\ny83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97\n+OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem\nK947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH//\nlU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V\nST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni\nmO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80\nENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf\nGUo6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/cert-music/.validation-hash",
    "content": "10c6db460ebbeaaf8d9f62a312b2e64391f9699b322c2d56bcb2e006134bc3d0"
  },
  {
    "path": "vars/per-machine/kfbox/cert-music/music.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFETCCAvmgAwIBAgIUESqh8zJyQyHog+1YCftZFwbsbvcwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTI3\nMDIwNzE1MjkyNVowFDESMBAGA1UEAwwJbXVzaWMucGluMIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEA6mt3nLZdhWbnaJO5slC9pudjCPQZgqJstmgWeT2g\nCLOKLGRvE3Hm0TWzbbt3ZYAPNnJZqAs2S3RBqEqynOp+zilxTyh+sBETeKUK1v39\nD3RabYmvZwIHU2EKTMP5CvHwwH2Gl4CeEwzkeZnRTNKYxqNAmD4hg0omLtGByY+8\nSFiCN7Pu7I1Aqvk2yjWA9l5M0EcqSRGg4+c2xRtG7fTYG9xpSpIeP40wc+fvspsZ\nb83K9a5KjOuR8wsNjkL7gtV4ap42PKmt1v9lGWzm6NgCHsPuQEC73NFmSDN70bma\nAwRkU72L6wBzFF8Tkfmtc+SdhBxvZr35P1L000dorQ08vU5rMbBF/eRtP0Dot7tw\nANZO0IgGO4t3xS8gjwhOOu/rkfiJykKKJWmnc0/3XhY6FRTly6VkOz4TRFnnKGFg\n46F1rwXd3qjg0d5IFlcYvq0k8PwUPhaLBVEIKsGJrflhHnG//5qjgpyegXB4qP+z\no0q3bkpeDAt/Udhrx7tr+bewUTITOR+v8HonF1nIy6n3A84kB+LmNzvDgrB6WBwx\nwq3oyS3leheC7OPeulD2i701K6d3rtTARHMw4VKHfwpfT+ASu+oxI0sek0hjVFRK\nyYhym4N2kbU1u2FycQIsR6PZqoPMlRRfcy0k1c/FyoXLA20al6thaHAT7mhhjDmV\nEeUCAwEAAaNYMFYwFAYDVR0RBA0wC4IJbXVzaWMucGluMB0GA1UdDgQWBBQIg/B0\np8RhoIewVQ9BR0HJCh5hsTAfBgNVHSMEGDAWgBSlqD9PP9BhVMURWC82L2CHtkq9\nzTANBgkqhkiG9w0BAQsFAAOCAgEAPQmGD31NWT4n5c0bqTUDy6Xcmn/XNCtbi8e6\nppLy/hPR0ho8hNlh5E1MdrntzipWKdy9gOe84efpGqqvu2gSKoH1lUvfkRBb1T/U\nq7onKi5K4YyzIjlijiI+3hC8AJLk50GWSGy1N0COQkEMlk6YARAJQume/jqIbypJ\nVWhBeiBp5ELnuxQ4z/cP8f4Pw7DmTEJ0G/qEO2TbvfZHS966RN8g05ZY2po9w3XY\ng6BDzQbQCESEBB/LPa2g5kzO8K1CDdQcjNgh1k7YomIivw+EykNjGNORVAjji8aD\nYvqv3pUOay26LhPDrPsTqnf05mQgLLXu43hbfalyMsN/1WgifhcelCo46f5c+pAq\nwgRDv4maTDgdiiutL5RuwdvidKMxjQHzSMYXTEgepwgmMSEQJgnqk5ctIIDDFgED\nbc0BPRR5aQpJLtOeCyuH/pY7Qh6cTziz7vsg4SeoBPLc2DK9SESPa00S6IOxuwud\nN847znj7w3SgEzUjnRX+pCRz0+UtQxp75Z65ZDMwW82C1nJgvs5IgcEvZikGJCLN\nHieSwByNgnl5cniUbdC+RLAAPUWAnUWhJr0qGFHjNwnQYVVbJ1Y1x7pC5cf9P2KR\nHP9sbQ3skBcSigcZnAhQfDKW4/SvgMs4Flk56QwZmw/WlcfqP14F28xB2YJILWhF\n6RwQ+Lw=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/cert-music/music.fullchain.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFETCCAvmgAwIBAgIUESqh8zJyQyHog+1YCftZFwbsbvcwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTI3\nMDIwNzE1MjkyNVowFDESMBAGA1UEAwwJbXVzaWMucGluMIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEA6mt3nLZdhWbnaJO5slC9pudjCPQZgqJstmgWeT2g\nCLOKLGRvE3Hm0TWzbbt3ZYAPNnJZqAs2S3RBqEqynOp+zilxTyh+sBETeKUK1v39\nD3RabYmvZwIHU2EKTMP5CvHwwH2Gl4CeEwzkeZnRTNKYxqNAmD4hg0omLtGByY+8\nSFiCN7Pu7I1Aqvk2yjWA9l5M0EcqSRGg4+c2xRtG7fTYG9xpSpIeP40wc+fvspsZ\nb83K9a5KjOuR8wsNjkL7gtV4ap42PKmt1v9lGWzm6NgCHsPuQEC73NFmSDN70bma\nAwRkU72L6wBzFF8Tkfmtc+SdhBxvZr35P1L000dorQ08vU5rMbBF/eRtP0Dot7tw\nANZO0IgGO4t3xS8gjwhOOu/rkfiJykKKJWmnc0/3XhY6FRTly6VkOz4TRFnnKGFg\n46F1rwXd3qjg0d5IFlcYvq0k8PwUPhaLBVEIKsGJrflhHnG//5qjgpyegXB4qP+z\no0q3bkpeDAt/Udhrx7tr+bewUTITOR+v8HonF1nIy6n3A84kB+LmNzvDgrB6WBwx\nwq3oyS3leheC7OPeulD2i701K6d3rtTARHMw4VKHfwpfT+ASu+oxI0sek0hjVFRK\nyYhym4N2kbU1u2FycQIsR6PZqoPMlRRfcy0k1c/FyoXLA20al6thaHAT7mhhjDmV\nEeUCAwEAAaNYMFYwFAYDVR0RBA0wC4IJbXVzaWMucGluMB0GA1UdDgQWBBQIg/B0\np8RhoIewVQ9BR0HJCh5hsTAfBgNVHSMEGDAWgBSlqD9PP9BhVMURWC82L2CHtkq9\nzTANBgkqhkiG9w0BAQsFAAOCAgEAPQmGD31NWT4n5c0bqTUDy6Xcmn/XNCtbi8e6\nppLy/hPR0ho8hNlh5E1MdrntzipWKdy9gOe84efpGqqvu2gSKoH1lUvfkRBb1T/U\nq7onKi5K4YyzIjlijiI+3hC8AJLk50GWSGy1N0COQkEMlk6YARAJQume/jqIbypJ\nVWhBeiBp5ELnuxQ4z/cP8f4Pw7DmTEJ0G/qEO2TbvfZHS966RN8g05ZY2po9w3XY\ng6BDzQbQCESEBB/LPa2g5kzO8K1CDdQcjNgh1k7YomIivw+EykNjGNORVAjji8aD\nYvqv3pUOay26LhPDrPsTqnf05mQgLLXu43hbfalyMsN/1WgifhcelCo46f5c+pAq\nwgRDv4maTDgdiiutL5RuwdvidKMxjQHzSMYXTEgepwgmMSEQJgnqk5ctIIDDFgED\nbc0BPRR5aQpJLtOeCyuH/pY7Qh6cTziz7vsg4SeoBPLc2DK9SESPa00S6IOxuwud\nN847znj7w3SgEzUjnRX+pCRz0+UtQxp75Z65ZDMwW82C1nJgvs5IgcEvZikGJCLN\nHieSwByNgnl5cniUbdC+RLAAPUWAnUWhJr0qGFHjNwnQYVVbJ1Y1x7pC5cf9P2KR\nHP9sbQ3skBcSigcZnAhQfDKW4/SvgMs4Flk56QwZmw/WlcfqP14F28xB2YJILWhF\n6RwQ+Lw=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2\nMDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8\nX4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ\nvOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg\n/usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k\nY4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe\n+bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ\nafEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t\nMQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE\n6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR\ncblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA\n1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i\nBDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G\nA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up\nSsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9\n4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD\nRf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3\ny83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97\n+OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem\nK947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH//\nlU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V\nST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni\nmO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80\nENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf\nGUo6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/data-mesher-host-key/public_key/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA9lBef9cvOwKBdAkIhPX60ggufLXkZLMqCsyugWSKoaA=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAMiXKPkr1NZ3JhrpxMKq2TVmEJ9lCAUYgWuAESqqZeUw=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWDC7yXRWZEFBoeWaZaKZEz6mqyv5ZW1ejN3ciCkzyMi19"
  },
  {
    "path": "vars/per-machine/kfbox/dex/.validation-hash",
    "content": "d2b8da6120ff1bcbaec6e423e507d423c13a4e2c55cee7b8f0ddbe33363312eb"
  },
  {
    "path": "vars/per-machine/kfbox/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAMu8cVts+1YkOvtJfpsI0OfunwPF3j1l8/dMqQxDCkok=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kfbox/go-karma-bot/.validation-hash",
    "content": "24d59f70cba7b4ee0c772354f0c041e925448e52431083a8bf37cda6b48b11a0"
  },
  {
    "path": "vars/per-machine/kfbox/hedgedoc/.validation-hash",
    "content": "96ba40a19e0a2b2696bb565184f28251d63c35f31f86ed8f85e526f660aa79ae"
  },
  {
    "path": "vars/per-machine/kfbox/jitsi-presence/.validation-hash",
    "content": "3ce0ea6332ad94bb81f83a03ee7c89514f4e6b218a43956b03ba4f48e4383432"
  },
  {
    "path": "vars/per-machine/kfbox/restic-exporter/.validation-hash",
    "content": "2281613666175c6d6f41a42c0c5136e29409c86f7821fa5e73be344cf9fd18d7"
  },
  {
    "path": "vars/per-machine/kfbox/state-version/version/value",
    "content": "22.05"
  },
  {
    "path": "vars/per-machine/kfbox/vikunja/.validation-hash",
    "content": "d46d7942b025ed4866f10969d73fb73fc90b2e3d7e30752d3c45d8a0142ecca5"
  },
  {
    "path": "vars/per-machine/kfbox/wireguard/publickey/value",
    "content": "N5rFljGz1BMiF3hxsRChK9VvZxmQchZFcQrIkECVEnU=\n"
  },
  {
    "path": "vars/per-machine/kfbox/wireguard-wg-clan/ipv4/value",
    "content": "192.168.8.5"
  },
  {
    "path": "vars/per-machine/kfbox/wireguard-wg-clan/publickey/value",
    "content": "p4aaNlsIcPQi+z1cMDj/OX8EK6qwDT2KdzHYul8fSno=\n"
  },
  {
    "path": "vars/per-machine/kfbox/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.5"
  },
  {
    "path": "vars/per-machine/kfbox/wireguard-wg-star/ipv6/value",
    "content": "fda1:05c8:00::c6e7:1e61:22a7:de2e"
  },
  {
    "path": "vars/per-machine/kfbox/wireguard-wg-star/publickey/value",
    "content": "2UBwODAKKGFL+xBPBBZ2Xl5U9hnFL+InBlicP77lXls=\n"
  },
  {
    "path": "vars/per-machine/kfbox/yggdrasil/address/value",
    "content": "200:2065:4635:7d71:2877:fc16:1fae:197d"
  },
  {
    "path": "vars/per-machine/kfbox/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA781c5UFHa8QB9PAo80Em7BHnbf042/37Dvg6DVlL3sE=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kiwi/cert-claw/.validation-hash",
    "content": "976efc6b3915b2db6922e211aba08e18d24c99902262244b902825a4d1bfbcf8"
  },
  {
    "path": "vars/per-machine/kiwi/cert-claw/claw.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUKdsmT2t/pcjdIfg8DCgBVQaiZMAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIxMzE2NDQ1N1oXDTI3\nMDIxMzE2NDQ1N1owEzERMA8GA1UEAwwIY2xhdy5waW4wggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQCxCO2H2mz0XuAd2fSvg0EwWzE6Zr8tcFBT5q9xqq0l\nNqn0xVqSjEXnnxrCc7KQdKikLJNFv0oXJ9wQ7xsmJVhgZ9AxYjZ967lLcLs5Oa5Q\nmX+1Ew1gx12qHNXWzateH/7T58EtLB0ZfGIIz1pq1DMnYYsfkh5pWDgxY3a5mujy\nRM/LbCTy8r4Bn30NJvSIPinoehjCiPfz4dGiNwOVVZ7b1ANS/4Bo7+/Y+VLxyzzF\ng3KtwegKXMDoTrh6Kd6JMnzk65GzCVs1a6L78mSSJnaQG3enVv/2L21yHwrQ5p9q\nQTsJ/rE92wVY7B0slCDDVTTKsLcEw956c4xjqmXyY7xYi7kTOzh3wbLs02Hgf503\nCAHnxaC54vSXDG7N7ba1g/MdQN84kbRUQveKz915ZsNKaOxuizRMyZdmbaU/eSjx\nLciXkXNx09FIdXGDUj1WCsTxQzHqXW76lnpDyUr9Dq1HXMIxk9kjLZkasm5nkHYw\nhGY60GfIiDJpmiZj0YOArAG5F3k05ZfcEhO11jqi9UMKqQFr1U/R8fRLFwj3PkNc\nFvm8krD6yvYnKDcEiVSkgWTnOLCCL1I+ADIiMQ/sbipxDOLLUb+HvX0DeCG9Jwnu\n1qCTYdue89HyuOg/CUZciI/MLuyZICgJyNK4eNPhxzjKM3pIfjY1gW+SnMxoXbKz\nMQIDAQABo1cwVTATBgNVHREEDDAKgghjbGF3LnBpbjAdBgNVHQ4EFgQUKOerRsZK\nAxHTrldLiNxg6PLheUowHwYDVR0jBBgwFoAUpag/Tz/QYVTFEVgvNi9gh7ZKvc0w\nDQYJKoZIhvcNAQELBQADggIBALpI/yLuEs7udHJ1IWspbdS/FOgUPVqf6bIStgMn\nUkEYcrRORWB8HSSDgvabZ/no7B/uyF9xdlyn62L+J04LYoZI28h2FPiq+JvwPQ6P\n3d32dsa7u6uBYERT55rVbCj5zGyG7k+hmHynjv81+EQIYkZEB1yYLLvmXo+RoEft\ntRQHwD6/ATXA9UaOSokleiGRnw58vRjo60AgOyoC9zQKEq4jMwufGFZuugNmzAnU\nyit9cZkqOf1/szPDeh7bJ9RrhFAx2vUqisTznHt7xG40THb46H+QYeu/mE11zjYi\nnBmJry4vNp6JoDOpjKjuevoigQxqq/7a/YI97WJ4QgcVS0o+UR7Iw50cW1CQyPKM\n/8/1NnnMady5RT7DP/qe4DlF7I4f1ei0s9Ma51sFUPhym+YnRcbAkgxG5tLSBApR\noE2Rh6hqesgkycMm4R/ipsbmHNlYPOLKeAPhafYwL7etuNsjvdwqA4GaPG0iKC2e\nD26Is1RCtmrHuaAk0jXRixZQSCKfQ+lJx/y8Pezu/VhVGFsJEwRdGDryepWKLi1l\nzSI3ZImORpXQgMbYOptXwkBh6c6w4NbML6h+C+OiOs0S4zG/Evo3sBaKlckpDAFv\n8ty2oB/9ZMkhr1V0M3VndG+ErogKaWLam7KJrHI0jEVKDn6L4uPPyM2B4vEoA7xI\nELfp\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/kiwi/cert-claw/claw.fullchain.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUKdsmT2t/pcjdIfg8DCgBVQaiZMAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIxMzE2NDQ1N1oXDTI3\nMDIxMzE2NDQ1N1owEzERMA8GA1UEAwwIY2xhdy5waW4wggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQCxCO2H2mz0XuAd2fSvg0EwWzE6Zr8tcFBT5q9xqq0l\nNqn0xVqSjEXnnxrCc7KQdKikLJNFv0oXJ9wQ7xsmJVhgZ9AxYjZ967lLcLs5Oa5Q\nmX+1Ew1gx12qHNXWzateH/7T58EtLB0ZfGIIz1pq1DMnYYsfkh5pWDgxY3a5mujy\nRM/LbCTy8r4Bn30NJvSIPinoehjCiPfz4dGiNwOVVZ7b1ANS/4Bo7+/Y+VLxyzzF\ng3KtwegKXMDoTrh6Kd6JMnzk65GzCVs1a6L78mSSJnaQG3enVv/2L21yHwrQ5p9q\nQTsJ/rE92wVY7B0slCDDVTTKsLcEw956c4xjqmXyY7xYi7kTOzh3wbLs02Hgf503\nCAHnxaC54vSXDG7N7ba1g/MdQN84kbRUQveKz915ZsNKaOxuizRMyZdmbaU/eSjx\nLciXkXNx09FIdXGDUj1WCsTxQzHqXW76lnpDyUr9Dq1HXMIxk9kjLZkasm5nkHYw\nhGY60GfIiDJpmiZj0YOArAG5F3k05ZfcEhO11jqi9UMKqQFr1U/R8fRLFwj3PkNc\nFvm8krD6yvYnKDcEiVSkgWTnOLCCL1I+ADIiMQ/sbipxDOLLUb+HvX0DeCG9Jwnu\n1qCTYdue89HyuOg/CUZciI/MLuyZICgJyNK4eNPhxzjKM3pIfjY1gW+SnMxoXbKz\nMQIDAQABo1cwVTATBgNVHREEDDAKgghjbGF3LnBpbjAdBgNVHQ4EFgQUKOerRsZK\nAxHTrldLiNxg6PLheUowHwYDVR0jBBgwFoAUpag/Tz/QYVTFEVgvNi9gh7ZKvc0w\nDQYJKoZIhvcNAQELBQADggIBALpI/yLuEs7udHJ1IWspbdS/FOgUPVqf6bIStgMn\nUkEYcrRORWB8HSSDgvabZ/no7B/uyF9xdlyn62L+J04LYoZI28h2FPiq+JvwPQ6P\n3d32dsa7u6uBYERT55rVbCj5zGyG7k+hmHynjv81+EQIYkZEB1yYLLvmXo+RoEft\ntRQHwD6/ATXA9UaOSokleiGRnw58vRjo60AgOyoC9zQKEq4jMwufGFZuugNmzAnU\nyit9cZkqOf1/szPDeh7bJ9RrhFAx2vUqisTznHt7xG40THb46H+QYeu/mE11zjYi\nnBmJry4vNp6JoDOpjKjuevoigQxqq/7a/YI97WJ4QgcVS0o+UR7Iw50cW1CQyPKM\n/8/1NnnMady5RT7DP/qe4DlF7I4f1ei0s9Ma51sFUPhym+YnRcbAkgxG5tLSBApR\noE2Rh6hqesgkycMm4R/ipsbmHNlYPOLKeAPhafYwL7etuNsjvdwqA4GaPG0iKC2e\nD26Is1RCtmrHuaAk0jXRixZQSCKfQ+lJx/y8Pezu/VhVGFsJEwRdGDryepWKLi1l\nzSI3ZImORpXQgMbYOptXwkBh6c6w4NbML6h+C+OiOs0S4zG/Evo3sBaKlckpDAFv\n8ty2oB/9ZMkhr1V0M3VndG+ErogKaWLam7KJrHI0jEVKDn6L4uPPyM2B4vEoA7xI\nELfp\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2\nMDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8\nX4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ\nvOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg\n/usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k\nY4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe\n+bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ\nafEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t\nMQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE\n6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR\ncblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA\n1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i\nBDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G\nA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up\nSsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9\n4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD\nRf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3\ny83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97\n+OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem\nK947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH//\nlU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V\nST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni\nmO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80\nENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf\nGUo6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/kiwi/data-mesher-host-key/public_key/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAsRoSObpLWJCKPVxUeNfOMMl2p+1eLe7NDF3cJaUedY0=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kiwi/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAp9vEAaIR35W84gOrNY+SiPV7yssU315KwMQpIANdG+k=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kiwi/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWM7cch7yvMeNbxUEwjBM91EKNxfeWuhPto4kT6nK7UzZE"
  },
  {
    "path": "vars/per-machine/kiwi/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAjoIxttD5fONZ20nAUWCpwSzcyxUAzz2fk8KQ+ll+CrE=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/kiwi/openclaw/.validation-hash",
    "content": "98598a2e147fff6f46d9ca8512ddd72c5f76c35d708a41195edb97437f1535f4"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow/.validation-hash",
    "content": "b08299c38b79b4a8195a4fe2e0c204c793278a003f4210a0dc778304366c3666"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-email/.validation-hash",
    "content": "bbeae7cc7a6c2e9e4c900505b7678737dcd4f5b02d6489439298e7bb5b6d0902"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-eversports/.validation-hash",
    "content": "5e33339ca7ab2f9fa413ff7a129654b234f7b80e2a5f523ce75535b5e0ca6116"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-geninf/pubkey/value",
    "content": "43359cc017f0cea6a8c5082abbb51020e00ad0f77eba0540d045ca8be90d23ff\n"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-geninf-user/pubkey/value",
    "content": "152553c8e87ca942bc8bae1b4af709002368bfa1186603759ab261c7d8a0df97\n"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-nextcloud/.validation-hash",
    "content": "d877886ea3c25f02f8005f8e0c336a27e0b756ef09c1075707847b009070b3ec"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-nextcloud-work/.validation-hash",
    "content": "77236f357fa2b9a3e9b633553abc4616a787272f3752388439197409aaf9dea7"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-nostr-bot/nostr-public-key/value",
    "content": "44e9c1fba70d51e8cd6d0b4bf99315c686cac547feaab5c2539a4f843c55e046"
  },
  {
    "path": "vars/per-machine/kiwi/opencrow-nostr-user/nostr-public-key/value",
    "content": "npub1evf9p0304tplxqdja8m2hjr9r77hmetz87nuexuc6fs07fnvapuqg5ak9j\n"
  },
  {
    "path": "vars/per-machine/kiwi/state-version/version/value",
    "content": "20.03"
  },
  {
    "path": "vars/per-machine/kiwi/tor_tor/hostname/value",
    "content": "7rf5fpxif4ufzo23imdirhfqoaqcthtixexjippwvo72yiejeqfrh3yd.onion\n"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-clan/ipv4/value",
    "content": "192.168.8.6"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-clan/publickey/value",
    "content": "rrN1oYQhcmJgjha/DwBtWRRDCXE1RwTzG2iAIh5mX0s=\n"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.6"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-star/ipv6/value",
    "content": "fda1:05c8:00::522c:5caa:22b4:f678"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-star/publickey/value",
    "content": "XGwUmveHixXWlkA8pUlnkj9MHMM97y6juXkOxsqvfU0=\n"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-tunnel/publickey/value",
    "content": "MueYIZYNfGm6j1KNpdvwy4RpFML/8cArh7Gfn892BTw=\n"
  },
  {
    "path": "vars/per-machine/kiwi/wireguard-wg-tunnel-ip/ipv4/value",
    "content": "10.100.0.2"
  },
  {
    "path": "vars/per-machine/kiwi/yggdrasil/address/value",
    "content": "200:c9ba:df22:4f62:4973:ed2d:208b:232b"
  },
  {
    "path": "vars/per-machine/kiwi/yggdrasil/publicKey/value",
    "content": "9b22906ed84edb4609696fba6e6a511e9d63df45308ebe43956e2ba196eb9266"
  },
  {
    "path": "vars/per-machine/kiwi/zerotier/zerotier-ip/value",
    "content": "fd0c:cb9f:98da:6865:9c99:9365:d8fa:9922"
  },
  {
    "path": "vars/per-machine/limette/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAtY2UUMDDL66GkKeZ40Pm/l4FoZE2zsdMZ+QcQLmZNsU=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/limette/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWN35B8zZhA9t1anJuATwjeruzDbT6TJw9duq8EWHQBHHe"
  },
  {
    "path": "vars/per-machine/limette/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAN3r9Tg32a58F+WnbnjxdAeSgXWvADa0+wcH/nYu4L2w=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/limette/state-version/version/value",
    "content": "20.03"
  },
  {
    "path": "vars/per-machine/limette/wireguard/publickey/value",
    "content": "TCDZ1RBxqVBEp699/3UUBm9icI9UERswFlw4kJWd/jE=\n"
  },
  {
    "path": "vars/per-machine/limette/wireguard-wg-clan/publickey/value",
    "content": "8DaOeJAmGyrRR2o9W5lmik+VqaRO958XXu+eRHy8yHQ=\n"
  },
  {
    "path": "vars/per-machine/limette/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.8"
  },
  {
    "path": "vars/per-machine/limette/yggdrasil/address/value",
    "content": "201:242d:a12e:958:f77e:4c95:eef7:3975"
  },
  {
    "path": "vars/per-machine/limette/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAdvSXtH2pwiBs2oRCMaKLLFPQPXBG/TZF6jhTbbuwByk=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/porree/alertmanager-ntfy/.validation-hash",
    "content": "7c21c80d4620962abd3d601c5b4302caa54dc10981de5982c6821d853b67a949"
  },
  {
    "path": "vars/per-machine/porree/caddy/.validation-hash",
    "content": "39b8a0ffd9699b029fa80287910104a5a646388572aeeff18edd9aff32c566e0"
  },
  {
    "path": "vars/per-machine/porree/caddy-basicauth/.validation-hash",
    "content": "e652f45df5a63f6f33855447f0b9aea66e664fc29399567bd01d1efd85a55cec"
  },
  {
    "path": "vars/per-machine/porree/cert-prometheus/.validation-hash",
    "content": "1cf8c129b76a777ce93c9d791880170480804991bbce933ed118df57236741ea"
  },
  {
    "path": "vars/per-machine/porree/cert-prometheus/prometheus.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFGzCCAwOgAwIBAgIUB/eKfajvUEO3Fg6Pwo8+A3HuZo0wDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIyMDQ1NFoXDTI3\nMDQwODIyMDQ1NFowGTEXMBUGA1UEAwwOcHJvbWV0aGV1cy5waW4wggIiMA0GCSqG\nSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0p9V+S1YRa+KVJD14u53EqzsSYdN5xvQL\nAexgIMyO6JdWBnPHD+GuSKnHvx5OzYH9A0bXdmrQWW694JVwvzn0tj00Qro3YDnT\nj+WH9vKZCJW3Sp6BzPtCh+trzjl4sWA+fMhrdJKoO2F2vZN/1eOTGpDHYHjXKsaI\nHY1pJuVehFd6EDGTIzo6obykp6im3M15AlDWAU2aLZgBvKtOimRCZyFtVTs6vmF1\nW+ALGV+uNOJwc75UF69FFCJB3AWa+xZDxCfN8xDbDs0KshXlwxh1UWQcATEcNmx5\nGYgkEbUxPqj0S4IMskygPNTCNZvMgjVOiLTKmgxMcXb6YT5UtktZb1smmjd/iSvs\nPyLhrK3qs9pi/i9kiZrD5N17OceHUlAm+7PmlKybKGwndZUVqStrN+eHr8zkiXVE\n12X5HPbxfISjkkXsFSJ1oSdSBxeeYCE5FKnTSIHLqVEuBDFoDMXmDg6uxfwI6jNF\nyVvsjTtiZAfYPbpw325JwPfMZBRVmvKuUya77vWK5DCn+7cAVuv9NsP2X5+ENNxi\nImwg3NMh/NsuqdydQMBO1ubuGj9CFHb40wCrjVno0sFhjFUcx9DwxAowlSALFkFq\ngofW5OpMXSM1iqntI/p0k4NcND1FJYd0yZee5CUxkhk6quZpDMVhvqoBDO5dEwss\nQc/WV/YbaQIDAQABo10wWzAZBgNVHREEEjAQgg5wcm9tZXRoZXVzLnBpbjAdBgNV\nHQ4EFgQUxI3Q4zH8y+TGlpr5ngdJYTUYRKIwHwYDVR0jBBgwFoAUpag/Tz/QYVTF\nEVgvNi9gh7ZKvc0wDQYJKoZIhvcNAQELBQADggIBAIYe7fap9MDJHyPVPyGwoG/0\nXYjUMM1qB8pbVHeKhtaTFiiD3oeyOrI4TLGgkqNoIrcQ7m4xA50GE/DRCQpxQvsJ\nhQ0BBtCxu4JAW76W2q1auEH26sGSh5AMjcwkccbvhO8H0zwvuSSJEQvsCq6x2e9q\nvPuxqKJC/8Kvz3fJdQ0u4B/wDx1nSB4G4MTrCaOMBkyx8PPNoFHOWKNEPiBxKqHQ\njbhs6+ggZ0+o+QvbivWJEgyyREsHuXwp7L5s0sqaq2hrQzmgkn/qiJaBV7sjVZU1\nKblqImo89ZJL/4sZRi60TCh6GKEgxyMHGKdgh0/0iFfoRw4YKUz6IPlGSNc0j6zD\np0DD+9YFgjue1Vre1tz5zaZMlRz1S38JOP3BF37BfIB9X8IKPwcSe4f7m5D9Die9\nfjHn8XJoURWPOMjJ1C6hn9H3a8SHNEbG0bT1x6IXwPvrdXTz2rXJ9JhYGi3qwMrG\n/h7tW5BdVrZ33ikFI/8ceM2+6we8NdaYw6WPko8vrzo9HzuDoxDwesy3Lj3rbk4a\nMnKrUB9H2dlniXcDa9gmPcFUVmldr4/Diq4EYi7N017haFtKTsSmzZLJ1+ZR8lX/\nAcKAufd9xxh6PBUIisCBCK/wm/R+aI89nsaCnaE+1GQf1XrPbiVXMNjDVn3KgcbQ\nZrte4cCk4+TXsGgNnPXb\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/porree/cert-prometheus/prometheus.fullchain.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFGzCCAwOgAwIBAgIUB/eKfajvUEO3Fg6Pwo8+A3HuZo0wDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIyMDQ1NFoXDTI3\nMDQwODIyMDQ1NFowGTEXMBUGA1UEAwwOcHJvbWV0aGV1cy5waW4wggIiMA0GCSqG\nSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0p9V+S1YRa+KVJD14u53EqzsSYdN5xvQL\nAexgIMyO6JdWBnPHD+GuSKnHvx5OzYH9A0bXdmrQWW694JVwvzn0tj00Qro3YDnT\nj+WH9vKZCJW3Sp6BzPtCh+trzjl4sWA+fMhrdJKoO2F2vZN/1eOTGpDHYHjXKsaI\nHY1pJuVehFd6EDGTIzo6obykp6im3M15AlDWAU2aLZgBvKtOimRCZyFtVTs6vmF1\nW+ALGV+uNOJwc75UF69FFCJB3AWa+xZDxCfN8xDbDs0KshXlwxh1UWQcATEcNmx5\nGYgkEbUxPqj0S4IMskygPNTCNZvMgjVOiLTKmgxMcXb6YT5UtktZb1smmjd/iSvs\nPyLhrK3qs9pi/i9kiZrD5N17OceHUlAm+7PmlKybKGwndZUVqStrN+eHr8zkiXVE\n12X5HPbxfISjkkXsFSJ1oSdSBxeeYCE5FKnTSIHLqVEuBDFoDMXmDg6uxfwI6jNF\nyVvsjTtiZAfYPbpw325JwPfMZBRVmvKuUya77vWK5DCn+7cAVuv9NsP2X5+ENNxi\nImwg3NMh/NsuqdydQMBO1ubuGj9CFHb40wCrjVno0sFhjFUcx9DwxAowlSALFkFq\ngofW5OpMXSM1iqntI/p0k4NcND1FJYd0yZee5CUxkhk6quZpDMVhvqoBDO5dEwss\nQc/WV/YbaQIDAQABo10wWzAZBgNVHREEEjAQgg5wcm9tZXRoZXVzLnBpbjAdBgNV\nHQ4EFgQUxI3Q4zH8y+TGlpr5ngdJYTUYRKIwHwYDVR0jBBgwFoAUpag/Tz/QYVTF\nEVgvNi9gh7ZKvc0wDQYJKoZIhvcNAQELBQADggIBAIYe7fap9MDJHyPVPyGwoG/0\nXYjUMM1qB8pbVHeKhtaTFiiD3oeyOrI4TLGgkqNoIrcQ7m4xA50GE/DRCQpxQvsJ\nhQ0BBtCxu4JAW76W2q1auEH26sGSh5AMjcwkccbvhO8H0zwvuSSJEQvsCq6x2e9q\nvPuxqKJC/8Kvz3fJdQ0u4B/wDx1nSB4G4MTrCaOMBkyx8PPNoFHOWKNEPiBxKqHQ\njbhs6+ggZ0+o+QvbivWJEgyyREsHuXwp7L5s0sqaq2hrQzmgkn/qiJaBV7sjVZU1\nKblqImo89ZJL/4sZRi60TCh6GKEgxyMHGKdgh0/0iFfoRw4YKUz6IPlGSNc0j6zD\np0DD+9YFgjue1Vre1tz5zaZMlRz1S38JOP3BF37BfIB9X8IKPwcSe4f7m5D9Die9\nfjHn8XJoURWPOMjJ1C6hn9H3a8SHNEbG0bT1x6IXwPvrdXTz2rXJ9JhYGi3qwMrG\n/h7tW5BdVrZ33ikFI/8ceM2+6we8NdaYw6WPko8vrzo9HzuDoxDwesy3Lj3rbk4a\nMnKrUB9H2dlniXcDa9gmPcFUVmldr4/Diq4EYi7N017haFtKTsSmzZLJ1+ZR8lX/\nAcKAufd9xxh6PBUIisCBCK/wm/R+aI89nsaCnaE+1GQf1XrPbiVXMNjDVn3KgcbQ\nZrte4cCk4+TXsGgNnPXb\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2\nMDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8\nX4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ\nvOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg\n/usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k\nY4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe\n+bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ\nafEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t\nMQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE\n6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR\ncblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA\n1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i\nBDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G\nA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up\nSsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9\n4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD\nRf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3\ny83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97\n+OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem\nK947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH//\nlU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V\nST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni\nmO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80\nENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf\nGUo6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/porree/cert-status/.validation-hash",
    "content": "a3ebf639b19ed4de7953512fa29f44a446f14f1d566791c1979e356c212dcb75"
  },
  {
    "path": "vars/per-machine/porree/cert-status/status.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFEzCCAvugAwIBAgIUMmWSeM2DttIlig7T72VOoLtLhikwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIxMDcyN1oXDTI3\nMDQwODIxMDcyN1owFTETMBEGA1UEAwwKc3RhdHVzLnBpbjCCAiIwDQYJKoZIhvcN\nAQEBBQADggIPADCCAgoCggIBAMdS1RImh07qq+BfzIGkWzYmXj6k1lNZouHX4JM8\nKbzSNL30I8DJg7mqQ+3CyGD6Ze5B1IebGuHib/EKKMXOEPeMEuklh5uUJVUMz6LS\nVfJCDAbe9UEriqjrusOxqdio3aw5SlsEPOhIfgwJpXmdR72b5/ag5L9PVggH97X0\nK30WW48+ACBwI4rJs4uJZkQhLakRBKLZOwrpt4TjbYuKqOH15jiwXIWgxYFoC2fq\nE3pgickNAHc823BXENmGjDCOlNfiXmbEtv/QLOooJF1vnP+JxAb1EjPgheW4YkvD\nznfxSnHJCmENKkX1uzvMsGmdGnOW+8PgGlzmFSUwy9HeY0g/eC+LXiOMYQJuUb/l\nG17sfXrA2K3xxrtna70ktX+Vnj1O6k3WBIIeos7O48PDo/POtVyyOtjCPm4a+qsI\ngqeIES/0Fbul8z2jVdZ6sQVbxSt0GEAHctjl937y5F7OdWKBLJRYjvyvIVYpeOeX\nYN10UJ5hU0hamBoZFEtMdzh6F+aS+vvAQoo1T6gS7SiLQ4oDNYhDbWYhmTTBPE4r\nBgV0EOsMLJR8eUN420Zjoli9UTXwjiXr+FNIEKcC6H2EZRWM1Y02Y87PQSWhGfud\nRvvCh8y7qIiewuATZkmNAtLqyy24o4eGL8ib6ys+j7m7/LhIOk00Ftil3bxTbGyQ\nC5GVAgMBAAGjWTBXMBUGA1UdEQQOMAyCCnN0YXR1cy5waW4wHQYDVR0OBBYEFE73\nkHDe6fgKvK2maqpgl/rwmEHKMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2\nSr3NMA0GCSqGSIb3DQEBCwUAA4ICAQCHBNk2OGVJTKUjslB+/vSz2KoQzYVA6HKt\n3ElDJBCig6r8chgyi8LeXg+dsX3CzLcUKWLidlxpGc+BY8P14mlaa3/16rL/0mTw\n1WmDJdcciP17IHN2P1xNk8xSHIN2zTrrvRe1Nmkx0DQfYOnKXTIt4w4r0XyFs3SH\n7k0Kkk669isch8eSLkt78dInC9jLYXg4sPNqV9tF9mh5X7dufUdchBA6IYJYpMtB\nzXmYD7u4+A7hiUnmsAnxjeEVa8tirGTQX250EBR9KKCEeASv6gJ98AjoP3kWyaHK\nOaqt+OHkVCmvExmAib0Xt3+W52usSj0t52rJq9wZ1bWWaQWdUCHGkZBL59d1K8I5\nzmAVrPOoiHNKaBARK466hOsayLH76huVG7H4TbtVKKw8zyEnbyvaACtY09RVi+Pm\nGWi5f/WwMMbYoI6hs7EtjsuWAj317vEwuTahaMpr1XaTcDlOoHOUhqkCJxhTTbf1\n8v9T6h6o/GMiWisSVFL5MuVMI8yjvs39YeQOTVelO4SrNKLOvTAVw4Q873P6guT+\nq7RtHgUcMOJPxw0+pICbsI2tIbLbOq+oAzqmXIFv7aadovrKjq66zF5wv1sLjiWU\nJ3dJq87IDdgEfb8WsXOu37YW2SDVc4m6mdqmytYr6tU7pXz7FQACccR60F0sKS6J\nINwSwduKhg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/porree/cert-status/status.fullchain.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFEzCCAvugAwIBAgIUMmWSeM2DttIlig7T72VOoLtLhikwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDQwODIxMDcyN1oXDTI3\nMDQwODIxMDcyN1owFTETMBEGA1UEAwwKc3RhdHVzLnBpbjCCAiIwDQYJKoZIhvcN\nAQEBBQADggIPADCCAgoCggIBAMdS1RImh07qq+BfzIGkWzYmXj6k1lNZouHX4JM8\nKbzSNL30I8DJg7mqQ+3CyGD6Ze5B1IebGuHib/EKKMXOEPeMEuklh5uUJVUMz6LS\nVfJCDAbe9UEriqjrusOxqdio3aw5SlsEPOhIfgwJpXmdR72b5/ag5L9PVggH97X0\nK30WW48+ACBwI4rJs4uJZkQhLakRBKLZOwrpt4TjbYuKqOH15jiwXIWgxYFoC2fq\nE3pgickNAHc823BXENmGjDCOlNfiXmbEtv/QLOooJF1vnP+JxAb1EjPgheW4YkvD\nznfxSnHJCmENKkX1uzvMsGmdGnOW+8PgGlzmFSUwy9HeY0g/eC+LXiOMYQJuUb/l\nG17sfXrA2K3xxrtna70ktX+Vnj1O6k3WBIIeos7O48PDo/POtVyyOtjCPm4a+qsI\ngqeIES/0Fbul8z2jVdZ6sQVbxSt0GEAHctjl937y5F7OdWKBLJRYjvyvIVYpeOeX\nYN10UJ5hU0hamBoZFEtMdzh6F+aS+vvAQoo1T6gS7SiLQ4oDNYhDbWYhmTTBPE4r\nBgV0EOsMLJR8eUN420Zjoli9UTXwjiXr+FNIEKcC6H2EZRWM1Y02Y87PQSWhGfud\nRvvCh8y7qIiewuATZkmNAtLqyy24o4eGL8ib6ys+j7m7/LhIOk00Ftil3bxTbGyQ\nC5GVAgMBAAGjWTBXMBUGA1UdEQQOMAyCCnN0YXR1cy5waW4wHQYDVR0OBBYEFE73\nkHDe6fgKvK2maqpgl/rwmEHKMB8GA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2\nSr3NMA0GCSqGSIb3DQEBCwUAA4ICAQCHBNk2OGVJTKUjslB+/vSz2KoQzYVA6HKt\n3ElDJBCig6r8chgyi8LeXg+dsX3CzLcUKWLidlxpGc+BY8P14mlaa3/16rL/0mTw\n1WmDJdcciP17IHN2P1xNk8xSHIN2zTrrvRe1Nmkx0DQfYOnKXTIt4w4r0XyFs3SH\n7k0Kkk669isch8eSLkt78dInC9jLYXg4sPNqV9tF9mh5X7dufUdchBA6IYJYpMtB\nzXmYD7u4+A7hiUnmsAnxjeEVa8tirGTQX250EBR9KKCEeASv6gJ98AjoP3kWyaHK\nOaqt+OHkVCmvExmAib0Xt3+W52usSj0t52rJq9wZ1bWWaQWdUCHGkZBL59d1K8I5\nzmAVrPOoiHNKaBARK466hOsayLH76huVG7H4TbtVKKw8zyEnbyvaACtY09RVi+Pm\nGWi5f/WwMMbYoI6hs7EtjsuWAj317vEwuTahaMpr1XaTcDlOoHOUhqkCJxhTTbf1\n8v9T6h6o/GMiWisSVFL5MuVMI8yjvs39YeQOTVelO4SrNKLOvTAVw4Q873P6guT+\nq7RtHgUcMOJPxw0+pICbsI2tIbLbOq+oAzqmXIFv7aadovrKjq66zF5wv1sLjiWU\nJ3dJq87IDdgEfb8WsXOu37YW2SDVc4m6mdqmytYr6tU7pXz7FQACccR60F0sKS6J\nINwSwduKhg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2\nMDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8\nX4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ\nvOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg\n/usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k\nY4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe\n+bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ\nafEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t\nMQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE\n6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR\ncblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA\n1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i\nBDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G\nA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up\nSsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9\n4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD\nRf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3\ny83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97\n+OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem\nK947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH//\nlU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V\nST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni\nmO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80\nENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf\nGUo6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/per-machine/porree/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAvQRxGpJCf8TRIi8Kl5SSuM6A6pu5t0jCdcutNWvh81U=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/porree/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWNYD9mk3j8e2KnnnQytStdsgEcVMHDDd39GnAkwnE4AtU"
  },
  {
    "path": "vars/per-machine/porree/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAh6lOesBKMf3jisZPF/5rGKKEqn0YWBNJdrPZQuj1NGg=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/porree/matrix-hook/.validation-hash",
    "content": "ad87e37f3326763c110d056e28f87471da0e88bb6891cfae314b43d38a79d214"
  },
  {
    "path": "vars/per-machine/porree/opencrow/.validation-hash",
    "content": "b08299c38b79b4a8195a4fe2e0c204c793278a003f4210a0dc778304366c3666"
  },
  {
    "path": "vars/per-machine/porree/opencrow-email/.validation-hash",
    "content": "bbeae7cc7a6c2e9e4c900505b7678737dcd4f5b02d6489439298e7bb5b6d0902"
  },
  {
    "path": "vars/per-machine/porree/opencrow-eversports/.validation-hash",
    "content": "5e33339ca7ab2f9fa413ff7a129654b234f7b80e2a5f523ce75535b5e0ca6116"
  },
  {
    "path": "vars/per-machine/porree/opencrow-local/.validation-hash",
    "content": "466f6a77204ea2ecea3cb9088ad26418fb61f819feb69a6b7a0574aa7919ae08"
  },
  {
    "path": "vars/per-machine/porree/opencrow-nextcloud/.validation-hash",
    "content": "d877886ea3c25f02f8005f8e0c336a27e0b756ef09c1075707847b009070b3ec"
  },
  {
    "path": "vars/per-machine/porree/opencrow-nextcloud-work/.validation-hash",
    "content": "77236f357fa2b9a3e9b633553abc4616a787272f3752388439197409aaf9dea7"
  },
  {
    "path": "vars/per-machine/porree/restic-exporter/.validation-hash",
    "content": "2281613666175c6d6f41a42c0c5136e29409c86f7821fa5e73be344cf9fd18d7"
  },
  {
    "path": "vars/per-machine/porree/state-version/version/value",
    "content": "20.03"
  },
  {
    "path": "vars/per-machine/porree/tor_tor/hostname/value",
    "content": "av3w5wysdbsjvpqdokbk5hvslkajebi3oawjayzz2rwnqx27lynbalad.onion\n"
  },
  {
    "path": "vars/per-machine/porree/vaultwarden/.validation-hash",
    "content": "763d277911fb0f2d246a13714dddc4f2e1a26c9f9850a96e39d21d77aa45345a"
  },
  {
    "path": "vars/per-machine/porree/wireguard/publickey/value",
    "content": "9CSzhcAPiBTmH2cXvQ2f9oDE3TlTjCORyzUKDd2FaSA=\n"
  },
  {
    "path": "vars/per-machine/porree/wireguard-wg-clan/publickey/value",
    "content": "V7t8UAixnIJHSL2ygKJQ/9q2Ga31uzgTFrjAj/Oc/VM=\n"
  },
  {
    "path": "vars/per-machine/porree/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.1"
  },
  {
    "path": "vars/per-machine/porree/wireguard-wg-star/ipv6/value",
    "content": "fda1:05c8:00::4c64:13e6:ade5:b27b"
  },
  {
    "path": "vars/per-machine/porree/wireguard-wg-star/publickey/value",
    "content": "tyeT5qTZxTa/CDmlVSDVr30lONoX52YwqBj9juV8nig=\n"
  },
  {
    "path": "vars/per-machine/porree/yggdrasil/address/value",
    "content": "200:b393:48bf:71a9:e823:b1e3:7f7b:35b1"
  },
  {
    "path": "vars/per-machine/porree/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEApjZboEcrC+4nDkBCZSdu5QUzGOURDqxfEoAYK2+yXPk=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/tanne/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAiZ6WD14E8xKvoFm3POefj3R8kHBwXu/z6LsRBcSe5Wk=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/tanne/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWK5aH4qJEmHcBBbiFmVUmhsBuhMJtKxv9cRvLR8d1E4pG"
  },
  {
    "path": "vars/per-machine/tanne/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAAS8EOkj6QymUn70aS/OceW3aptbdL7l/ye/IEikGFsY=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/tanne/state-version/version/value",
    "content": "25.11"
  },
  {
    "path": "vars/per-machine/tanne/yggdrasil/address/value",
    "content": "201:e616:fc1d:6f16:39b4:b222:b09:919a"
  },
  {
    "path": "vars/per-machine/tanne/yggdrasil/publicKey/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEARnpA+KQ6cZLTd309m5lQQBCl2Sgb7m8i+QOoc5/eGm8=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/traube/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAt8oDdpITSE28hLV1f044+FqO0kLd5W2uYLrjDfjuLQw=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/traube/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWNBoSS2C8mX64NBjDiJb7bB2T799o8c16WdXe8x8s27QP"
  },
  {
    "path": "vars/per-machine/traube/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAU8+DHWE2Pq9M8mP5TZrXp4O2VYAlkQbcD3Qa6VUw0ZM=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/traube/opencrow/.validation-hash",
    "content": "b08299c38b79b4a8195a4fe2e0c204c793278a003f4210a0dc778304366c3666"
  },
  {
    "path": "vars/per-machine/traube/opencrow-traube/.validation-hash",
    "content": "466f6a77204ea2ecea3cb9088ad26418fb61f819feb69a6b7a0574aa7919ae08"
  },
  {
    "path": "vars/per-machine/traube/state-version/version/value",
    "content": "26.05"
  },
  {
    "path": "vars/per-machine/traube/yggdrasil/address/value",
    "content": "200:1fdd:ec44:6540:159c:1b73:ffff:af"
  },
  {
    "path": "vars/per-machine/traube/yggdrasil/publicKey/value",
    "content": "f01109ddcd5ff531f24600007fa816248498a57b27552449fa2f7dbd2c7f227e"
  },
  {
    "path": "vars/per-machine/tunnelmonster/abiotic-docker/.validation-hash",
    "content": "767d345465b3e7b464d7915995eebcb802fde48ec6d68f2a98f5e85c6d521318"
  },
  {
    "path": "vars/per-machine/tunnelmonster/porkbun-dns/.validation-hash",
    "content": "89fe28a9686f5ad73bcaa8569c0a91dceeac2303dab6268e855544c62401df90"
  },
  {
    "path": "vars/per-machine/tunnelmonster/state-version/version/value",
    "content": "25.11"
  },
  {
    "path": "vars/per-machine/uconsole/data-mesher-node-identity/identity.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAFIdLLWmFHcpY4lP2ksXRoUudfTNO/qvlEfk/gbMOvcY=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/uconsole/data-mesher-node-identity/peer.id/value",
    "content": "12D3KooWBCVydSSCb17yQG5MDakfX6pGWZCMx1rkQkuS4o968WZB"
  },
  {
    "path": "vars/per-machine/uconsole/dm-pull-deploy-status-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEANNGShgLvIVT84/KEacPe6cLJFBIXIQOaguKXA12WNoo=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/per-machine/uconsole/state-version/version/value",
    "content": "26.05"
  },
  {
    "path": "vars/per-machine/uconsole/wireguard-wg-clan/publickey/value",
    "content": "Cfdk+ZcKRgsBdlunDjJNDYdL/XWTnb9wr5U13TEbxws=\n"
  },
  {
    "path": "vars/per-machine/uconsole/wireguard-wg-clan-ip/ipv4/value",
    "content": "192.168.8.7"
  },
  {
    "path": "vars/per-machine/uconsole/yggdrasil/address/value",
    "content": "201:63db:5f30:9e74:1e84:ef2f:a06b:5465"
  },
  {
    "path": "vars/per-machine/uconsole/yggdrasil/publicKey/value",
    "content": "67092833d862f85ec43417e52ae69843be0951ba70f7a4bbf0da92bec6662887"
  },
  {
    "path": "vars/shared/bulletin-signing-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAccTy8KpteT2Nb0XGQapmPGrIuQW3VZ6u5jQG0ERgMkU=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/data-mesher-ca/ca.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA86WG8R6leepwlI1cXp10yiOw/sK7jZGPnVHkeX6CMl0=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/data-mesher-network/network.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEABv/K4aNwUgHG5KNr5vbqXADWDxUkRE7q3WepqQkMzQE=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/data-mesher-network-key/public_key/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAKv4CCHplPWe2IgqtDKeVeGLRxtIjdGZNrESDVbRwDFI=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/data-mesher-signing-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA8tcaE7z2zHmpEbTdicvOVy2zTGpAOcsyNeY2PAXkIww=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/dm-dns/.validation-hash",
    "content": "1428c04cb97c4f2294527292e10dea76847b5fb3598a3d5da8c9caad0ff05ee1"
  },
  {
    "path": "vars/shared/dm-dns/zone.conf/value",
    "content": "local-zone: \"pin.\" transparent\nlocal-data: \"status.pin. CNAME porree.pin.\"\nlocal-data: \"prometheus.pin. CNAME porree.pin.\"\nlocal-data: \"irc.pin. CNAME kfbox.pin.\"\n"
  },
  {
    "path": "vars/shared/dm-dns-signing-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAf5E1o5fdkm2LfOYwbhQtQ4duJONhhthxOH5Z+fo3lH0=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/dm-pull-deploy-signing-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAnJopkWCbSkcOIZydLrn5KKDPoY2DRY/8pMesPIRTdbU=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/dm-wallpaper-signing-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEATpRyueno7oq68F/14v5TfT8GSf2vcnloaJbd/UMD9sw=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/dm-wg-star-wg-star-signing-key/signing.pub/value",
    "content": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAK7yaemgaNxay7QLucIuBSnyZo/oeuk2n4Iq/JdCZGsU=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "vars/shared/dns-mesher/.validation-hash",
    "content": "5ac66cf8dd4bda704331135b1369acea19e581b68bfa87cf1e15b908915c6382"
  },
  {
    "path": "vars/shared/dns-mesher/entries/value",
    "content": "otherservice birne\nexampleservice kiwi\n"
  },
  {
    "path": "vars/shared/dns-mesher/zone.conf/value",
    "content": "local-zone: \"pin.\" transparent\nlocal-data: \"music.pin. CNAME kfbox.pin.\"\n"
  },
  {
    "path": "vars/shared/pki-root-ca/ca.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFDzCCAvegAwIBAgIUQjBDSURUCyly2/5c+Fw/iB+HcUAwDQYJKoZIhvcNAQEL\nBQAwFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMB4XDTI2MDIwNzE1MjkyNVoXDTM2\nMDIwNTE1MjkyNVowFzEVMBMGA1UEAwwMQ2xhbiBSb290IENBMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAwXHu+e90SAm7iqxXPenn+N7NfMM97rWpVkB8\nX4g/2jQFfitN9M/CbbXYNPli010+nVzG/bzqBKrIybtIw+5TkCkU1BunRQzlAWxZ\nvOfX1bHj7qwn3q4d98h2wRJ1vkqt/0qf82g0AzmMfMZPRpAxoI4UlDQbVNLOGavg\n/usO20yfsdhCMAm2cNVS8MTYVO9xkgNiMUnDG2j8S/srZcrY8v21EG62atiM5I1k\nY4tikfJSAYKzQL+OuvZjX+rrkpt084CzbKEHkNodz/+OmCqzRyMdR3j4dtMK1OSe\n+bH0tmp7JMR5nGOkhfVdrTE7VyOT1yIjRbRaneYnbrz4FUiO51jKdta08axXKcaJ\nafEd+CT4iWZfRCwVzf2SVhCXzk3KbR6IBM/UeQkIFA4k8YRQPWYmUkMe725f2y1t\nMQOFj/xbI36MzcQavbdLzpstBrbIidjWik5475clhx9NzsvHX9B6amBBUheKN4lE\n6TstNK1uM3+053JQovHM9RS/Ff3AeFiOLf5+W+WL/0M6ue+h7PSe5w2KlYmhw0IR\ncblg3ixhA5CrjXktUURYJfDTaNqjDHuBoCUFwFjq6MowM8WttpViFDbN/5ZtZHMA\n1n6dkLlSW1D0/TcvzAgmTQPq51KdEc9ek6/nwDRcM5OlAd8ohhQ9Ci6Jnm4t3Q0i\nBDzCYOcCAwEAAaNTMFEwHQYDVR0OBBYEFKWoP08/0GFUxRFYLzYvYIe2Sr3NMB8G\nA1UdIwQYMBaAFKWoP08/0GFUxRFYLzYvYIe2Sr3NMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggIBAGZKm92aAxwq2+9WZV3NDqPx3YDpTeiZ69UQF+up\nSsPZn7dAcL2fmMyYyIJ67OR5aiv+UH4+Z4Gp8vaP5y4PqHJKkXOhiSTx11oVNDV9\n4He18FT55BVdHS6xdxnJVV19dFxz1whlv2fP/tiTAdihER0wNj/mJTQifcyUcBkD\nRf6Qm+s1y1Ryu3lJp3yMff/7XBwdz2gg5dGp1+kYBYtRZnXPFfFvQy3uQvd8oqb3\ny83EJS5Biz1ChfE9nydOzoTqNA+m+vKSvOfPpF5ZQ7UgRb855/eSORf1mRWvaD97\n+OKRliXP37njHzzlBSfvi7RxfhLbR3dU6wRmGYOhnxzATXS7cFVp8ziNd3/oXLem\nK947ZsQaJsidyuq19MjQpeae9V8vp43fAB1i4TfzJC9KjgrZi9UUUKBmCHXyrH//\nlU4VBsQzaMvrQDUIdc+Cjl5+DlVleyL2kgJWm1EobgO9V7uHXi3GOl+9UyEaad8V\nST7yg3pIDnrfmvQdBcJ9lA6xaaujVuHaddE+HlO2t+9iARPSdAdxDA/GQKAyDzni\nmO+SpoHu02ECdiUM8avICL0q7zIb/LdQHfsqnm7UU+Q4VAs13BkpaX0XK9TZoM80\nENFRyXIBJ4hP9+V57D0fA+46aDSZCJEKlcz7hj2R9Zl7EGJNaIcMe3CCcV2GXMYf\nGUo6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/shared/restic-cert/.validation-hash",
    "content": "a3da374e9cad9051dce842a991c18e71feb879b073bca0f4ef291cb33c01a0a7"
  },
  {
    "path": "vars/shared/restic-cert/restic-cert/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDgDCCAmigAwIBAgIUDBp2agTh90M4T0oU59HTKguVW+QwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNTAxMjIxMzE3MTlaFw0yNTAy\nMjExMzE3MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw\nHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQDLq1xdCjYpFoKoNTde+ca+Av3Q2aeeE1iug+GO7mAi\nR4WXUlIxBncwvzAqlQBEixsPWsYvL/MND570NvzGfUmfY204fyMb46sMOBAL0Rfi\n/gVaVXLErTGJrOGPUdZgWpdLAATbwMVLqUawKM66lcr+A+ftxUjyosTPpQwrq/Zv\narlORTW4Ekzwo9nX9VQ+ZzoRlokay1H/Nzd3no8lY16IKxleuif+l0O96NsUFgZL\nVFvZHsGBwn5PfWxXvUDqdRjh3QJ3gbdlFq6+B+znsgkj38jW7YdvPUKYYojXEgk/\naAaii33RB4SjyCFeaO9QSTxQC3z8dxG+R9HXfVqOMD2pAgMBAAGjaDBmMB0GA1Ud\nDgQWBBQuz8nhTizE/kt8ePJzj4s8f3BXgDAfBgNVHSMEGDAWgBQuz8nhTizE/kt8\nePJzj4s8f3BXgDAPBgNVHRMBAf8EBTADAQH/MBMGA1UdEQQMMAqCCCoucmVzdGlj\nMA0GCSqGSIb3DQEBCwUAA4IBAQB5knuIq6d3EjfBoiCDJwHFVIjgaWqo7g3Z1Rg7\nGFe4s4HZYMHQcmKFaKDMiqEENstMopUO/iT8dmmeNEIXMuofAVdfd2PJri0hMmCo\njcpswgtOtCHqSE0mALV2R/tEUt0nzSTRbmH1PfDlYfvt8y00kyQLqE8qQwjx+1Pd\nldlxK3b6qZqvfOZzwhvYhRDYON6UFY6u/qc8mF5Qr5qFnzoasz0XDhlhhz5ogvst\nI4Wn9Kb9SxKPL42t5jQVinAT+KZ0x3/cIgl20qVOdiQdCx57p3ZdgDru5g6BOyGk\nCZ7HRSaXpO8dgDZ5bmR3NNlNwvA2TRT0oLcBR3y1tohbp9Ye\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/shared/restic-credentials/.validation-hash",
    "content": "406e5d73f5915d7762fdbbe0dfafb7b37fd5ad1f0e7c47895a7279180d88b810"
  },
  {
    "path": "vars/shared/restic-credentials-backblaze/.validation-hash",
    "content": "406e5d73f5915d7762fdbbe0dfafb7b37fd5ad1f0e7c47895a7279180d88b810"
  },
  {
    "path": "vars/shared/restic-kfbox/.validation-hash",
    "content": "4bfe1766afd037ef15eeeaa180acb87117e957d87631f545782b5f69a1657e79"
  },
  {
    "path": "vars/shared/sftp-credentials/ssh-public-key/value",
    "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7VcxqV5v9vAd4ebHeZew/eOrDVoLwB/r6BMSkG/h0s seed-inbox@kiwi\n"
  },
  {
    "path": "vars/shared/step-ca/ca.crt/value",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBcDCCARegAwIBAgIQTe4Ng6fJCiNj5//dN4M5CDAKBggqhkjOPQQDAjAXMRUw\nEwYDVQQDEwxDbGFuIFJvb3QgQ0EwHhcNMjUwOTAyMDEwNTA2WhcNMjYwOTAyMTMw\nNTA2WjAXMRUwEwYDVQQDEwxDbGFuIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjO\nPQMBBwNCAARa5QaVes9sLb8FDppziGLYslExaSFyOI5PhQDR+aFLyKFi3qrJcNIg\nxcvKTPIF7dNnmRXeaOf5lV7EKrJnujj+o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYD\nVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUXm7vDWvMgJugl2NPa5HcUyY4Zdcw\nCgYIKoZIzj0EAwIDRwAwRAIgAPGLorxuaJa/aJ8gWWeej8gPBhXuVNrTt4Gojaao\nybkCIEX2JVMs5GVmGUiu1igA/x40H6+GtUSHIdOqDWWQgu7M\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "vars/shared/storagebox-ssh/ssh-public-key/value",
    "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHhbO2XmZHSfHUPGYWG4RV93WF3L8/g1y2oIiNn+859r kiwi-storagebox\n"
  },
  {
    "path": "vars/shared/zerotier-controller/zerotier-ip/value",
    "content": "fd0c:cb9f:98da:6865:9c99:930c:cb9f:98da"
  },
  {
    "path": "vars/shared/zerotier-controller/zerotier-network-id/value",
    "content": "0ccb9f98da68659c"
  }
]