diff --git a/.sops.yaml b/.sops.yaml
index 5a45e0c..a08de39 100644
--- a/.sops.yaml
+++ b/.sops.yaml
@@ -72,14 +72,6 @@ creation_rules:
           - *zimbatm
           - *zowoq
           - *adisbladis
-  - path_regex: modules/darwin/.+\.yaml$
-    key_groups:
-      - age:
-          - *mic92
-          - *ryantm
-          - *zimbatm
-          - *zowoq
-          - *adisbladis
   - path_regex: modules/nixos/hercules-ci/.+\.yaml$
     key_groups:
       - age:
diff --git a/dev/shell.nix b/dev/shell.nix
index 15608dc..a02bd89 100644
--- a/dev/shell.nix
+++ b/dev/shell.nix
@@ -1,8 +1,9 @@
-{ pkgs, ... }:
+{ inputs', pkgs, ... }:
 {
   devShells = {
     default = with pkgs; mkShellNoCC {
       packages = [
+        inputs'.agenix.packages.default
         jq
         python3.pkgs.deploykit
         python3.pkgs.invoke
diff --git a/dev/treefmt.nix b/dev/treefmt.nix
index e29f891..9619a76 100644
--- a/dev/treefmt.nix
+++ b/dev/treefmt.nix
@@ -30,6 +30,7 @@
     editorconfig-checker = {
       command = pkgs.editorconfig-checker;
       includes = [ "*" ];
+      excludes = [ "*.age" ];
     };
 
     nix = {
diff --git a/flake.lock b/flake.lock
index 1dbe01e..03c8c8a 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,5 +1,31 @@
 {
   "nodes": {
+    "agenix": {
+      "inputs": {
+        "darwin": [
+          "nix-darwin"
+        ],
+        "home-manager": [],
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1714879853,
+        "narHash": "sha256-URv/JEimxdhCEgokhY9xdMF09iGX8UE96GXFs3RXiJg=",
+        "owner": "qowoz",
+        "repo": "agenix",
+        "rev": "0248db39f453e47c04f39922d170e11b78fa026a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "qowoz",
+        "ref": "darwin",
+        "repo": "agenix",
+        "type": "github"
+      }
+    },
     "buildbot-nix": {
       "inputs": {
         "flake-parts": [
@@ -197,6 +223,7 @@
     },
     "root": {
       "inputs": {
+        "agenix": "agenix",
         "buildbot-nix": "buildbot-nix",
         "comin": "comin",
         "disko": "disko",
@@ -268,6 +295,21 @@
         "type": "github"
       }
     },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
     "treefmt-nix": {
       "inputs": {
         "nixpkgs": [
diff --git a/flake.nix b/flake.nix
index cf321f5..fe85c97 100644
--- a/flake.nix
+++ b/flake.nix
@@ -21,6 +21,12 @@
     # actually not used when using the modules but than nothing ever will try to fetch this nixpkgs variant
     srvos.inputs.nixpkgs.follows = "nixpkgs";
 
+    # rebased patch from https://github.com/ryantm/agenix/pull/241
+    agenix.url = "github:qowoz/agenix/darwin";
+    agenix.inputs.nixpkgs.follows = "nixpkgs";
+    agenix.inputs.home-manager.follows = "";
+    agenix.inputs.darwin.follows = "nix-darwin";
+
     nixpkgs-update.url = "github:nix-community/nixpkgs-update";
     nixpkgs-update.inputs.mmdoc.follows = "";
     nixpkgs-update.inputs.treefmt-nix.follows = "treefmt-nix";
diff --git a/modules/darwin/common/default.nix b/modules/darwin/common/default.nix
index 08a406e..215b573 100644
--- a/modules/darwin/common/default.nix
+++ b/modules/darwin/common/default.nix
@@ -15,6 +15,7 @@ in
     ./upgrade-diff.nix
     ../../shared/known-hosts.nix
     ../../shared/nix-daemon.nix
+    inputs.agenix.darwinModules.age
   ];
 
   # TODO: refactor this to share /users with nixos
diff --git a/modules/darwin/hercules-ci/default.nix b/modules/darwin/hercules-ci/default.nix
index 83089a6..5eebb30 100644
--- a/modules/darwin/hercules-ci/default.nix
+++ b/modules/darwin/hercules-ci/default.nix
@@ -5,10 +5,27 @@ let
   '';
 in
 {
-  # hercules secrets are installed manually from ./secrets.yaml
-  # https://docs.hercules-ci.com/hercules-ci/getting-started/deploy/nix-darwin
+  age.secrets.binary-caches = {
+    file = ../../../secrets/binary-caches.age;
+    mode = "600";
+    owner = "_hercules-ci-agent";
+    group = "_hercules-ci-agent";
+  };
+
+  age.secrets.cluster-join-token = {
+    file = ../../../secrets/cluster-join-token.age;
+    mode = "600";
+    owner = "_hercules-ci-agent";
+    group = "_hercules-ci-agent";
+  };
+
   services.hercules-ci-agent.enable = true;
 
+  services.hercules-ci-agent.settings = {
+    binaryCachesPath = config.age.secrets.binary-caches.path;
+    clusterJoinTokenPath = config.age.secrets.cluster-join-token.path;
+  };
+
   # hercules-ci-agent: security: createProcess: posix_spawnp: does not exist
   # https://github.com/LnL7/nix-darwin/blob/36524adc31566655f2f4d55ad6b875fb5c1a4083/modules/services/hercules-ci-agent/default.nix#L28
   launchd.daemons.hercules-ci-agent.path = pkgs.lib.mkForce [ config.nix.package securityWrapper ];
diff --git a/modules/darwin/hercules-ci/secrets.yaml b/modules/darwin/hercules-ci/secrets.yaml
deleted file mode 100644
index 4b4b3e5..0000000
--- a/modules/darwin/hercules-ci/secrets.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-cluster-join-token.key: ENC[AES256_GCM,data:23z3EpVexVRC5Tlv9Spo0zxqA2dDI7SnVWjpXuqfFsLvFjgbMbv1rw8svHvlL3h3ROV6GQNqa2LtzsiGO4rCDKDui6CpV5pnIH7VnkvS7kqt9OHgkFOJ/dPqgJ6sB4kubEBhvSXqxehbGS+V8y/djhmOM8/faHC8dqryXE30K0LUZ1adI9vE+5r3lmSq6ICeDyq8QgEHVyygaOdP7YvY2ZKZd9aedG35aohAn1XEuJniNXkqpA/W7psCzQtQoKomrjTouuHF6LYDiCSxMLc4SLcfsQO28M+hsovN7Xiugq5OfpQ53FqCsKHXcXw=,iv:bOnyxOYGQyhK4zL9dMcCjVgCdUNtL8Sy1iuWL+OqYgM=,tag:ORqKCRFmN+C16j/Ksamt3g==,type:str]
-binary-caches.json: ENC[AES256_GCM,data:b+YoC3vomkFFZGhix04yvLPFe1h9FW5g561IAY6uBCjiHmCWBJRs5tWHXiTb7tdSR3c37Xkkz8oagLKJv8Fg2+rQgTfPOvsFjsalc54f5QhRFx+/nG+DQ/xE9HMWyT7DsJhERuFmh/ZnfmddB/ESfjOZvSMqluOiB9iT4ypUMTvSU2LH8NGr6bZXFNAkbU/JJFtxxk4p9Deo5+9QbF/jRSVgrg33up/m2zjcW5DQEgL4mLAQSfggaH0R/6wqztErX2uhffVvgKZTt5UPErXe9FpdfU9ndQSrGXrtBKE2NYQEIyI3t55RSNlqdjoFZT5jAc3tD/RIP0uf+qIjfF0i3B8xbRhj8odalwQ7bANjr9TXSWu5wBY8L9Uk4Tp4aFguVdwT7YCGsLYRIME1VKYl5xvvnqYdUbzk7+Azxc1W1Zc=,iv:7tN1ZaJDV+rhNbuQJM11i/GgpkZUmHnflHiZ4DIFIQY=,tag:zOZMTucFA4WAwXAT+bQm0w==,type:str]
-sops:
-    kms: []
-    gcp_kms: []
-    azure_kv: []
-    hc_vault: []
-    age:
-        - recipient: age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz
-          enc: |
-            -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1VW4vSnA3S0taNGtNMERC
-            Yzlwa3FFTXZKTzZ3cnlDRFhVRnFEZ3d6eEdjClFIbHYzR0RYZ1ZuQ0tmZTdXaEFF
-            TkRROThkdlV6Y1FxV0NBZmVPaWRNamcKLS0tIDdyOGMyak5yUWhoUUxUWTlZalRl
-            WTYrc0xHcklQd1c0VkZpZDVVWTQ3WlkK7clbIbcIKxb9XJFg7Crf90Xz2iOG7qsg
-            xNr1iv7lqrWQIO0mb1b8EC/PN8BpP24NmKXurD5BYJUHVoZQnq5tFA==
-            -----END AGE ENCRYPTED FILE-----
-        - recipient: age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay
-          enc: |
-            -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2SUtGN2R6K0VQeUd1MjBI
-            dGdUN3VrWGl2aUQ0VCtidnZhVXNJQnRvQ2c0CjQwT25RR1NMdmdUZ0JqcXJlaUdS
-            TFFwelltdWtQbkVBVVZYUEloNlAxVEEKLS0tIERZZ0tHUURSaXVWZmZtekJtRjlo
-            WEI3dVE5QVJXMmJQNGxjSm1GMWJ2bUUKciFofJsB2vLgWbJiozL4Dc3XJRNyYfr5
-            uhN29RDVGA0WjRsExPc/9TyCVFynE6NKIYr6bNFgq/MTuU/Oc7uUig==
-            -----END AGE ENCRYPTED FILE-----
-        - recipient: age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h
-          enc: |
-            -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlM1RpdWlsQXYxd3gyUTJO
-            MUl1Z0RSMDBCSmlLM1E0V0ZxcjByMG4rSVFjCjNXd1lsMldEMDZ2cWNQK1hzQlFh
-            b2dJRUtPTkdNOTBRUlYxYlBkVTVBQVEKLS0tIHdxTDI2WnMvR2RlaUtTek4vdGJO
-            MmtSQnZqaVJHTG1pbDg5MmgxSnVxQUkKqAuztZ/LNVzCn03nQxbN6rJlngijvPbo
-            RI45pv5o6BKR3Ty1sI/Gmr/WTp1mQPjgP7Am8CTxjVXzcQzvgnlSQA==
-            -----END AGE ENCRYPTED FILE-----
-        - recipient: age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n
-          enc: |
-            -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1djhWYWZXVmh2ellhSlNU
-            QkZ6SEwzNFhib3dSdXNqOTBCN0Zld3VMYlFZClAvcWY3VlVjYlJjLzdlbG05QTlH
-            M0UrVURwU3hkZ2tUdS9USlBxY3AwVzQKLS0tICtCNGtPd2RIWFJvRmgrVnR5OXl3
-            QW1ZQ2Rab1hsbTRFb3Z3dCt3UzV0ekEK2sn0tU7lM09mjsys5WZhhn+WVJ+uCy70
-            lNK30Wu50f2wv0JjdwcANXY1tWOJyZJAcp75p8Rgy+JS+xIoJb1QqQ==
-            -----END AGE ENCRYPTED FILE-----
-        - recipient: age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy
-          enc: |
-            -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCNGt3dVdvdUptY1VOdEVQ
-            NFA3dG81SVkzRm1IWmN4MkZrNWZ3SU5hT0c0CkdldDJFODVGNnJENlRmRnVrNGJ4
-            OVo3d0M2L3lYczVVenRRRVkrTnBtemcKLS0tIGI3K3ViZ0swdE1yWmI3TGpqQ0hG
-            U3hITzJvWUthY1hkd1hod09LbUhXaE0KSS10Ocvu5bRgLAZCQv+A8dptHNQxsAfz
-            8xU6aSgMMI1rxP4DcuEe/+ysTAyAQUnXwAmeYWGdfIxUpVG/84xsOw==
-            -----END AGE ENCRYPTED FILE-----
-    lastmodified: "2023-07-17T03:50:09Z"
-    mac: ENC[AES256_GCM,data:8HpTYCODhaPNU9blJuIO6uDnYHwQGNpnZYUuiiP3oH4a870R8+/aj5qziRgL/kXkufZnjKBo6IbEXj9qfMVi+/+SxV+cFQHaQy8mPpD9QpzVeWEHRVypuBx4wY3T08fcKSZVCRYsumEMnF5cHZfse6vB5Fe/gclTE22OaYlHEXA=,iv:WJJqyLZ97kRr44iJOo7q2Rzu4W4yvZyuH8TdJYmmCTQ=,tag:omyuDAEkR7w8+XOE1EGDvQ==,type:str]
-    pgp: []
-    unencrypted_suffix: _unencrypted
-    version: 3.7.3
diff --git a/secrets/binary-caches.age b/secrets/binary-caches.age
new file mode 100644
index 0000000..de5ff58
Binary files /dev/null and b/secrets/binary-caches.age differ
diff --git a/secrets/cluster-join-token.age b/secrets/cluster-join-token.age
new file mode 100644
index 0000000..d1e9547
--- /dev/null
+++ b/secrets/cluster-join-token.age
@@ -0,0 +1,24 @@
+age-encryption.org/v1
+-> ssh-rsa ALNSWw
+k14GuxixIuiA4WhYtWW5PaevHx5QZc2HF9HM7Ia2ji4mNg2Pc1+cXFZG/QLROTVo
+EL0c3/MzZBGAdFYkkm8hlA+S9JLdgiP8ROIT8hjhOE55uWWaH8uDQGODQX42nBe0
+w1wN9iBDKJJ0s4kSak9K8GqS0afVvppLPZTcqoaHbh2YapXSYu7LK8BBgz4+nBUP
+0axc3TIVgUzEDls7VGU1c+aavDvBb8c/fg5w5pJZy379bzU5TWpppmi7U7hEboCA
+IMeAH5iffaksmyPIHlK/iwpHdkchLKX+2YHAu8DxywHeowm4rbxKv3oHfH+/3uM3
+28VUeqYY/SCqwLSe84ZnSg
+-> ssh-ed25519 Qi7vNw W23Q9s5rainiPnp67oLEcLKpEfmvqxUUWL5u+yvN+0o
+/Tiyf6QaTM1NIKPPdrK9e8K43Ee0cNAV5uS5fiab3p8
+-> ssh-ed25519 MW0fCg 2AXjCOaTHC6kJ+m5OnVwyuy6DEI2+6E//fZ7PkZsfFo
+gEvzFrYhSCCvBaOjPb1aI49kCJBK5mpDGShJuVpbSn4
+-> ssh-ed25519 92bXiA xv18v2ncQRE9MWJbpNsGUkwhho/NNZ465zcOl1qi3HQ
+OKP7B3ecWEeBF7GA0Vx72BMRbM6iE6/fQ4mkCaGx4R0
+-> ssh-ed25519 h1lenA tBhqzlU6IKkHKkTb9p8p2R/OOyLtOhLyAIujO+1oyEg
+8ORTR81GImpbXu4rJ0HTSOwbFb3Zw+JmfYSGFoQXLHg
+-> ssh-ed25519 7tFeRw BpJpUC2tTiDfGnO5JvYwW/JiTU2RSfeKzDOCMfLBUxY
+u0mDqrcX/vKNJvqu9Bjl6qUrf1CAkGm5cBRhg984lXk
+-> ssh-ed25519 /B167A t3O6wWHJ1GAxe/e7XwiUzl+uWVBG5F7vc088zFYoFm0
+T954lFCHmJTuOnMy5N1OizGzySbd5/ow1eBbcpJl/F4
+--- BHVcjNVuUaft0wyxOjncdhbpiC9UtUgWSk8sUr6lBCw
+��'���y�"�N��Tm;�)w�V�Ĭ���ќwtֽ,����}-�1�|�ʅ�����
b��	t%���+l0�`��W�� �vw�6�>"7�i3�&L��Y*�P(S��	<򠎜������m��ˠTqdK$(��y7�PG(y�*��7p��E�/gT�?3Aq���16�#�ȋ�T'y��G�e%.�ۀʭ�Op��:�
+��Ҩ3Hv��E%(�����s�����l��%������������
+`�w��FLX
\ No newline at end of file
diff --git a/secrets/secrets.nix b/secrets/secrets.nix
new file mode 100644
index 0000000..507deb3
--- /dev/null
+++ b/secrets/secrets.nix
@@ -0,0 +1,18 @@
+let
+  adisbladis = builtins.readFile ../users/keys/adisbladis;
+  mic92 = builtins.readFile ../users/keys/mic92;
+  ryantm = builtins.readFile ../users/keys/ryantm;
+  zimbatm = builtins.readFile ../users/keys/zimbatm;
+  zowoq = builtins.readFile ../users/keys/zowoq;
+
+  users = [ adisbladis mic92 ryantm zimbatm zowoq ];
+
+  inherit ((import ../modules/shared/known-hosts.nix).programs.ssh) knownHosts;
+
+  darwin02 = knownHosts.darwin02.publicKey;
+  darwin03 = knownHosts.darwin03.publicKey;
+in
+{
+  "binary-caches.age".publicKeys = users ++ [ darwin02 darwin03 ];
+  "cluster-join-token.age".publicKeys = users ++ [ darwin02 darwin03 ];
+}