惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Engineering at Meta
Engineering at Meta
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
MongoDB | Blog
MongoDB | Blog
U
Unit 42
B
Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
P
Privacy International News Feed
L
LINUX DO - 最新话题
博客园_首页
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
C
Cisco Blogs
博客园 - 【当耐特】
阮一峰的网络日志
阮一峰的网络日志
I
Intezer
罗磊的独立博客
MyScale Blog
MyScale Blog
Last Week in AI
Last Week in AI
A
About on SuperTechFans
G
GRAHAM CLULEY
Y
Y Combinator Blog
Microsoft Security Blog
Microsoft Security Blog
GbyAI
GbyAI
T
Threat Research - Cisco Blogs
P
Proofpoint News Feed
D
DataBreaches.Net
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
AWS News Blog
AWS News Blog
I
InfoQ
T
The Exploit Database - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 叶小钗
Project Zero
Project Zero

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Building SwiftDeploy: A Self-Writing Infrastructure Tool with OPA Policy Enforcement and Prometheus Observability
Hezekiah Umo · 2026-05-07 · via DEV Community

Building SwiftDeploy: A Self-Writing Infrastructure Tool with OPA Policy Enforcement and Prometheus Observability

Introduction

What if your deployment tool could refuse to deploy when your disk is full? What if it could block a canary promotion when error rates spike — automatically, based on policy — without a single hardcoded if statement in the CLI?

That's exactly what I built for Stage 4b of the HNG14 DevOps track. In this post I'll walk through the full journey: from a manifest-driven deployment engine to a policy-enforced, fully observable stack with a live terminal dashboard and audit trail.


The Architecture at a Glance

manifest.yaml  (single source of truth)
      |
      v
swiftdeploy CLI
      |
      +-- Jinja2 templates --> docker-compose.yml + nginx.conf
      |
      +-- OPA policy check --> allow / block + reason
      |
      v
Docker Compose Stack
  ├── app (FastAPI + /metrics)
  ├── nginx (public ingress on swiftdeploy-net)
  └── opa (isolated on opa-internal, queried via docker exec)

Enter fullscreen mode Exit fullscreen mode

The core principle: manifest.yaml is the only file a human ever edits. Everything else — config files, policy decisions, audit reports — is generated.


Stage 4a Recap: The Engine

In Stage 4a I built the foundation:

  • A manifest.yaml that describes the entire stack (image, port, mode, network)
  • A Python CLI (swiftdeploy) that reads the manifest and renders Jinja2 templates into docker-compose.yml and nginx.conf
  • Subcommands: init, validate, deploy, promote, teardown
  • A FastAPI service with /, /healthz, and /chaos endpoints
  • Canary/stable mode switching via promote

The key insight: the CLI never writes config by hand — it always renders from templates. Change one field in manifest.yaml, re-run init, and the entire stack config regenerates consistently.


Stage 4b: The Eyes and the Brain

Stage 4b adds three major capabilities:

  1. The Eyes — Prometheus /metrics endpoint
  2. The Brain — OPA policy sidecar enforcing deploy/promote gates
  3. The Memory — audit trail and report generation

1. Instrumentation: The /metrics Endpoint

The FastAPI service now exposes a /metrics endpoint in Prometheus text format. I implemented the metrics collector entirely in Python without any external library — just a middleware that intercepts every request and records it.

@app.middleware("http")
async def metrics_middleware(request: Request, call_next):
    start = time.time()
    response = await call_next(request)
    dur = time.time() - start
    if request.url.path != "/metrics":
        record_request(request.method, request.url.path,
                       response.status_code, dur)
    return response

Enter fullscreen mode Exit fullscreen mode

Five metrics are exposed:

Metric Type Description
http_requests_total counter Requests by method, path, status_code
http_request_duration_seconds histogram Latency with 11 standard buckets
app_uptime_seconds gauge Seconds since process start
app_mode gauge 0=stable, 1=canary
chaos_active gauge 0=none, 1=slow, 2=error

The histogram uses standard Prometheus buckets (0.005s through 10s) so P99 latency can be calculated from bucket counts — no extra libraries needed.


2. The Policy Sidecar: OPA

Why OPA?

The spec had a critical requirement: the CLI must not make any allow/deny decision itself. All decision logic lives exclusively in OPA. This is the separation of concerns that makes the system auditable and extensible — you can change policy without touching the CLI.

Isolation Architecture

OPA runs as a sidecar in Docker Compose but on a completely separate network from nginx:

networks:
  swiftdeploy-net:    # nginx + app live here
    driver: bridge
  opa-internal:       # OPA lives here, isolated
    driver: bridge

services:
  nginx:
    networks: [swiftdeploy-net]   # can NOT reach OPA
  opa:
    networks: [opa-internal]      # can NOT be reached via nginx

Enter fullscreen mode Exit fullscreen mode

This means there is zero path from the public port 8081 to the OPA API. The No "Leakage" requirement from the spec is satisfied architecturally, not just by configuration.

Domain-Isolated Policies

I wrote two completely independent Rego policies, each owning exactly one domain:

policies/infrastructure.rego — answers: Is this host safe to deploy onto?

package swiftdeploy.infrastructure

default allow := false

allow if { count(violations) == 0 }

violations contains msg if {
    input.disk_free_gb < data.infrastructure.min_disk_free_gb
    msg := sprintf("Disk free (%.1f GB) is below minimum threshold (%.1f GB)",
                   [input.disk_free_gb, data.infrastructure.min_disk_free_gb])
}

violations contains msg if {
    input.cpu_load > data.infrastructure.max_cpu_load
    msg := sprintf("CPU load (%.2f) exceeds maximum threshold (%.2f)",
                   [input.cpu_load, data.infrastructure.max_cpu_load])
}

Enter fullscreen mode Exit fullscreen mode

policies/canary.rego — answers: Is the canary safe to promote?

package swiftdeploy.canary

default allow := false

allow if { count(violations) == 0 }

violations contains msg if {
    input.error_rate_percent > data.canary.max_error_rate_percent
    msg := sprintf("Error rate (%.2f%%) exceeds maximum threshold (%.2f%%)",
                   [input.error_rate_percent, data.canary.max_error_rate_percent])
}

Enter fullscreen mode Exit fullscreen mode

Crucially, all threshold values live in policies/data.json — not in the Rego files:

{
  "infrastructure": {
    "min_disk_free_gb": 10.0,
    "max_cpu_load": 2.0,
    "min_mem_free_percent": 10.0
  },
  "canary": {
    "max_error_rate_percent": 1.0,
    "max_p99_latency_ms": 500
  }
}

Enter fullscreen mode Exit fullscreen mode

To change the disk threshold from 10GB to 20GB, you edit only data.json. The Rego files never need to change. This is the single source of truth for policy thresholds.

OPA Never Returns a Bare Boolean

Every OPA decision carries the reasoning behind it:

{
  "allow": false,
  "violations": [
    "Error rate (46.94%) exceeds maximum threshold (1.00%) over the observation window"
  ]
}

Enter fullscreen mode Exit fullscreen mode

The CLI surfaces this directly to the operator — no cryptic error codes, just a plain English explanation of why deployment was blocked.


3. The CLI: Gated Lifecycle

Pre-Deploy Check

Before bringing up the stack, swiftdeploy deploy collects host stats and sends them to OPA:

> swiftdeploy deploy
  Checking infrastructure policy...
  > Host -> disk: 328.6 GB | CPU: 0.20 | mem free: 37.4%
  + [OPA/INFRASTRUCTURE] Policy passed — proceeding
  > Bringing up the stack...
  + Stack healthy -> http://localhost:8081

Enter fullscreen mode Exit fullscreen mode

If I were to fill up the disk to below 10GB, the output would instead show:

  x [OPA/INFRASTRUCTURE] Policy FAILED — blocked
              - Disk free (3.2 GB) is below minimum threshold (10.0 GB)
  x Deployment blocked by policy.

Enter fullscreen mode Exit fullscreen mode

Pre-Promote Check (The Chaos Test)

This is where it gets interesting. Before promoting a canary to stable, the CLI scrapes /metrics, calculates error rate and P99 latency, and sends them to OPA.

I injected an 80% error rate using the chaos endpoint:

Invoke-RestMethod -Method Post -Uri http://localhost:8081/chaos `
  -ContentType "application/json" `
  -Body '{"mode":"error","rate":0.8}'

Enter fullscreen mode Exit fullscreen mode

Then tried to promote:

> swiftdeploy promote stable
  Checking canary health policy...
  > Canary -> error rate: 46.94% | P99: 10 ms
  x [OPA/CANARY] Policy FAILED — blocked
              - Error rate (46.94%) exceeds maximum threshold (1.00%) over the observation window
  x Promotion blocked — canary is not healthy enough.

Enter fullscreen mode Exit fullscreen mode

The canary policy gate caught a 47x threshold breach and blocked the promotion. This is exactly the kind of automated safety net that prevents bad canaries from reaching production.


4. The Status Dashboard

swiftdeploy status runs a live-refreshing terminal dashboard that scrapes /metrics every 5 seconds:

------------------------------------------------------------
  SwiftDeploy Status              2026-05-07 09:54:00
------------------------------------------------------------

  Mode: canary   Chaos: none   Uptime: 3420s

  Metric                           Value
  --------------------------------------------
  Throughput (req/s)               2.40
  Error Rate                       0.00%
  P99 Latency                      10 ms

  Policy Compliance
  --------------------------------------------
  [+]  Infra: Disk >= 10 GB
  [+]  Infra: CPU load <= 2.0
  [+]  Infra: Mem free >= 10%
  [+]  Canary: Error rate <= 1%
  [+]  Canary: P99 latency <= 500ms

  Refreshing every 5s — Ctrl+C to exit

Enter fullscreen mode Exit fullscreen mode

Every scrape is appended to history.jsonl — a newline-delimited JSON file that forms the audit trail.


5. The Audit Report

swiftdeploy audit parses history.jsonl and generates audit_report.md with four sections:

  • Timeline — every deploy, promote, teardown, and policy check with timestamps
  • Mode Changes — when the stack switched between stable and canary
  • Policy Violations — every time a check failed, with the full violation message
  • Metrics Summary — min/max/avg of error rate, P99 latency, and throughput

The report renders perfectly as GitHub Flavored Markdown.


The Windows Challenge: OPA Port Binding

This section is for anyone running Docker Desktop on Windows — I hit a wall that took significant debugging to solve.

The problem: OPA's port 8181 was correctly configured in docker-compose.yml as "0.0.0.0:8181:8181", and docker inspect confirmed the binding was set. But netstat showed nothing listening on 8181, and curl http://localhost:8181/health failed with connection refused.

This is a known Docker Desktop + WSL2 bug where port forwarding from WSL2 containers to the Windows host is unreliable for certain port ranges.

The solution: Instead of querying OPA via HTTP from the host, I switched to docker exec with the OPA CLI directly inside the container:

cmd = (
    f'docker exec -i {opa_container} opa eval '
    f'--data /policies '
    f'--stdin-input '
    f'--format json '
    f'"{opa_path}"'
)
r = subprocess.run(cmd, shell=True, input=input_json,
                   capture_output=True, text=True, timeout=10)

Enter fullscreen mode Exit fullscreen mode

This approach:

  • Bypasses host port binding entirely
  • Works identically on Linux, Mac, and Windows
  • Is actually more reliable — no network stack involved at all
  • Satisfies the isolation requirement (OPA is still on its own network, nginx can't reach it)

The lesson: when Docker networking misbehaves on Windows, docker exec is your escape hatch.


Lessons Learned

1. Separation of concerns is worth the complexity. Having OPA own all policy decisions and the CLI own only orchestration made both parts easier to test and reason about independently.

2. Thresholds in data, logic in code. Putting OPA thresholds in data.json instead of hardcoding them in Rego files means ops teams can tune policy without touching code or redeploying anything.

3. Every failure mode needs a distinct message. The spec said "every distinct failure mode must produce a different, human-readable outcome." I ended up with five distinct OPA error states (unreachable, timeout, malformed JSON, undefined result, policy failed) each producing a clear, actionable message.

4. Platform-specific bugs are real. The Docker Desktop port binding issue cost hours. The fix (docker exec) is actually cleaner than HTTP anyway — but you only find that out after hitting the wall.

5. The audit trail is free if you build it from the start. Appending JSON to history.jsonl on every event costs almost nothing at runtime but provides complete forensic history for free.


Conclusion

SwiftDeploy Stage 4b is a deployment tool that can see (metrics), think (OPA policy), remember (audit trail), and refuse (policy gates). The entire stack — from /metrics to audit_report.md — is driven by a single manifest.yaml.

The code is available at: [your GitHub repo URL here]

If you're building something similar, the key takeaways are: isolate your policy engine, never return bare booleans from policy checks, and always give operators a human-readable reason when you block them.


Built for HNG14 DevOps Track — Stage 4b