<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: John Onukwu</title>
    <description>The latest articles on Forem by John Onukwu (@john_onukwu_b0ef7ecaec540).</description>
    <link>https://forem.com/john_onukwu_b0ef7ecaec540</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3916680%2F5ee4988a-88e6-42b2-81af-547529f92855.jpg</url>
      <title>Forem: John Onukwu</title>
      <link>https://forem.com/john_onukwu_b0ef7ecaec540</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/john_onukwu_b0ef7ecaec540"/>
    <language>en</language>
    <item>
      <title>Building SwiftDeploy: From Declarative Deployments to Policy-Gated Releases</title>
      <dc:creator>John Onukwu</dc:creator>
      <pubDate>Wed, 06 May 2026 20:47:09 +0000</pubDate>
      <link>https://forem.com/john_onukwu_b0ef7ecaec540/building-swiftdeploy-from-declarative-deployments-to-policy-gated-releases-apo</link>
      <guid>https://forem.com/john_onukwu_b0ef7ecaec540/building-swiftdeploy-from-declarative-deployments-to-policy-gated-releases-apo</guid>
      <description>&lt;h1&gt;
  
  
  Building SwiftDeploy: From Declarative Deployments to Policy-Gated Releases
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;SwiftDeploy is a DevOps deployment tool I built as part of the HNG DevOps Track. The project started in Stage 4A as a deployment automation tool, then evolved in Stage 4B into a safer and more observable deployment system.&lt;/p&gt;

&lt;p&gt;In Stage 4A, I built the deployment engine. SwiftDeploy could read a &lt;code&gt;manifest.yaml&lt;/code&gt; file, generate infrastructure configuration files, deploy the stack, and switch between stable and canary modes.&lt;/p&gt;

&lt;p&gt;In Stage 4B, I extended the same project with observability, Open Policy Agent policy enforcement, chaos testing, status monitoring, and audit reporting.&lt;/p&gt;

&lt;p&gt;The goal was to move from simply starting containers to building a deployment tool that can answer important safety questions before making changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the host healthy enough to deploy?
Is the canary safe enough to promote?
What happened during deployment?
Can we prove the system was checked before action was taken?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Design: A Tool That Writes Its Own Infrastructure
&lt;/h2&gt;

&lt;p&gt;The main design principle of SwiftDeploy is that &lt;code&gt;manifest.yaml&lt;/code&gt; is the single source of truth.&lt;/p&gt;

&lt;p&gt;Instead of manually editing &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;nginx.conf&lt;/code&gt;, the CLI reads values from the manifest and generates the required infrastructure files from templates.&lt;/p&gt;

&lt;p&gt;The flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;manifest.yaml
      |
      v
swiftdeploy init
      |
      v
nginx.conf + docker-compose.yml
      |
      v
docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the generated files can be deleted and recreated again from the manifest. That makes the deployment repeatable and easier to grade, test, and maintain.&lt;/p&gt;

&lt;p&gt;A simplified version of my manifest looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10johnny-swiftdeploy-stage4b:latest&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;
  &lt;span class="na"&gt;restart_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;proxy_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;

&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;swiftdeploy-net&lt;/span&gt;
  &lt;span class="na"&gt;driver_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;

&lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;opa_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8181&lt;/span&gt;
  &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;min_disk_free_gb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;max_cpu_load&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.0&lt;/span&gt;
    &lt;span class="na"&gt;max_error_rate_percent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;max_p99_latency_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The manifest controls the service image, service port, deployment mode, Nginx port, Docker network, restart policy, and policy thresholds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Diagram
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         +----------------------+
                         |      Operator        |
                         |  python swiftdeploy  |
                         +----------+-----------+
                                    |
                                    | queries policies
                                    v
                         +----------------------+
                         |   OPA Policy Engine  |
                         | localhost:8181 only  |
                         +----------------------+

User / curl / Browser
        |
        v
+----------------------+
|   Nginx Container    |
|   Host Port: 8080    |
| X-Deployed-By header |
| Access logs          |
+----------+-----------+
           |
           | internal Docker network
           v
+----------------------+
|  Python API Service  |
|  Internal Port:3000  |
| /                    |
| /healthz             |
| /chaos               |
| /metrics             |
+----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only Nginx is exposed to the host. The Python service is not exposed directly. OPA is also not exposed through Nginx; it is only reachable by the CLI through &lt;code&gt;127.0.0.1:8181&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This design prevents public leakage of the OPA API and keeps all user-facing traffic going through Nginx.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 4A: Deployment Lifecycle
&lt;/h2&gt;

&lt;p&gt;In Stage 4A, SwiftDeploy supported the core lifecycle commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy init
python swiftdeploy validate
python swiftdeploy deploy
python swiftdeploy promote canary
python swiftdeploy promote stable
python swiftdeploy teardown
python swiftdeploy teardown &lt;span class="nt"&gt;--clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Init
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;init&lt;/code&gt; command reads &lt;code&gt;manifest.yaml&lt;/code&gt; and generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx.conf
docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These files are generated in the project root, not in a separate generated folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;validate&lt;/code&gt; command performs pre-flight checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. manifest.yaml exists and is valid YAML
2. all required fields are present
3. Docker image exists locally
4. Nginx port is free
5. nginx.conf syntax is valid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;deploy&lt;/code&gt; command regenerates the config files, starts the Docker Compose stack, and waits until &lt;code&gt;/healthz&lt;/code&gt; confirms that the application is healthy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Promote
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;promote canary&lt;/code&gt; command updates &lt;code&gt;manifest.yaml&lt;/code&gt;, regenerates the compose file, restarts only the service container, and confirms the mode through &lt;code&gt;/healthz&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;promote stable&lt;/code&gt; command switches the deployment back to stable mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 4B: Adding Observability — The Eyes
&lt;/h2&gt;

&lt;p&gt;Stage 4B required the API service to expose a &lt;code&gt;/metrics&lt;/code&gt; endpoint in Prometheus text format.&lt;/p&gt;

&lt;p&gt;I added metrics using &lt;code&gt;prometheus_client&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The application now tracks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http_requests_total
http_request_duration_seconds
app_uptime_seconds
app_mode
chaos_active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These metrics show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;request throughput
HTTP status codes
request latency
application uptime
current deployment mode
active chaos state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful metrics output includes values like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http_requests_total{method="GET",path="/healthz",status_code="200"}
http_request_duration_seconds_bucket
app_uptime_seconds
app_mode
chaos_active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gave SwiftDeploy the visibility it needed before making promotion decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Guardrails: OPA Policy Enforcement
&lt;/h2&gt;

&lt;p&gt;Stage 4B also added Open Policy Agent, also known as OPA.&lt;/p&gt;

&lt;p&gt;OPA became the policy decision engine. The important rule is that the CLI should not make the allow or deny decision by itself. The CLI collects data, sends it to OPA, and OPA returns a structured decision with reasons.&lt;/p&gt;

&lt;p&gt;The policies live inside the &lt;code&gt;policies/&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;policies/
├── infrastructure.rego
└── canary.rego
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each policy domain answers a different question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Policy
&lt;/h2&gt;

&lt;p&gt;The infrastructure policy answers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the host safe enough for deployment?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It denies deployment if:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disk free space is below 10GB
CPU load is above 2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the hard gate for deployment. If the host does not meet the required safety threshold, &lt;code&gt;swiftdeploy deploy&lt;/code&gt; fails before starting the stack.&lt;/p&gt;

&lt;p&gt;The CLI sends host data to OPA, including disk and CPU information. OPA then returns a decision like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision: ALLOW
- Infrastructure policy passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or a denial reason such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision: DENY
- Disk free 5.20GB is below required 10GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes failures clear to the operator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canary Safety Policy
&lt;/h2&gt;

&lt;p&gt;The canary policy answers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the canary safe enough to promote?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It denies promotion if:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error rate is above 1%
P99 latency is above 500ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before promotion, SwiftDeploy scrapes &lt;code&gt;/metrics&lt;/code&gt;, calculates the error rate and P99 latency, and sends that data to OPA.&lt;/p&gt;

&lt;p&gt;This prevents promoting a canary that is already showing unhealthy behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Policy Isolation Matters
&lt;/h2&gt;

&lt;p&gt;Policy isolation is important because each policy should own one responsibility.&lt;/p&gt;

&lt;p&gt;The infrastructure policy only checks host safety.&lt;/p&gt;

&lt;p&gt;The canary policy only checks application safety before promotion.&lt;/p&gt;

&lt;p&gt;This means a change to the canary policy does not require changing the infrastructure policy. Each domain owns one question and one set of data.&lt;/p&gt;

&lt;p&gt;This also makes debugging easier. The CLI can show which policy passed or failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Policy Compliance:
- Infrastructure: PASS
- Canary: PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Policy Compliance:
- Infrastructure: PASS
- Canary: FAIL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the reason for blocking a deployment or promotion clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  No OPA Leakage
&lt;/h2&gt;

&lt;p&gt;OPA is added as a sidecar container in Docker Compose, but it is not routed through Nginx.&lt;/p&gt;

&lt;p&gt;The OPA service is bound to localhost only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:8181:8181"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the CLI can reach OPA, but users coming through the Nginx port cannot access the OPA API.&lt;/p&gt;

&lt;p&gt;That satisfies the no-leakage requirement because Nginx only forwards user traffic to the application service, not to the policy engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gated Deploy Flow
&lt;/h2&gt;

&lt;p&gt;The new deploy flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python swiftdeploy deploy
      |
      v
generate config files
      |
      v
start OPA sidecar
      |
      v
collect host stats
      |
      v
send input to OPA infrastructure policy
      |
      v
deploy only if OPA allows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful deploy shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPA is reachable by swiftdeploy CLI

Policy domain: infrastructure
Decision: ALLOW
- Infrastructure policy passed

OK Stack is healthy --&amp;gt; http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gated Promote Flow
&lt;/h2&gt;

&lt;p&gt;The new promote flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python swiftdeploy promote canary
      |
      v
start/check OPA
      |
      v
scrape /metrics
      |
      v
calculate error rate and P99 latency
      |
      v
send input to OPA canary policy
      |
      v
promote only if OPA allows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful canary promotion shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Policy domain: canary
Decision: ALLOW
- Canary safety policy passed

OK Mode confirmed: canary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The canary response also includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-Mode: canary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it easy to confirm that the service is really running in canary mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Chaos: Testing Slow and Error States
&lt;/h2&gt;

&lt;p&gt;The API includes a &lt;code&gt;/chaos&lt;/code&gt; endpoint that is active only in canary mode.&lt;/p&gt;

&lt;p&gt;It supports slow mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also supports error mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Slow mode delays responses. Error mode causes some requests to return HTTP 500 responses.&lt;/p&gt;

&lt;p&gt;This makes it possible to test whether the metrics endpoint, status command, and canary policy can detect unhealthy behavior.&lt;/p&gt;

&lt;p&gt;Example request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/chaos &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;mode&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:0.5}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After injecting chaos, repeated requests increase the error count in &lt;code&gt;/metrics&lt;/code&gt;. Then &lt;code&gt;python swiftdeploy status&lt;/code&gt; can show a higher error rate or latency.&lt;/p&gt;

&lt;p&gt;Example status view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requests: 30
Error Rate: 5.0%
P99 Latency: 700ms

Policy Compliance:
- Infrastructure: PASS
- Canary: FAIL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the purpose of canary safety. The system should detect the failure before allowing promotion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Status Command
&lt;/h2&gt;

&lt;p&gt;I added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The status command scrapes &lt;code&gt;/metrics&lt;/code&gt;, calculates live request statistics, checks policy compliance, and appends every scrape to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;history.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command displays information like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requests: 8.0
Error Rate: 0.0%
P99 Latency: 75.0ms

Policy Compliance:
- Infrastructure: PASS
- Canary: PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the operator a terminal dashboard for the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit Command
&lt;/h2&gt;

&lt;p&gt;I also added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command reads &lt;code&gt;history.jsonl&lt;/code&gt; and generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;audit_report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The audit report is written in GitHub Flavored Markdown and includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deployment timeline
policy checks
mode changes
status scrapes
policy violations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# SwiftDeploy Audit Report&lt;/span&gt;

&lt;span class="gu"&gt;## Timeline&lt;/span&gt;

| Time | Event | Summary |
|---|---|---|
| 2026-05-06T23:00:16+00:00 | deploy | Deploy in stable mode |
| 2026-05-06T23:07:36+00:00 | mode_change | canary -&amp;gt; stable |

&lt;span class="gu"&gt;## Policy Violations&lt;/span&gt;

No policy violations recorded.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the project traceability. Instead of only knowing the current state, I can review what happened during the deployment and testing process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Journey: Problems I Found and Fixed
&lt;/h2&gt;

&lt;p&gt;While testing Stage 4B, I encountered some realistic DevOps issues. These issues helped me understand the importance of testing, logs, and container permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Docker Desktop Was Not Running
&lt;/h3&gt;

&lt;p&gt;At one point, Docker commands failed because Docker Desktop was not running.&lt;/p&gt;

&lt;p&gt;The error showed that the Docker API could not be reached.&lt;/p&gt;

&lt;p&gt;The fix was to open Docker Desktop, wait until the daemon was running, and then rebuild the image.&lt;/p&gt;

&lt;p&gt;This reminded me that local DevOps workflows depend on the container runtime being available before running Docker commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. PowerShell Tried to Run Python Code
&lt;/h3&gt;

&lt;p&gt;While adding the &lt;code&gt;/metrics&lt;/code&gt; route, I accidentally pasted Python code directly into PowerShell.&lt;/p&gt;

&lt;p&gt;PowerShell tried to interpret lines like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/metrics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as PowerShell commands, which caused parser errors.&lt;/p&gt;

&lt;p&gt;The fix was to write the full Python file using &lt;code&gt;Set-Content&lt;/code&gt; and a PowerShell here-string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="sh"&gt;@'
# Python code here
'@&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;app\main.py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;UTF8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made the update safer and avoided broken copy-and-paste edits.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Missing Canary Policy
&lt;/h3&gt;

&lt;p&gt;During implementation, I created the infrastructure policy first. Later, I added the canary policy separately.&lt;/p&gt;

&lt;p&gt;The canary policy was important because Stage 4B required at least two separate Rego policies. One policy handles infrastructure safety, while the other handles canary safety.&lt;/p&gt;

&lt;p&gt;This improved the policy design and matched the requirement that policy domains should be separated.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Nginx Was Restarting Because of Dropped Capabilities
&lt;/h3&gt;

&lt;p&gt;The most important issue I fixed was with Nginx.&lt;/p&gt;

&lt;p&gt;I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cap_drop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a security best practice, but Nginx still needed some capabilities during startup. The container kept restarting with this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chown("/var/cache/nginx/client_temp", 101) failed (1: Operation not permitted)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service container was healthy and OPA was running, but Nginx could not stay up. Because of that, &lt;code&gt;curl http://localhost:8080/healthz&lt;/code&gt; failed.&lt;/p&gt;

&lt;p&gt;The fix was to keep &lt;code&gt;cap_drop: ALL&lt;/code&gt;, but add back only the capabilities Nginx needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cap_add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NET_BIND_SERVICE&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CHOWN&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SETUID&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SETGID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this fix, the stack became healthy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OK Stack is healthy --&amp;gt; http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The containers also showed the correct state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx    Up
opa      Up on 127.0.0.1:8181
service  Up and healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a good lesson in balancing security and functionality. Dropping all capabilities is safe, but some containers need a small set of capabilities to start correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Verification
&lt;/h2&gt;

&lt;p&gt;After fixing the Nginx capability issue, I tested the complete flow again.&lt;/p&gt;

&lt;p&gt;The health endpoint worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/healthz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"stable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"uptime_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;2.85&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The metrics endpoint worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returned Prometheus metrics including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http_requests_total
http_request_duration_seconds
app_uptime_seconds
app_mode
chaos_active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The status command worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Policy Compliance:
- Infrastructure: PASS
- Canary: PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The audit command worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OK audit_report.md generated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nginx access logs also showed the required pipe-delimited format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-05-06T23:00:16+00:00 | 200 | 0.045s | 172.20.0.3:3000 | GET /healthz HTTP/1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Run the Project
&lt;/h2&gt;

&lt;p&gt;Clone the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/10Johnny/swiftdeploy-stage4a.git
&lt;span class="nb"&gt;cd &lt;/span&gt;swiftdeploy-stage4a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install CLI dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pyyaml jinja2 requests psutil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the service image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; 10johnny-swiftdeploy-stage4b:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate the infrastructure files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check health:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/healthz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Promote to canary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy promote canary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check canary header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the status dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate an audit report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tear down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python swiftdeploy teardown &lt;span class="nt"&gt;--clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repository Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;manifest.yaml
swiftdeploy
Dockerfile
README.md
app/
templates/
policies/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generated files are created in the root folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx.conf
docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Audit files include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;history.jsonl
audit_report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This project taught me that deployment automation is more than starting containers.&lt;/p&gt;

&lt;p&gt;I learned how to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generate infrastructure files from templates
use Docker Compose to manage multiple services
use Nginx as a reverse proxy
expose Prometheus-style metrics
use OPA for policy decisions
separate policy logic from CLI logic
build policy-gated deploy and promote flows
debug container permission issues
create audit reports from deployment history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The biggest lesson was that a deployment tool should provide safety, visibility, and traceability.&lt;/p&gt;

&lt;p&gt;A good deployment tool should not only ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can I start the containers?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should also ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the host healthy?
Is the canary safe?
What policy allowed this action?
What happened during deployment?
Can I prove it later?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SwiftDeploy Stage 4B helped me understand how observability and policy enforcement make deployments safer and more reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SwiftDeploy started in Stage 4A as a declarative deployment CLI. In Stage 4B, I extended it with metrics, OPA policy checks, canary safety, chaos testing, status monitoring, and audit reporting.&lt;/p&gt;

&lt;p&gt;The final result is a deployment tool that can generate its own infrastructure files, observe the running service, enforce safety rules, and keep a record of what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/10Johnny/swiftdeploy-stage4a" rel="noopener noreferrer"&gt;https://github.com/10Johnny/swiftdeploy-stage4a&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>nginx</category>
      <category>observability</category>
    </item>
  </channel>
</rss>
