<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>How-to guides on ZTD — Zero Trust Dev</title><link>https://ztd-98a2ef.gitlab.io/how-to/</link><description>Recent content in How-to guides on ZTD — Zero Trust Dev</description><generator>Hugo</generator><language>en</language><atom:link href="https://ztd-98a2ef.gitlab.io/how-to/index.xml" rel="self" type="application/rss+xml"/><item><title>Use the Proxmox backend</title><link>https://ztd-98a2ef.gitlab.io/how-to/proxmox-backend/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/proxmox-backend/</guid><description>&lt;h2 id="use-the-proxmox-backend"&gt;Use the Proxmox backend&lt;a class="anchor" href="#use-the-proxmox-backend"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run the same VM on a remote Proxmox node. The interface is identical to the local
backend — only &lt;code&gt;TARGET&lt;/code&gt; changes — and your host needs &lt;strong&gt;only Docker&lt;/strong&gt; (no
libvirt/qemu).&lt;/p&gt;
&lt;blockquote class='book-hint '&gt;
&lt;p&gt;Validated end-to-end against a real PVE 8 node (boot → SSH → guest Docker +
Claude Code). Attribute names track &lt;code&gt;bpg/proxmox&lt;/code&gt;; if your provider version
differs, expect to adjust a name or two.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;You need three things from your Proxmox node: an &lt;strong&gt;API token&lt;/strong&gt;, the &lt;strong&gt;names&lt;/strong&gt; of
the node/datastores/bridge ztd should use, and &lt;strong&gt;snippets enabled&lt;/strong&gt; on a
datastore. This guide walks through each.&lt;/p&gt;</description></item><item><title>Forward a port from the VM</title><link>https://ztd-98a2ef.gitlab.io/how-to/forward-ports/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/forward-ports/</guid><description>&lt;h2 id="forward-a-port-from-the-vm"&gt;Forward a port from the VM&lt;a class="anchor" href="#forward-a-port-from-the-vm"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Reach a service running inside the guest from your host&amp;rsquo;s &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./ztd tunnel -- &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This opens an SSH tunnel (&lt;code&gt;-N -L 8080:localhost:8080&lt;/code&gt;) to the VM using its
ephemeral key. Open &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt; to hit the guest service. Stop the
tunnel with &lt;code&gt;Ctrl-C&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="a-different-port"&gt;A different port&lt;a class="anchor" href="#a-different-port"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The port comes after &lt;code&gt;--&lt;/code&gt; (that&amp;rsquo;s how Task forwards CLI arguments):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./ztd tunnel -- &lt;span style="color:#ae81ff"&gt;5432&lt;/span&gt; &lt;span style="color:#75715e"&gt;# e.g. a Postgres running in the VM&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="multiple-ports-at-once"&gt;Multiple ports at once&lt;a class="anchor" href="#multiple-ports-at-once"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run one tunnel per port in separate terminals, or open them in the background:&lt;/p&gt;</description></item><item><title>Publish the agent's results</title><link>https://ztd-98a2ef.gitlab.io/how-to/scoped-git-token/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/scoped-git-token/</guid><description>&lt;h2 id="publish-the-agents-results-git-runs-on-the-host"&gt;Publish the agent&amp;rsquo;s results (git runs on the host)&lt;a class="anchor" href="#publish-the-agents-results-git-runs-on-the-host"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The agent works inside an &lt;strong&gt;untrusted&lt;/strong&gt; VM, so the VM never holds forge
credentials and never contacts a forge — &lt;strong&gt;all forge git is a host operation.&lt;/strong&gt;
Code goes &lt;em&gt;in&lt;/em&gt; via the repo sync; results come &lt;em&gt;out&lt;/em&gt; through the host.&lt;/p&gt;
&lt;h3 id="why-not-push-from-the-vm"&gt;Why not push from the VM?&lt;a class="anchor" href="#why-not-push-from-the-vm"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A push from the guest would need a forge credential &lt;em&gt;in&lt;/em&gt; the guest. Even a
short-lived, repo-scoped token is still a credential sitting in an
assumed-compromised box — one more thing to leak. Keeping every clone/push on the
trusted host removes that surface entirely. The only credential the guest ever
holds is the per-VM ephemeral SSH key, which grants nothing on your forge. See
&lt;a href="https://ztd-98a2ef.gitlab.io/explanation/zero-trust-model/#credentials"&gt;The zero-trust model&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Use the vz (macOS) backend</title><link>https://ztd-98a2ef.gitlab.io/how-to/vz-backend/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/vz-backend/</guid><description>&lt;h2 id="use-the-vz-macos-backend"&gt;Use the vz (macOS) backend&lt;a class="anchor" href="#use-the-vz-macos-backend"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run a disposable ZTD guest &lt;strong&gt;locally on macOS&lt;/strong&gt; — on Apple&amp;rsquo;s
Virtualization.framework via &lt;a href="https://lima-vm.io"&gt;&lt;code&gt;lima&lt;/code&gt;&lt;/a&gt;, instead of KVM (which
macOS doesn&amp;rsquo;t have). This is the &lt;code&gt;vz&lt;/code&gt; backend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;vz&lt;/code&gt; is the native &lt;em&gt;local&lt;/em&gt; backend on macOS, the way &lt;code&gt;kvm&lt;/code&gt; is on Linux&lt;/strong&gt; — so on
a Mac, &lt;code&gt;local&lt;/code&gt; resolves to &lt;code&gt;vz&lt;/code&gt; automatically and a bare &lt;code&gt;./ztd up&lt;/code&gt; (no &lt;code&gt;TARGET&lt;/code&gt;)
does the right thing. &lt;code&gt;TARGET=vz&lt;/code&gt; is just the explicit form; the examples below
spell it out for clarity, but you can drop it on macOS.&lt;/p&gt;</description></item><item><title>Reset or destroy a VM</title><link>https://ztd-98a2ef.gitlab.io/how-to/reset-and-destroy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/reset-and-destroy/</guid><description>&lt;h2 id="reset-or-destroy-a-vm"&gt;Reset or destroy a VM&lt;a class="anchor" href="#reset-or-destroy-a-vm"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="destroy"&gt;Destroy&lt;a class="anchor" href="#destroy"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./ztd down&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Removes the VM and its disk. On a remote backend, prefix with the target:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TARGET&lt;span style="color:#f92672"&gt;=&lt;/span&gt;proxmox ./ztd down&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="reset-to-a-clean-state"&gt;Reset to a clean state&lt;a class="anchor" href="#reset-to-a-clean-state"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To throw away a VM and recreate it from clean cloud-init in one step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./ztd reset&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is &lt;code&gt;down&lt;/code&gt; followed by &lt;code&gt;up&lt;/code&gt;. Use it when an agent run has left the VM in a
state you don&amp;rsquo;t trust and you want a known-good box again — which, given ZTD&amp;rsquo;s
&lt;a href="https://ztd-98a2ef.gitlab.io/explanation/cattle-not-pets/"&gt;cattle model&lt;/a&gt;, is the normal way
to &amp;ldquo;fix&amp;rdquo; a VM.&lt;/p&gt;</description></item><item><title>Use the EC2 backend</title><link>https://ztd-98a2ef.gitlab.io/how-to/ec2-backend/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/ec2-backend/</guid><description>&lt;h2 id="use-the-ec2-aws-backend"&gt;Use the EC2 (AWS) backend&lt;a class="anchor" href="#use-the-ec2-aws-backend"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ec2&lt;/code&gt; backend runs the disposable VM as an AWS EC2 instance. It reuses the same
shared cloud-init, ephemeral SSH key, and host-side agent workflow
(&lt;code&gt;mount&lt;/code&gt;/&lt;code&gt;fetch&lt;/code&gt;/&lt;code&gt;auth&lt;/code&gt;/&lt;code&gt;run&lt;/code&gt;/&lt;code&gt;audit&lt;/code&gt;) as the other backends — only the &amp;ldquo;where the
VM lives&amp;rdquo; layer differs.&lt;/p&gt;
&lt;blockquote class='book-hint '&gt;
&lt;p&gt;Status: validated end-to-end (&lt;code&gt;TARGET=ec2 ./ztd test&lt;/code&gt; green on a real account).
EC2 instances &lt;strong&gt;cost money per running hour&lt;/strong&gt; — &lt;code&gt;ztd down&lt;/code&gt; terminates them; don&amp;rsquo;t
leave one running.&lt;/p&gt;</description></item><item><title>Pin or override the toolbox image</title><link>https://ztd-98a2ef.gitlab.io/how-to/toolbox-image/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/toolbox-image/</guid><description>&lt;h2 id="pin-or-override-the-toolbox-image"&gt;Pin or override the toolbox image&lt;a class="anchor" href="#pin-or-override-the-toolbox-image"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;kvm&lt;/code&gt;, &lt;code&gt;proxmox&lt;/code&gt;, and &lt;code&gt;ec2&lt;/code&gt; backends provision through the &lt;strong&gt;toolbox
container&lt;/strong&gt; — Terraform, Task, the libvirt client, and the pinned Go toolchain all
live there so your host needs only Docker. (The &lt;code&gt;vz&lt;/code&gt; backend runs &lt;code&gt;limactl&lt;/code&gt; on the
host and uses no toolbox at all, so none of this applies to it.)&lt;/p&gt;
&lt;p&gt;Which image that container runs is decided by the &lt;code&gt;ztd&lt;/code&gt; binary, not by you editing
a compose file.&lt;/p&gt;</description></item><item><title>Install ztd</title><link>https://ztd-98a2ef.gitlab.io/how-to/install/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ztd-98a2ef.gitlab.io/how-to/install/</guid><description>&lt;h2 id="install-ztd"&gt;Install ztd&lt;a class="anchor" href="#install-ztd"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ztd&lt;/code&gt; is a single static binary. It carries its own runtime assets — the Terraform
root, the lima template, cloud-init, the toolbox compose file — embedded inside
it, so there is &lt;strong&gt;no ZTD source checkout to clone&lt;/strong&gt; and nothing to keep in sync
with the binary.&lt;/p&gt;
&lt;p&gt;What it still needs from the host is the thing that can&amp;rsquo;t be shipped in a binary:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Backend&lt;/th&gt;
					&lt;th&gt;Host needs&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;vz&lt;/code&gt; (local macOS)&lt;/td&gt;
					&lt;td&gt;Docker + &lt;a href="https://lima-vm.io/"&gt;lima&lt;/a&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;kvm&lt;/code&gt; (local Linux)&lt;/td&gt;
					&lt;td&gt;Docker + libvirt/qemu/dnsmasq + &lt;code&gt;virtiofsd&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;proxmox&lt;/code&gt; / &lt;code&gt;ec2&lt;/code&gt; (remote)&lt;/td&gt;
					&lt;td&gt;Docker only&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Run &lt;a href="https://ztd-98a2ef.gitlab.io/reference/cli/ztd_check/"&gt;&lt;code&gt;ztd check&lt;/code&gt;&lt;/a&gt; after installing — it
verifies every one of those and tells you the exact command to fix what&amp;rsquo;s missing.&lt;/p&gt;</description></item></channel></rss>