nixops4-quick-intro/slides.html

394 lines
116 KiB
HTML
Raw Permalink Normal View History

2025-02-01 11:44:50 +01:00
<!DOCTYPE html><html lang="en-US"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0"><meta name="apple-mobile-web-app-capable" content="yes"><meta http-equiv="X-UA-Compatible" content="ie=edge"><meta property="og:type" content="website"><meta name="twitter:card" content="summary"><style>@media screen{body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-note-container button{-webkit-tap-highlight-color:transparent;-webkit-appearance:none;appearance:none;background-color:transparent;border:0;color:inherit;cursor:pointer;font-size:inherit;opacity:.8;outline:none;padding:0;transition:opacity .2s linear}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:disabled,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:disabled,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:disabled,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-note-container button:disabled{cursor:not-allowed;opacity:.15!important}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:hover,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:hover,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:hover,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-note-container button:hover{opacity:1}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:active,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:active,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:hover:active,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-note-container button:hover:active{opacity:.6}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:not(:disabled),body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:not(:disabled),body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:hover:not(:disabled),body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-note-container button:hover:not(:disabled){transition:none}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=prev],body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=prev],body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button.bespoke-marp-presenter-info-page-prev{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSI1IiBkPSJNNjggOTAgMjggNTBsNDAtNDAiLz48L3N2Zz4=") no-repeat 50%;background-size:contain;overflow:hidden;text-indent:100%;white-space:nowrap}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=next],body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=next],body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button.bespoke-marp-presenter-info-page-next{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPS
/*!
* Marp default theme.
*
* @theme default
* @author Yuki Hattori
*
* @auto-scaling true
* @size 16:9 1280px 720px
* @size 4:3 960px 720px
*/div#\:\$p>svg>foreignObject>section,div#\:\$p>svg>foreignObject>section [data-theme=light]{--color-prettylights-syntax-comment:#6e7781;--color-prettylights-syntax-constant:#0550ae;--color-prettylights-syntax-entity:#6639ba;--color-prettylights-syntax-storage-modifier-import:#24292f;--color-prettylights-syntax-entity-tag:#116329;--color-prettylights-syntax-keyword:#cf222e;--color-prettylights-syntax-string:#0a3069;--color-prettylights-syntax-variable:#953800;--color-prettylights-syntax-brackethighlighter-unmatched:#82071e;--color-prettylights-syntax-invalid-illegal-text:#f6f8fa;--color-prettylights-syntax-invalid-illegal-bg:#82071e;--color-prettylights-syntax-carriage-return-text:#f6f8fa;--color-prettylights-syntax-carriage-return-bg:#cf222e;--color-prettylights-syntax-string-regexp:#116329;--color-prettylights-syntax-markup-list:#3b2300;--color-prettylights-syntax-markup-heading:#0550ae;--color-prettylights-syntax-markup-italic:#24292f;--color-prettylights-syntax-markup-bold:#24292f;--color-prettylights-syntax-markup-deleted-text:#82071e;--color-prettylights-syntax-markup-deleted-bg:#ffebe9;--color-prettylights-syntax-markup-inserted-text:#116329;--color-prettylights-syntax-markup-inserted-bg:#dafbe1;--color-prettylights-syntax-markup-changed-text:#953800;--color-prettylights-syntax-markup-changed-bg:#ffd8b5;--color-prettylights-syntax-markup-ignored-text:#eaeef2;--color-prettylights-syntax-markup-ignored-bg:#0550ae;--color-prettylights-syntax-meta-diff-range:#8250df;--color-prettylights-syntax-brackethighlighter-angle:#57606a;--color-prettylights-syntax-sublimelinter-gutter-mark:#8c959f;--color-prettylights-syntax-constant-other-reference-link:#0a3069;--color-fg-default:#1f2328;--color-fg-muted:#656d76;--color-fg-subtle:#6e7781;--color-canvas-default:#fff;--color-canvas-subtle:#f6f8fa;--color-border-default:#d0d7de;--color-border-muted:#d8dee4;--color-neutral-muted:rgba(175,184,193,.2);--color-accent-fg:#0969da;--color-accent-emphasis:#0969da;--color-attention-subtle:#fff8c5;--color-danger-fg:#d1242f;color-scheme:light}div#\:\$p>svg>foreignObject>section [data-theme=dark],div#\:\$p>svg>foreignObject>section:where(.invert){--color-prettylights-syntax-comment:#8b949e;--color-prettylights-syntax-constant:#79c0ff;--color-prettylights-syntax-entity:#d2a8ff;--color-prettylights-syntax-storage-modifier-import:#c9d1d9;--color-prettylights-syntax-entity-tag:#7ee787;--color-prettylights-syntax-keyword:#ff7b72;--color-prettylights-syntax-string:#a5d6ff;--color-prettylights-syntax-variable:#ffa657;--color-prettylights-syntax-brackethighlighter-unmatched:#f85149;--color-prettylights-syntax-invalid-illegal-text:#f0f6fc;--color-prettylights-syntax-invalid-illegal-bg:#8e1519;--color-prettylights-syntax-carriage-return-text:#f0f6fc;--color-prettylights-syntax-carriage-return-bg:#b62324;--color-prettylights-syntax-string-regexp:#7ee787;--color-prettylights-syntax-markup-list:#f2cc60;--color-prettylights-syntax-markup-heading:#1f6feb;--color-prettylights-syntax-markup-italic:#c9d1d9;--color-prettylights-syntax-markup-bold:#c9d1d9;--color-prettylights-syntax-markup-deleted-text:#ffdcd7;--color-prettylights-syntax-markup-deleted-bg:#67060c;--color-prettylights-syntax-markup-inserted-text:#aff5b4;--color-prettylights-syntax-markup-inserted-bg:#033a16;--color-prettylights-syntax-markup-changed-text:#ffdfb6;--color-prettylights-syntax-markup-changed-bg:#5a1e02;--color-prettylights-syntax-markup-ignored-text:#c9d1d9;--color-prettylights-syntax-markup-ignored-bg:#1158c7;--color-prettylights-syntax-meta-diff-range:#d2a8ff;--color-prettylights-syntax-brackethighlighter-angle:#8b949e;--color-prettylights-syntax-sublimelinter-gutter-mark:#484f58;--color-prettylights-syntax-constant-other-reference-link:#a5d6ff;--color-fg-default:#e6edf3;--color-fg-muted:#7d8590;--color-fg-subtle:#6e7681;--color-canvas-default:#0d1117;--color-canvas-subtle:#161b22;--color-border-default:#30363d;--color-border-muted:#21262d;--color-neutral-muted:hsla(215,8%,47%,.4);--color-accent-fg:#2f81f7;--color-accent-emphasis:#1f6feb;--color-attention-subtle:rgba(187,128,9,.1
/* content:""; */display:table}div#\:\$p>svg>foreignObject>section:after{clear:both}div#\:\$p>svg>foreignObject>section>:first-child{margin-top:0!important}div#\:\$p>svg>foreignObject>section>:last-child{margin-bottom:0!important}div#\:\$p>svg>foreignObject>section a:not([href]){color:inherit;text-decoration:none}div#\:\$p>svg>foreignObject>section .absent{color:var(--color-danger-fg)}div#\:\$p>svg>foreignObject>section .anchor{float:left;line-height:1;margin-left:-20px;padding-right:4px}div#\:\$p>svg>foreignObject>section .anchor:focus{outline:none}div#\:\$p>svg>foreignObject>section :is(pre,marp-pre),div#\:\$p>svg>foreignObject>section blockquote,div#\:\$p>svg>foreignObject>section details,div#\:\$p>svg>foreignObject>section dl,div#\:\$p>svg>foreignObject>section ol,div#\:\$p>svg>foreignObject>section p,div#\:\$p>svg>foreignObject>section table,div#\:\$p>svg>foreignObject>section ul{margin-bottom:16px;margin-top:0}div#\:\$p>svg>foreignObject>section blockquote>:first-child{margin-top:0}div#\:\$p>svg>foreignObject>section blockquote>:last-child{margin-bottom:0}div#\:\$p>svg>foreignObject>section :is(h1,marp-h1) .octicon-link,div#\:\$p>svg>foreignObject>section :is(h2,marp-h2) .octicon-link,div#\:\$p>svg>foreignObject>section :is(h3,marp-h3) .octicon-link,div#\:\$p>svg>foreignObject>section :is(h4,marp-h4) .octicon-link,div#\:\$p>svg>foreignObject>section :is(h5,marp-h5) .octicon-link,div#\:\$p>svg>foreignObject>section :is(h6,marp-h6) .octicon-link{color:var(--color-fg-default);vertical-align:middle;visibility:hidden}div#\:\$p>svg>foreignObject>section :is(h1,marp-h1):hover .anchor,div#\:\$p>svg>foreignObject>section :is(h2,marp-h2):hover .anchor,div#\:\$p>svg>foreignObject>section :is(h3,marp-h3):hover .anchor,div#\:\$p>svg>foreignObject>section :is(h4,marp-h4):hover .anchor,div#\:\$p>svg>foreignObject>section :is(h5,marp-h5):hover .anchor,div#\:\$p>svg>foreignObject>section :is(h6,marp-h6):hover .anchor{text-decoration:none}div#\:\$p>svg>foreignObject>section :is(h1,marp-h1):hover .anchor .octicon-link,div#\:\$p>svg>foreignObject>section :is(h2,marp-h2):hover .anchor .octicon-link,div#\:\$p>svg>foreignObject>section :is(h3,marp-h3):hover .anchor .octicon-link,div#\:\$p>svg>foreignObject>section :is(h4,marp-h4):hover .anchor .octicon-link,div#\:\$p>svg>foreignObject>section :is(h5,marp-h5):hover .anchor .octicon-link,div#\:\$p>svg>foreignObject>section :is(h6,marp-h6):hover .anchor .octicon-link{visibility:visible}div#\:\$p>svg>foreignObject>section :is(h1,marp-h1) code,div#\:\$p>svg>foreignObject>section :is(h1,marp-h1) tt,div#\:\$p>svg>foreignObject>section :is(h2,marp-h2) code,div#\:\$p>svg>foreignObject>section :is(h2,marp-h2) tt,div#\:\$p>svg>foreignObject>section :is(h3,marp-h3) code,div#\:\$p>svg>foreignObject>section :is(h3,marp-h3) tt,div#\:\$p>svg>foreignObject>section :is(h4,marp-h4) code,div#\:\$p>svg>foreignObject>section :is(h4,marp-h4) tt,div#\:\$p>svg>foreignObject>section :is(h5,marp-h5) code,div#\:\$p>svg>foreignObject>section :is(h5,marp-h5) tt,div#\:\$p>svg>foreignObject>section :is(h6,marp-h6) code,div#\:\$p>svg>foreignObject>section :is(h6,marp-h6) tt{font-size:inherit;padding:0 .2em}div#\:\$p>svg>foreignObject>section summary :is(h1,marp-h1),div#\:\$p>svg>foreignObject>section summary :is(h2,marp-h2),div#\:\$p>svg>foreignObject>section summary :is(h3,marp-h3),div#\:\$p>svg>foreignObject>section summary :is(h4,marp-h4),div#\:\$p>svg>foreignObject>section summary :is(h5,marp-h5),div#\:\$p>svg>foreignObject>section summary :is(h6,marp-h6){display:inline-block}div#\:\$p>svg>foreignObject>section summary :is(h1,marp-h1) .anchor,div#\:\$p>svg>foreignObject>section summary :is(h2,marp-h2) .anchor,div#\:\$p>svg>foreignObject>section summary :is(h3,marp-h3) .anchor,div#\:\$p>svg>foreignObject>section summary :is(h4,marp-h4) .anchor,div#\:\$p>svg>foreignObject>section summary :is(h5,marp-h5) .anchor,div#\:\$p>svg>foreignObject>section summary :is(h6,marp-h6) .anchor{margin-left:-40px}div#\:\$p>svg>foreignObject>section summary :is(h1,marp-h1),div#\:\$p>svg>foreignObject>section summary :is(h2,marp-h2){b
<h1 id="nixops4">NixOps4</h1>
<ul>
<li>Why</li>
<li>What</li>
<li>How</li>
</ul>
<p>Robert Hensing<br />
@roberth</p>
<p>fediversity.eu</p>
</section>
</foreignObject><foreignObject width="1280" height="720" data-marpit-advanced-background="pseudo"><section data-class="invert lead" data-heading-divider="1" lang="en-US" class="invert lead" style="" data-marpit-advanced-background="pseudo" data-marpit-advanced-background-split="left"></section></foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;--marpit-advanced-background-split:30%;" data-marpit-advanced-background="background" data-marpit-advanced-background-split="left"><div data-marpit-advanced-background-container="true" data-marpit-advanced-background-direction="horizontal"><figure style="background-image:url(&quot;roberth.jpeg&quot;);background-size:40%;"></figure></div></section></foreignObject><foreignObject width="70%" height="720" x="30%"><section id="2" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;--marpit-advanced-background-split:30%;" data-marpit-advanced-background="content" data-marpit-advanced-background-split="left">
<h1 id="roberth">@roberth</h1>
<p><br />
Robert Hensing</p>
<ul>
<li>Hercules CI</li>
<li>Nix Steering Committee</li>
<li>Nix team</li>
<li>Module System / flake-parts / modular services / single package fixpoint</li>
<li>NixOps4</li>
</ul>
</section>
</foreignObject><foreignObject width="1280" height="720" data-marpit-advanced-background="pseudo"><section data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="" data-marpit-advanced-background="pseudo" data-marpit-advanced-background-split="left"></section></foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;--marpit-advanced-background-split:50%;" data-marpit-advanced-background="background" data-marpit-advanced-background-split="left"><div data-marpit-advanced-background-container="true" data-marpit-advanced-background-direction="horizontal"><figure style="background-image:url(&quot;ngi_fedi_full.svg&quot;);background-size:70%;"></figure></div></section></foreignObject><foreignObject width="50%" height="720" x="50%"><section id="3" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;--marpit-advanced-background-split:50%;" data-marpit-advanced-background="content" data-marpit-advanced-background-split="left">
<h1 id="fediversityeu">Fediversity.eu</h1>
<ul>
<li>NLNet</li>
<li>NORDUnet</li>
<li>Tweag</li>
<li>Open Internet Discourse Foundation</li>
</ul>
</section>
</foreignObject><foreignObject width="1280" height="720" data-marpit-advanced-background="pseudo"><section data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="" data-marpit-advanced-background="pseudo" data-marpit-advanced-background-split="left"></section></foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="4" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="fediversityeu-1">Fediversity.eu</h1>
<p>Quotes, emphasis mine:</p>
<p>The Fediversity Project is a comprehensive effort to bring easy-to-use, <em><strong>hosted cloud services</strong></em> that have service portability and personal freedom at their core to everyone.</p>
<p>The Fediversity Project enables easy hosting for a wide variety of fediverse platforms, all based on <em><strong>NixOS</strong></em>.</p>
<p>helps with decentralisation of the internet, a core principle of the NGI, by making it easier for people to participate in the Open Social Web <em><strong>on their own terms</strong></em>.</p>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="5" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="nixops4-1">NixOps4</h1>
<ul>
<li>Deployment tool</li>
<li>WIP</li>
<li>Successor to NixOps</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="6" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="why">Why</h1>
<p>2013 - 2020</p>
<ul>
<li>NixOps 1 is a tool to deploy NixOS systems</li>
<li>Provisioning, secrets</li>
<li>Also resources, e.g. AWS Route53</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="7" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="why-1">Why</h1>
<ul>
<li>Testing and CI</li>
<li>Python (*)</li>
<li>Call Nix evaluator in batch, twice (bad)</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="8" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="why-2">Why</h1>
<p>NixOps 2</p>
<p>2020 - ...</p>
<ul>
<li>Plugins</li>
<li>Polyrepo</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;--marpit-advanced-background-split:66%;" data-marpit-advanced-background="background" data-marpit-advanced-background-split="right"><div data-marpit-advanced-background-container="true" data-marpit-advanced-background-direction="horizontal"><figure style="background-image:url(&quot;nixops2.png&quot;);background-size:auto 80%;"></figure></div></section></foreignObject><foreignObject width="34%" height="720"><section id="9" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;--marpit-advanced-background-split:66%;" data-marpit-advanced-background="content" data-marpit-advanced-background-split="right">
<h1 id="why-3">Why</h1>
<p>NixOps 2</p>
<p>2020 - ...</p>
</section>
</foreignObject><foreignObject width="1280" height="720" data-marpit-advanced-background="pseudo"><section data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="" data-marpit-advanced-background="pseudo" data-marpit-advanced-background-split="right"></section></foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="10" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="why-4">Why</h1>
<p>2021</p>
<p>@roberth</p>
<ul>
<li>Still the only tool that integrates provisioning</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="11" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="step-back">Step back</h1>
<p>Nix</p>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code>
┌────────────┐ ┌────────────────────┐
│ Nix │---- instantiate ----&gt;│ Derivations │ ↺ store path
│ expression │ └────────────────────┘
│ language │ ⇑ builds
│ │ ┌────────────────────┐
│ │ │ Nix sandbox, store │
└────────────┘ └────────────────────┘
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="12" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="architecture">Architecture</h1>
<p>NixOps4</p>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code>┌────────────┐ ┌────────────────────┐
│ Nix │---- configure ------&gt;│ Resources │ ↺ nix value
│ expression │ └────────────────────┘
│ language │ ⇑ run output
│ │ ┌────────────────────┐
│ │---- instantiate ----&gt;│ Derivations │ ↺ store path
│ │ └────────────────────┘
│ │ ⇑ builds
│ │ ┌────────────────────┐
│ │ │ Nix sandbox, store │
└────────────┘ └────────────────────┘
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="13" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="resource">Resource</h1>
<ul>
<li>Declares the existence of a real world object</li>
<li>Operations
<ul>
<li>Create</li>
<li>Read</li>
<li>Update</li>
<li>Delete</li>
</ul>
</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="14" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="deployment">Deployment</h1>
<p>Collection of resources</p>
<ul>
<li>wired together with Nix expressions</li>
<li>reflecting some area of the real world</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="15" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="operations">Operations</h1>
<ul>
<li>
<p>CRUD</p>
</li>
<li>
<p>&quot;<code>nix run</code>&quot;</p>
<ul>
<li>backup</li>
<li>key rotation</li>
</ul>
</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="16" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="resource-provider">Resource Provider</h1>
<ul>
<li>Program built with Nix</li>
<li>Called by NixOps</li>
<li>JSON over stdio</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="17" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions">Expressions</h1>
<p>Simplified</p>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">{ <span class="hljs-comment"># flake.nix</span>
<span class="hljs-attr">outputs</span> = inputs: {
nixops4Deployments.<span class="hljs-attr">default</span> = { resources, ... }: {
<span class="hljs-attr">resources</span> = {
&lt;resource name&gt; = {
...
};
};
};
};
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="18" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions-1">Expressions</h1>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">{ resources, ... }: {
<span class="hljs-attr">resources</span> = {
<span class="hljs-string">&quot;nixos&quot;</span> = {
<span class="hljs-attr">imports</span> = [ inputs.nixos.modules.nixops4Resource.nixos ];
<span class="hljs-attr">inputs</span> = {
ssh.<span class="hljs-attr">privateKey</span> = resources.sshkeypair.privateKey;
ssh.<span class="hljs-attr">host</span> = resources.host;
<span class="hljs-attr">module</span> = ./configuration.nix;
};
};
};
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="19" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions-2">Expressions</h1>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">{ resources, ... }: {
<span class="hljs-attr">resources</span> = {
<span class="hljs-string">&quot;nixos&quot;</span> = ...;
<span class="hljs-string">&quot;sshkeypair&quot;</span> = {
<span class="hljs-attr">type</span> = <span class="hljs-string">&quot;ssh.keypair&quot;</span>;
<span class="hljs-attr">inputs</span> = {
<span class="hljs-attr">state</span> = resources.state;
};
};
};
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="20" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions-3">Expressions</h1>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">{ resources, ... }: {
<span class="hljs-attr">resources</span> = {
<span class="hljs-string">&quot;nixos&quot;</span> = ...;
<span class="hljs-string">&quot;sshkeypair&quot;</span> = ...;
<span class="hljs-string">&quot;state&quot;</span> = {
<span class="hljs-attr">type</span> = <span class="hljs-string">&quot;s3.object&quot;</span>;
<span class="hljs-attr">inputs</span> = {
<span class="hljs-attr">endpoint</span> = <span class="hljs-string">&quot;https://garage.example.com&quot;</span>;
<span class="hljs-attr">bucket</span> = <span class="hljs-string">&quot;nixops4-my-project&quot;</span>;
};
};
};
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="21" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions-4">Expressions</h1>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">{ config, resources, ... }: {
options.<span class="hljs-attr">customers</span> = mkOption {
<span class="hljs-attr">type</span> = attrsOf (submodule ./customer.nix);
};
config.<span class="hljs-attr">resources</span> = {
<span class="hljs-string">&quot;state&quot;</span> = ...;
<span class="hljs-string">&quot;sshkeypair&quot;</span> = ...;
<span class="hljs-string">&quot;nixos&quot;</span> = ... (foo config.customers) ...;
};
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="22" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions-5">Expressions</h1>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">{ resources, ... }: {
<span class="hljs-attr">imports</span> = [
./data-model.nix
./applications/pixelfed.nix
./applications/mastodon.nix
./applications/peertube.nix
];
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="23" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="expressions-6">Expressions</h1>
<ul>
<li><code>resources</code> monoid in the category of endofunctors <img class="emoji" draggable="false" alt="😉" src="https://cdn.jsdelivr.net/gh/jdecked/twemoji@14.1.2/assets/svg/1f609.svg" data-marp-twemoji=""/></li>
<li>Structural composition like <code>attrsOf</code> or <code>submodule</code>
<ul>
<li><code>imports</code> is mix-ins</li>
</ul>
</li>
</ul>
<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-nix">top@{ resources, ... }: {
<span class="hljs-attr">resources</span> = {
<span class="hljs-string">&quot;state&quot;</span> = ...;
<span class="hljs-string">&quot;my-host&quot;</span> = mkSequence ({ resources, ... }: {
<span class="hljs-string">&quot;sshkeypair&quot;</span> = ... top.resources.state.handle ...;
<span class="hljs-string">&quot;nixos&quot;</span> = ... resources.sshkeypair.privateKey ...;
});
};
}
</code></pre>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="24" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="module-author-benefits">Module author benefits</h1>
<ul>
<li>All-Nix development experience</li>
<li>No glue code</li>
<li>All declarative</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="25" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="application-benefits">Application benefits</h1>
<p>&quot;NixPanel&quot;</p>
<ul>
<li>
<p>Structured logging</p>
</li>
<li>
<p>Separate evaluator for stability</p>
</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="26" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="operator-benefits">Operator benefits</h1>
<p>CLI for the backend</p>
<p>Integrate arbitrary scripts, no glue code</p>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="27" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="operator-benefits-1">Operator benefits</h1>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="28" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="progress">Progress</h1>
<p>Built, but subject to fixes and improvements</p>
<ul>
<li><code>nixops4</code>/<code>nixops4-eval</code> process architecture</li>
<li>Nix C API, Rust bindings</li>
<li>Documentation tooling</li>
<li>Testing</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="29" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="progress-1">Progress</h1>
<p>TBD</p>
<ul>
<li><code>mkSequence</code> nesting / data dependencies</li>
<li>Read, Update, Delete</li>
<li>State management</li>
<li>More resources, OpenTofu</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="30" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="questions">Questions</h1>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="31" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="not-discussed">Not discussed</h1>
<ul>
<li>
<p>Resource naming within the state</p>
<ul>
<li>read multiple =&gt; migrations</li>
</ul>
</li>
<li>
<p><code>resourceProviderSystem</code></p>
</li>
</ul>
</section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="32" data-class="invert" data-heading-divider="1" lang="en-US" class="invert" style="--class:invert;--heading-divider:1;">
<h1 id="process-architecture">Process Architecture</h1>
<ul>
<li><code>nixops4</code>
<ul>
<li><code>nixops4-eval</code> -&gt; <code>libnixexpr</code> etc (internal)</li>
<li>resource providers
<ul>
<li><code>nixops4-resources-local</code></li>
<li><code>nixops4-resources-opentofu</code> (planned)</li>
<li>...</li>
</ul>
</li>
</ul>
</li>
</ul>
</section>
<script>!function(){"use strict";const t={h1:{proto:()=>HTMLHeadingElement,attrs:{role:"heading","aria-level":"1"},style:"display: block; font-size: 2em; margin-block-start: 0.67em; margin-block-end: 0.67em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold;"},h2:{proto:()=>HTMLHeadingElement,attrs:{role:"heading","aria-level":"2"},style:"display: block; font-size: 1.5em; margin-block-start: 0.83em; margin-block-end: 0.83em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold;"},h3:{proto:()=>HTMLHeadingElement,attrs:{role:"heading","aria-level":"3"},style:"display: block; font-size: 1.17em; margin-block-start: 1em; margin-block-end: 1em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold;"},h4:{proto:()=>HTMLHeadingElement,attrs:{role:"heading","aria-level":"4"},style:"display: block; margin-block-start: 1.33em; margin-block-end: 1.33em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold;"},h5:{proto:()=>HTMLHeadingElement,attrs:{role:"heading","aria-level":"5"},style:"display: block; font-size: 0.83em; margin-block-start: 1.67em; margin-block-end: 1.67em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold;"},h6:{proto:()=>HTMLHeadingElement,attrs:{role:"heading","aria-level":"6"},style:"display: block; font-size: 0.67em; margin-block-start: 2.33em; margin-block-end: 2.33em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold;"},span:{proto:()=>HTMLSpanElement},pre:{proto:()=>HTMLElement,style:"display: block; font-family: monospace; white-space: pre; margin: 1em 0; --marp-auto-scaling-white-space: pre;"}},e="data-marp-auto-scaling-wrapper",i="data-marp-auto-scaling-svg",n="data-marp-auto-scaling-container";class s extends HTMLElement{constructor(){super(),this.svgPreserveAspectRatio="xMinYMid meet";const t=t=>([e])=>{const{width:i,height:n}=e.contentRect;this[t]={width:i,height:n},this.updateSVGRect()};this.attachShadow({mode:"open"}),this.containerObserver=new ResizeObserver(t("containerSize")),this.wrapperObserver=new ResizeObserver(((...e)=>{t("wrapperSize")(...e),this.flushSvgDisplay()}))}static get observedAttributes(){return["data-downscale-only"]}connectedCallback(){var t,s,o,r,a;this.shadowRoot.innerHTML=`\n<style>\n svg[${i}] { display: block; width: 100%; height: auto; vertical-align: top; }\n span[${n}] { display: table; white-space: var(--marp-auto-scaling-white-space, nowrap); width: max-content; }\n</style>\n<div ${e}>\n <svg part="svg" ${i}>\n <foreignObject><span ${n}><slot></slot></span></foreignObject>\n </svg>\n</div>\n `.split(/\n\s*/).join(""),this.wrapper=null!==(t=this.shadowRoot.querySelector(`div[${e}]`))&&void 0!==t?t:void 0;const l=this.svg;this.svg=null!==(o=null===(s=this.wrapper)||void 0===s?void 0:s.querySelector(`svg[${i}]`))&&void 0!==o?o:void 0,this.svg!==l&&(this.svgComputedStyle=this.svg?window.getComputedStyle(this.svg):void 0),this.container=null!==(a=null===(r=this.svg)||void 0===r?void 0:r.querySelector(`span[${n}]`))&&void 0!==a?a:void 0,this.observe()}disconnectedCallback(){this.svg=void 0,this.svgComputedStyle=void 0,this.wrapper=void 0,this.container=void 0,this.observe()}attributeChangedCallback(){this.observe()}flushSvgDisplay(){const{svg:t}=this;t&&(t.style.display="inline",requestAnimationFrame((()=>{t.style.display=""})))}observe(){this.containerObserver.disconnect(),this.wrapperObserver.disconnect(),this.wrapper&&this.wrapperObserver.observe(this.wrapper),this.container&&this.containerObserver.observe(this.container),this.svgComputedStyle&&this.observeSVGStyle(this.svgComputedStyle)}observeSVGStyle(t){const e=()=>{const i=(()=>{const e=t.getPropertyValue("--preserve-aspect-ratio");if(e)return e.trim();return`x${(({textAlign:t,direction:e})=>{if(t.endsWith("left"))return"Min";if(t.endsWith("right"))return"Max";if("start"===t||"end"===t){let i="rtl"===e;return"end"===t&&(i=!i),i?"Max":"Min"}return"Mid"})(t)}YMid meet`})();i!==this.svgPreserveAspectRatio&&(this.svgPreserveAspectRatio=i,this.updateSVGRect()),t===this.svgComputedStyle&&requestAnimationFrame(e)};e()
</script></foreignObject></svg></div><div class="bespoke-marp-note" data-index="5" tabindex="0"><p>What is the problem NixOps solves?</p></div><div class="bespoke-marp-note" data-index="6" tabindex="0"><p>Also _has_ problems
Testing and CI are underdeveloped
Python is probably a fine language, but it did turn away contributors, and the way the code base was set up made it hard
Calling the evaluator twice is both too often and too little, leading to bad workarounds.</p></div><div class="bespoke-marp-note" data-index="8" tabindex="0"><p>I sincerely apologize to the authors and previous maintainers.
They did a good job with the architecture they had.
- Plugin system
- Ossified the architecture</p></div><div class="bespoke-marp-note" data-index="10" tabindex="0"><p>TODO: Explain firmly</p></div><div class="bespoke-marp-note" data-index="11" tabindex="0"><p>Adds new layer on top, analogous to Nix
Focus on `nix value` =&gt; 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 &quot;just a script&quot; that grew until it failed to scale and then ossified with plugins.</p></div><div class="bespoke-marp-note" data-index="14" tabindex="0"><p>a. Arbitrary callable methods in resource provider
b. Scripts depending on resource outputs</p></div><div class="bespoke-marp-note" data-index="16" tabindex="0"><p>These are very abstract. Clarify why.</p><p>Next: zoom in on the function</p></div><script>/*!! License: https://unpkg.com/@marp-team/marp-cli@3.4.0/lib/bespoke.js.LICENSE.txt */
!function(){"use strict";function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var t={from:function(e,t){var n,r=1===(e.parent||e).nodeType?e.parent||e:document.querySelector(e.parent||e),o=[].filter.call("string"==typeof e.slides?r.querySelectorAll(e.slides):e.slides||r.children,(function(e){return"SCRIPT"!==e.nodeName})),i={},a=function(e,t){return(t=t||{}).index=o.indexOf(e),t.slide=e,t},s=function(e,t){i[e]=(i[e]||[]).filter((function(e){return e!==t}))},l=function(e,t){return(i[e]||[]).reduce((function(e,n){return e&&!1!==n(t)}),!0)},c=function(e,t){o[e]&&(n&&l("deactivate",a(n,t)),n=o[e],l("activate",a(n,t)))},d=function(e,t){var r=o.indexOf(n)+e;l(e>0?"next":"prev",a(n,t))&&c(r,t)},u={off:s,on:function(e,t){return(i[e]||(i[e]=[])).push(t),s.bind(null,e,t)},fire:l,slide:function(e,t){if(!arguments.length)return o.indexOf(n);l("slide",a(o[e],t))&&c(e,t)},next:d.bind(null,1),prev:d.bind(null,-1),parent:r,slides:o,destroy:function(e){l("destroy",a(n,e)),i={}}};return(t||[]).forEach((function(e){e(u)})),n||c(0),u}},n=e(t);const r=document.body,o=(...e)=>history.replaceState(...e),i="presenter",a="next",s=["",i,a],l="bespoke-marp-",c=`data-${l}`,d=(e,{protocol:t,host:n,pathname:r,hash:o}=location)=>{const i=e.toString();return`${t}//${n}${r}${i?"?":""}${i}${o}`},u=()=>r.dataset.bespokeView,f=e=>new URLSearchParams(location.search).get(e),m=(e,t={})=>{var n;const r={location,setter:o,...t},i=new URLSearchParams(r.location.search);for(const t of Object.keys(e)){const n=e[t];"string"==typeof n?i.set(t,n):i.delete(t)}try{r.setter({...null!==(n=window.history.state)&&void 0!==n?n:{}},"",d(i,r.location))}catch(e){console.error(e)}},g=(()=>{const e="bespoke-marp";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(e){return!1}})(),p=e=>{try{return localStorage.getItem(e)}catch(e){return null}},v=(e,t)=>{try{return localStorage.setItem(e,t),!0}catch(e){return!1}},h=e=>{try{return localStorage.removeItem(e),!0}catch(e){return!1}},y=(e,t)=>{const n="aria-hidden";t?e.setAttribute(n,"true"):e.removeAttribute(n)},b=e=>{e.parent.classList.add(`${l}parent`),e.slides.forEach((e=>e.classList.add(`${l}slide`))),e.on("activate",(t=>{const n=`${l}active`,r=t.slide,o=r.classList,i=!o.contains(n);if(e.slides.forEach((e=>{e.classList.remove(n),y(e,!0)})),o.add(n),y(r,!1),i){const e=`${n}-ready`;o.add(e),document.body.clientHeight,o.remove(e)}}))},w=e=>{let t=0,n=0;Object.defineProperty(e,"fragments",{enumerable:!0,value:e.slides.map((e=>[null,...e.querySelectorAll("[data-marpit-fragment]")]))});const r=r=>void 0!==e.fragments[t][n+r],o=(r,o)=>{t=r,n=o,e.fragments.forEach(((e,t)=>{e.forEach(((e,n)=>{if(null==e)return;const i=t<r||t===r&&n<=o;e.setAttribute(`${c}fragment`,(i?"":"in")+"active");const a=`${c}current-fragment`;t===r&&n===o?e.setAttribute(a,"current"):e.removeAttribute(a)}))})),e.fragmentIndex=o;const i={slide:e.slides[r],index:r,fragments:e.fragments[r],fragmentIndex:o};e.fire("fragment",i)};e.on("next",(({fragment:i=!0})=>{if(i){if(r(1))return o(t,n+1),!1;const i=t+1;e.fragments[i]&&o(i,0)}else{const r=e.fragments[t].length;if(n+1<r)return o(t,r-1),!1;const i=e.fragments[t+1];i&&o(t+1,i.length-1)}})),e.on("prev",(({fragment:i=!0})=>{if(r(-1)&&i)return o(t,n-1),!1;const a=t-1;e.fragments[a]&&o(a,e.fragments[a].length-1)})),e.on("slide",(({index:t,fragment:n})=>{let r=0;if(void 0!==n){const o=e.fragments[t];if(o){const{length:e}=o;r=-1===n?e-1:Math.min(Math.max(n,0),e-1)}}o(t,r)})),o(0,0)},x=document,k=()=>!(!x.fullscreenEnabled&&!x.webkitFullscreenEnabled),$=()=>!(!x.fullscreenElement&&!x.webkitFullscreenElement),E=e=>{e.fullscreen=()=>{k()&&(async()=>{return $()?null===(e=x.exitFullscreen||x.webkitExitFullscreen)||void 0===e?void 0:e.call(x):((e=x.body)=>{var t;return null===(t=e.requestFullscreen||e.webkitRequestFullscreen)||void 0===t?void 0:t.call(e)})();var e})()},document.addEventListener("keydown",(t=>{"f"!==t.key&&"F11"!==t.key||t.altKey||t.ctrlKey||t.metaKey||!k()||(e.fullscreen(),t.preventDefault())}))},L=`${l}inactive`,S=(e=2e3)=>({parent:t,fire:n})=>{c