commit eab042ab187a7aa53419c09af4a3662f9468db2e
Author: Robert Hensing <robert@roberthensing.nl>
Date:   Tue Nov 12 14:38:34 2024 +0100

    init

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d16cf89
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+slides.html
+
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..ffb7d86
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,58 @@
+{
+  "nodes": {
+    "flake-parts": {
+      "inputs": {
+        "nixpkgs-lib": "nixpkgs-lib"
+      },
+      "locked": {
+        "lastModified": 1730504689,
+        "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "rev": "506278e768c2a08bec68eb62932193e341f55c90",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1731139594,
+        "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-lib": {
+      "locked": {
+        "lastModified": 1730504152,
+        "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=",
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz"
+      },
+      "original": {
+        "type": "tarball",
+        "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz"
+      }
+    },
+    "root": {
+      "inputs": {
+        "flake-parts": "flake-parts",
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..26110bd
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,51 @@
+{
+  description = "Description for the project";
+
+  inputs = {
+    flake-parts.url = "github:hercules-ci/flake-parts";
+    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+  };
+
+  outputs = inputs@{ flake-parts, ... }:
+    flake-parts.lib.mkFlake { inherit inputs; } {
+      imports = [
+      ];
+      systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
+      perSystem = { config, self', inputs', pkgs, system, ... }: {
+        devShells.default = pkgs.mkShell {
+          nativeBuildInputs = [
+            pkgs.marp-cli
+          ];
+          shellHook = ''
+            echo "$motd" >&2
+          '';
+          motd = ''
+            Hello! The following commands are available:
+
+                - live
+
+          '';
+        };
+        packages.default = pkgs.stdenv.mkDerivation {
+          name = "nixops4-quick-intro";
+          nativeBuildInputs = [
+            pkgs.marp-cli
+          ];
+          src = ./.;
+      	  buildPhase = ''
+            ls -al
+            marp slides.md
+          '';
+          installPhase = ''
+            mkdir $out
+            cp slides.html $out/
+            cp *.png *.svg $out/
+          '';
+        };
+        apps.default.program = pkgs.writeScriptBin "open-slides" ''
+          #!${pkgs.runtimeShell}
+          ${if pkgs.stdenv.hostPlatform.isDarwin then "open" else "xdg-open"} ${config.packages.default}/slides.html
+        '';
+      };
+    };
+}
diff --git a/nixops2-wip.xcf b/nixops2-wip.xcf
new file mode 100644
index 0000000..b1ac624
Binary files /dev/null and b/nixops2-wip.xcf differ
diff --git a/nixops2.png b/nixops2.png
new file mode 100644
index 0000000..e8367a2
Binary files /dev/null and b/nixops2.png differ
diff --git a/nixops2.xcf b/nixops2.xcf
new file mode 100644
index 0000000..e3e0cd9
Binary files /dev/null and b/nixops2.xcf differ
diff --git a/nixops4.svg b/nixops4.svg
new file mode 100644
index 0000000..9e2cf90
--- /dev/null
+++ b/nixops4.svg
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="514.33069"
+   height="527.11523"
+   viewBox="0 0 514.33069 527.11523"
+   version="1.1"
+   id="svg1"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs1">
+    <linearGradient
+       id="linearGradient73">
+      <stop
+         style="stop-color:#415e9a;stop-opacity:1;"
+         offset="0"
+         id="stop73" />
+      <stop
+         style="stop-color:#4a6baf;stop-opacity:1;"
+         offset="0.23"
+         id="stop77" />
+      <stop
+         style="stop-color:#5277c3;stop-opacity:1;"
+         offset="1"
+         id="stop74" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient71">
+      <stop
+         style="stop-color:#699ad7;stop-opacity:1;"
+         offset="0"
+         id="stop71" />
+      <stop
+         style="stop-color:#7eb1dd;stop-opacity:1;"
+         offset="0.22531722"
+         id="stop75" />
+      <stop
+         style="stop-color:#7ebae4;stop-opacity:1;"
+         offset="1"
+         id="stop72" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient68">
+      <stop
+         style="stop-color:#415e9a;stop-opacity:1;"
+         offset="0"
+         id="stop69" />
+      <stop
+         style="stop-color:#4a6baf;stop-opacity:1;"
+         offset="0.23"
+         id="stop76" />
+      <stop
+         style="stop-color:#5277c3;stop-opacity:1;"
+         offset="1"
+         id="stop70" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient66">
+      <stop
+         style="stop-color:#699ad7;stop-opacity:1;"
+         offset="0"
+         id="stop66" />
+      <stop
+         style="stop-color:#7eb1dd;stop-opacity:1;"
+         offset="0.28229418"
+         id="stop68" />
+      <stop
+         style="stop-color:#7ebae4;stop-opacity:1;"
+         offset="1"
+         id="stop67" />
+    </linearGradient>
+    <linearGradient
+       xlink:href="#linearGradient66"
+       id="linearGradient67"
+       x1="386.29163"
+       y1="527.11523"
+       x2="214.33086"
+       y2="319.26953"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       xlink:href="#linearGradient68"
+       id="linearGradient70"
+       x1="218.03917"
+       y1="-4.4999979e-06"
+       x2="450.00012"
+       y2="311.76953"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       xlink:href="#linearGradient71"
+       id="linearGradient72"
+       x1="-4.9999999e-06"
+       y1="-5e-07"
+       x2="261.34012"
+       y2="311.76953"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       xlink:href="#linearGradient73"
+       id="linearGradient74"
+       x1="514.33069"
+       y1="319.26953"
+       x2="342.99097"
+       y2="527.11523"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <g
+     id="g4"
+     style="display:inline"
+     transform="translate(-12.834648,-6.4423828)">
+    <g
+       id="g79"
+       transform="translate(12.834653,6.4423872)">
+      <g
+         id="g78">
+        <path
+           style="display:inline;fill:url(#linearGradient70);fill-opacity:1;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
+           d="m 235.35948,59.999996 48.1875,83.462884 7.17187,12.42188 -55.35937,95.88477 -17.32031,30 17.32031,30 h 34.64062 l 55.35938,-95.88477 55.35931,95.88477 h 69.28132 L 360.0001,155.88476 270.0001,-4.499998e-6 Z"
+           id="path5" />
+        <path
+           style="display:inline;fill:url(#linearGradient72);fill-opacity:1;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
+           d="M 261.34013,-5e-7 H 200.71874 89.999995 L -5e-6,155.88476 l 90,155.88477 H 226.69891 L 209.3782,281.76917 226.6985,251.76953 H 124.64062 L 69.281245,155.88476 124.64062,60 h 102.0585 z"
+           id="path1" />
+      </g>
+      <g
+         id="g77">
+        <path
+           style="display:inline;fill:url(#linearGradient67);fill-opacity:1;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
+           d="m 368.97133,467.11523 -25.35938,-43.92187 25.35938,-43.92383 17.32031,-30 -17.32031,-30 H 334.3307 l -25.35937,43.92383 -25.36238,-43.92383 h -69.27809 l 59.99984,103.92383 60,103.92187 z"
+           id="path66" />
+        <path
+           style="display:inline;fill:url(#linearGradient74);fill-opacity:1;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
+           d="M 342.99096,527.11523 H 454.3307 l 60,-103.92187 -60,-103.92383 h -50.71875 -25.98047 l 17.32031,30 -17.32031,30 h 42.0586 l 25.35937,43.92383 -25.35937,43.92187 h -42.05811 z"
+           id="path4" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/slides.md b/slides.md
new file mode 100644
index 0000000..444ab31
--- /dev/null
+++ b/slides.md
@@ -0,0 +1,305 @@
+---
+marp: true
+# theme: gaia
+class: invert
+headingDivider: 1
+---
+
+
+# NixOps4
+
+
+![bg left:40% 80%](nixops4.svg)
+
+<!--
+_class: invert lead
+-->
+
+- Why
+- What
+- How
+- Demo
+
+
+# Why
+
+- NixOps 1 is a tool to deploy NixOS systems
+- Provisioning
+- Secrets
+- Other resources, such as AWS Route53, etc
+- Python program
+- Call Nix evaluator twice
+
+<!--
+  calling the evaluator twice is not good enough
+-->
+
+
+# Architecture
+
+NixOps 2
+
+2013 - 2020 - ...
+
+![bg right:66% height:80%](nixops2.png)
+
+<!--
+
+I sincerely apologize to the authors and previous maintainers.
+They did a good job with the architecture they had.
+
+- Plugin system
+- Ossified the architecture
+-->
+
+
+# Architecture
+
+Nix
+
+```
+
+
+
+
+┌────────────┐                      ┌────────────────────┐
+│ Nix        │---- instantiate ---->│ Derivations        │ ↺ store path
+│ expression │                      └────────────────────┘
+│ language   │                                ⇑ builds
+│            │                      ┌────────────────────┐
+│            │                      │ Nix sandbox, store │
+└────────────┘                      └────────────────────┘
+```
+
+<!--
+  Explain thoroughly
+-->
+
+# Architecture
+
+NixOps4
+
+```
+┌────────────┐                      ┌────────────────────┐
+│ Nix        │---- configure ------>│ Resources          │ ↺ nix value
+│ expression │                      └────────────────────┘
+│ language   │                                ⇑ run output
+│            │                      ┌────────────────────┐
+│            │---- instantiate ---->│ Derivations        │ ↺ store path
+│            │                      └────────────────────┘
+│            │                                ⇑ builds
+│            │                      ┌────────────────────┐
+│            │                      │ Nix sandbox, store │
+└────────────┘                      └────────────────────┘
+```
+
+<!--
+Adds new layer on top
+
+Focus on `nix value` => precisely that; no tight coupling between NixOps and its resources
+
+NixOps4 just manages the data flows generically
+
+Another benefit
+ - resource can be implemented in any language, with any library
+
+Not comparable to NixOps 2 architecture image. NixOps 2 is "just a script" that grew until it failed to scale and then ossified with plugins.
+
+-->
+
+
+# Resource
+
+- Declares the existence of a real world object
+- Operations
+  - Create
+  - Read
+  - Update
+  - Delete
+
+# Resource Provider
+
+- Separate process
+- executable obtained with Nix.
+
+# Operations
+
+- CRUD
+
+- "`nix run`"
+  - backup
+  - key rotation
+
+<!--
+  a. Arbitrary callable methods in resource provider
+  b. Scripts depending on resource outputs 
+-->
+
+# Process Architecture
+
+- NixOps4
+  - `nixops4-eval` -> `libnixexpr` etc (internal)
+  - resource providers
+    - `nixops4-resources-local`
+    - `nixops4-resources-opentofu` (planned)
+    - ...
+
+# Expressions
+
+Simplified
+
+```nix
+{ # flake.nix
+  outputs = inputs: {
+    nixops4Deployments.default = { resources, ... }: {
+      resources = {
+        "state" = {
+          ...
+        };
+      };
+    };
+  };
+}
+```
+
+# Expressions
+
+```nix
+{ resources, ... }: {
+  resources = {
+    "state" = {
+      type = "s3.object";
+      inputs = {
+        endpoint = "https://garage.example.com";
+        bucket = "nixops4-my-project";
+      };
+    };
+  };
+}
+```
+
+# Expressions
+
+```nix
+{ resources, ... }: {
+  resources = {
+    "state" = ...;
+    
+
+
+
+
+  
+  };
+}
+```
+
+# Expressions
+
+```nix
+{ resources, ... }: {
+  resources = {
+    "state" = ...;
+    "sshkey" = {
+      type = "ssh.key";
+      inputs = {
+        state = resources.state.handle;
+      };
+    };
+  };
+}
+```
+
+# Expressions
+
+```nix
+{ resources, ... }: {
+  resources = {
+    "state" = ...;
+    "sshkey" = ...;
+    "nixos" = {
+      imports = [ inputs.nixos.modules.nixops4Resource.nixos ];
+      inputs = {
+        ssh.privateKey = resources.sshkey.privateKey;
+        ssh.host = resources.host;
+        module = ./configuration.nix;
+      };
+    };
+  };
+}
+```
+
+# Expressions
+
+```nix
+{ resources, ... }: {
+  options.customers = mkOption {
+    type = attrsOf (submodule ./customer.nix);
+  };
+  config.resources = {
+    "state" = ...;
+    "sshkey" = ...;
+    "nixos" = ...;
+  };
+}
+```
+
+# Expressions
+
+```nix
+{ resources, ... }: {
+  imports = [
+    ./data-model.nix
+    ./applications/pixelfed.nix
+    ./applications/mastodon.nix
+    ./applications/peertube.nix
+  ];
+}
+```
+
+# Expressions
+
+- `resources` monoid in the category of endofunctors :wink:
+- Structural composition like `attrsOf` or `submodule`
+  - `imports` is mix-ins
+
+```nix
+top@{ resources, ... }: {
+  resources = {
+    "state" = ...;
+    "my-host" = mkSequence ({ resources, ... }: {
+      "sshkey" = ... top.resources.state.handle ...;
+      "nixos" = ... resources.sshkey.privateKey ...;
+    });
+  };
+}
+```
+
+# Module author benefits
+
+- All-Nix development experience
+- No glue code
+- All declarative
+
+# Application benefits
+
+"NixPanel"
+
+- Structured logging
+
+- Separate evaluator for stability
+
+# Operator benefits
+
+CLI interface for the backend
+
+Integrate arbitrary scripts, no glue code
+
+# Demo?
+
+# Not discussed
+
+- Resource naming within the state
+  - read multiple => migrations
+
+- `resourceProviderSystem`