<?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: Ivan Annovazzi</title>
    <description>The latest articles on Forem by Ivan Annovazzi (@ivannovazzi).</description>
    <link>https://forem.com/ivannovazzi</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%2F3789934%2Ffb65f389-caa7-4b5e-9a49-9dd4c8e4c1c0.jpeg</url>
      <title>Forem: Ivan Annovazzi</title>
      <link>https://forem.com/ivannovazzi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ivannovazzi"/>
    <language>en</language>
    <item>
      <title>I Audited My Team's .env Practices. Here's What I Found.</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Thu, 26 Mar 2026 21:04:25 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/i-audited-my-teams-env-practices-heres-what-i-found-k56</link>
      <guid>https://forem.com/ivannovazzi/i-audited-my-teams-env-practices-heres-what-i-found-k56</guid>
      <description>&lt;p&gt;Last month I did something uncomfortable: I spent a Friday afternoon auditing how my team actually handles secrets. Not how we &lt;em&gt;say&lt;/em&gt; we handle them. How we &lt;em&gt;actually&lt;/em&gt; do it.&lt;/p&gt;

&lt;p&gt;I checked Slack history, git logs, CI configs, and local machines. What I found wasn't a disaster — it was worse. It was &lt;em&gt;normal&lt;/em&gt;. The kind of normal that every team thinks is fine until it isn't.&lt;/p&gt;

&lt;p&gt;Here's exactly what I found, and what we did about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The audit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Team size&lt;/strong&gt;: 5 developers, 3 services, 2 environments (staging + production).&lt;/p&gt;

&lt;p&gt;I looked at five things:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Where do .env files live?
&lt;/h3&gt;

&lt;p&gt;I asked everyone to run &lt;code&gt;find ~ -name ".env" -not -path "*/node_modules/*"&lt;/code&gt; on their machines.&lt;/p&gt;

&lt;p&gt;Combined results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;23 &lt;code&gt;.env&lt;/code&gt; files across 5 laptops&lt;/li&gt;
&lt;li&gt;7 of them contained production credentials&lt;/li&gt;
&lt;li&gt;2 developers had &lt;code&gt;.env&lt;/code&gt; files for projects they left &lt;em&gt;months&lt;/em&gt; ago&lt;/li&gt;
&lt;li&gt;1 file had a Stripe live key and a database URL on the same line — copy-paste artifact from Slack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The problem isn't that .env files exist. It's that they accumulate.&lt;/strong&gt; Nobody cleans them up. Nobody knows how many copies are out there.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. How do new devs get credentials?
&lt;/h3&gt;

&lt;p&gt;I searched our Slack for "env", "password", "key", "token", and "secret".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;14 messages containing actual credentials in the last 6 months&lt;/li&gt;
&lt;li&gt;3 of those were in a &lt;em&gt;public&lt;/em&gt; channel (we caught that quickly, at least)&lt;/li&gt;
&lt;li&gt;The onboarding doc said "ask someone for the .env" — no specifics about who, which values, or which environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The worst part: Slack retains message history. Those credentials are sitting in Slack's servers &lt;em&gt;forever&lt;/em&gt; unless someone manually deletes each one.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Are secrets in git history?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Quick check for common patterns&lt;/span&gt;
git log &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"(API_KEY|SECRET|PASSWORD|TOKEN)="&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two hits. Both were "temporary" commits from months ago that were reverted — but the values are still in the history. &lt;code&gt;git revert&lt;/code&gt; doesn't erase data. Anyone who clones the repo gets those secrets.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. How does CI/CD get secrets?
&lt;/h3&gt;

&lt;p&gt;Our GitHub Actions workflows used repository secrets. Fine in principle, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 people had admin access to the repo (and could read all secrets)&lt;/li&gt;
&lt;li&gt;No rotation schedule — some secrets hadn't been updated in over a year&lt;/li&gt;
&lt;li&gt;No audit log of who accessed what&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. What happens when someone leaves?
&lt;/h3&gt;

&lt;p&gt;We'd had one departure in the past year. Checked the offboarding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub access revoked ✓&lt;/li&gt;
&lt;li&gt;AWS IAM removed ✓&lt;/li&gt;
&lt;li&gt;Secrets rotated ✗&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every API key, database password, and service token that developer had access to was still active. For &lt;em&gt;four months&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scorecard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Practice&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Secrets encrypted at rest&lt;/td&gt;
&lt;td&gt;✗ (plaintext .env files)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets shared securely&lt;/td&gt;
&lt;td&gt;✗ (Slack DMs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets rotated on departure&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets rotated on schedule&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git history clean&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Least-privilege access&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trail&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero out of seven. And we're not a careless team. We write tests. We do code review. We just never formalized secrets management because it always felt like a "later" problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed
&lt;/h2&gt;

&lt;p&gt;We didn't do everything at once. Here's the order that worked for us:&lt;/p&gt;

&lt;h3&gt;
  
  
  Week 1: Stop the bleeding
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rotated every secret&lt;/strong&gt; that the departed developer had access to. This took half a day and was painful — but it's the highest-impact thing you can do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deleted secrets from Slack.&lt;/strong&gt; Searched, found them, deleted each message. Then pinned a message in #engineering: "Never paste credentials here. Not even temporarily."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scrubbed git history&lt;/strong&gt; using &lt;a href="https://rtyley.github.io/bfg-repo-cleaner/" rel="noopener noreferrer"&gt;BFG Repo-Cleaner&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bfg &lt;span class="nt"&gt;--replace-text&lt;/span&gt; passwords.txt repo.git
git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt;
git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--aggressive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then force-pushed and had everyone re-clone. Annoying but necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Week 2: Centralize secrets
&lt;/h3&gt;

&lt;p&gt;We evaluated three options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HashiCorp Vault&lt;/strong&gt; — powerful, but needs dedicated ops. We're 5 people.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Doppler&lt;/strong&gt; — solid, but more surface area than we needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KeyEnv&lt;/strong&gt; — CLI-first, minimal setup, fit our workflow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We went with &lt;a href="https://keyenv.dev" rel="noopener noreferrer"&gt;KeyEnv&lt;/a&gt; because the migration was zero-friction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Import existing .env&lt;/span&gt;
keyenv push

&lt;span class="c"&gt;# From now on, every developer does:&lt;/span&gt;
keyenv pull

&lt;span class="c"&gt;# Or skip the file entirely:&lt;/span&gt;
keyenv run &lt;span class="nt"&gt;--&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No code changes. Apps still read &lt;code&gt;process.env&lt;/code&gt;. The difference is that secrets come from an encrypted store instead of a file someone pasted in Slack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Week 3: Lock down access
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up per-environment permissions. Junior devs get &lt;code&gt;development&lt;/code&gt; only. Production secrets are admin-restricted.&lt;/li&gt;
&lt;li&gt;Created service tokens for CI/CD — scoped, rotatable, auditable.&lt;/li&gt;
&lt;li&gt;Established a quarterly rotation schedule (calendar reminder, nothing fancy).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Week 4: Wrote it down
&lt;/h3&gt;

&lt;p&gt;Added a &lt;code&gt;SECRETS.md&lt;/code&gt; to each repo:&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="gu"&gt;## Secrets Management&lt;/span&gt;

This project uses KeyEnv for secrets management.

&lt;span class="gu"&gt;### Setup&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Get invited to the team: ask in #engineering
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="sb"&gt;`keyenv pull`&lt;/span&gt; to get development secrets
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="sb"&gt;`keyenv run -- npm start`&lt;/span&gt; to run with injected secrets

&lt;span class="gu"&gt;### Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never commit secrets to git
&lt;span class="p"&gt;-&lt;/span&gt; Never share secrets via Slack, email, or docs
&lt;span class="p"&gt;-&lt;/span&gt; Rotate secrets when a team member leaves
&lt;span class="p"&gt;-&lt;/span&gt; Production secrets require admin access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run your own audit
&lt;/h2&gt;

&lt;p&gt;You can do this in an afternoon. Here's the checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Search Slack for credentials (&lt;code&gt;password&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt;, &lt;code&gt;token&lt;/code&gt;, &lt;code&gt;secret&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Count &lt;code&gt;.env&lt;/code&gt; files across developer machines&lt;/li&gt;
&lt;li&gt;[ ] Check git history for leaked values&lt;/li&gt;
&lt;li&gt;[ ] Review who has access to CI/CD secrets&lt;/li&gt;
&lt;li&gt;[ ] Verify secrets were rotated after last departure&lt;/li&gt;
&lt;li&gt;[ ] Check if any service accounts use shared credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll probably find what I found: not a catastrophe, but a pile of small risks that compound over time.&lt;/p&gt;

&lt;p&gt;The fix doesn't require enterprise tooling. It requires &lt;em&gt;deciding&lt;/em&gt; that secrets management matters, then picking any system that's better than Slack + .env files.&lt;/p&gt;




&lt;p&gt;Have you ever audited your team's secret practices? I'm curious what others have found — especially the "normal" stuff that everyone does but nobody talks about.&lt;/p&gt;

</description>
      <category>security</category>
      <category>productivity</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Moveet: Building a City-Agnostic Fleet Simulator with Real Road Networks and Live Traffic</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Thu, 19 Mar 2026 11:06:15 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/moveet-building-a-city-agnostic-fleet-simulator-with-real-road-networks-and-live-traffic-2nkh</link>
      <guid>https://forem.com/ivannovazzi/moveet-building-a-city-agnostic-fleet-simulator-with-real-road-networks-and-live-traffic-2nkh</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/ivannovazzi/moveet" rel="noopener noreferrer"&gt;Moveet&lt;/a&gt; is an open-source, real-time vehicle fleet simulator built in TypeScript. It runs vehicles on actual road networks with A* pathfinding, realistic traffic modeling, and a custom D3-based map renderer — no Leaflet, no Mapbox, no tile provider.&lt;/p&gt;

&lt;p&gt;The latest release makes the simulator fully city-agnostic. Point it at any city, and it builds a routable graph from OpenStreetMap data, complete with congestion modeling, geofencing, and a live traffic overlay. Here is what went into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  From OSM Data to a Routable Graph
&lt;/h2&gt;

&lt;p&gt;A CLI pipeline (&lt;code&gt;apps/network&lt;/code&gt;) handles the full OpenStreetMap ingestion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev &lt;span class="nt"&gt;--&lt;/span&gt; prepare nairobi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads a country PBF from Geofabrik, clips a bounding box with osmium (via Docker), filters to drivable road classes, exports GeoJSON, and validates the topology. Cached after first run. A &lt;code&gt;regions.json&lt;/code&gt; manifest covers major cities globally — switching cities is a one-argument change.&lt;/p&gt;

&lt;p&gt;The builder prunes disconnected components, parses OSM tags like lane counts, surface quality, roundabouts, access restrictions, and traffic signals, and handles edge cases like reversed one-ways and near-duplicate nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traffic Realism
&lt;/h2&gt;

&lt;p&gt;The A* engine applies a BPR (Bureau of Public Roads) congestion function when computing route costs. Road capacity is derived from OSM lane data. As vehicles accumulate on a segment, the routing cost grows and traffic naturally shifts to alternative corridors — the same principle used in professional traffic assignment models.&lt;/p&gt;

&lt;p&gt;Additional realism layers include surface quality speed factors, traffic signal intersection delays, and an incident-aware route cache keyed by active incident state.&lt;/p&gt;

&lt;p&gt;A simulation clock drives time-varying demand — rush hour amplification, night reduction — and the server broadcasts per-edge congestion snapshots at a regular interval. The UI renders them with a Google Maps-style color ramp (green through red), with only roads carrying actual traffic colored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing Quality
&lt;/h2&gt;

&lt;p&gt;The A* router now handles OSM turn restrictions, enforces forward-only roundabout traversal with speed reduction, filters out private and restricted-access roads, and computes an admissible heuristic from the network's actual speed range rather than a hardcoded constant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Geofencing
&lt;/h2&gt;

&lt;p&gt;Draw polygons on the map, name them, assign types and colors. The server runs ray-casting checks every tick and broadcasts enter/exit events over WebSocket. Full REST CRUD, an alerts panel, and per-zone toggle are included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vehicle Types and Trails
&lt;/h2&gt;

&lt;p&gt;Car, truck, motorcycle, ambulance, and bus — each with distinct speed profiles, acceleration curves, road restrictions, and special behaviors. Fleet composition is configurable at start time.&lt;/p&gt;

&lt;p&gt;Each vehicle also leaves a fading breadcrumb trail on the map, stored in a circular buffer and rendered as an opacity gradient. Trail length is adjustable.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket Subscribe Filters
&lt;/h2&gt;

&lt;p&gt;Clients can filter their WebSocket feed by fleet, vehicle type, or geographic bounding box:&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"subscribe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"fleetId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fleet-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"vehicleType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"truck"&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;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;Backwards-compatible — clients without a filter receive everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  UI Overhaul
&lt;/h2&gt;

&lt;p&gt;All interactive components were migrated to &lt;a href="https://react-spectrum.adobe.com/react-aria/" rel="noopener noreferrer"&gt;React Aria&lt;/a&gt; for proper accessibility. The visual layer received a glassmorphism theme with tiered depth, a custom fuzzy search with match highlighting, and tighter panel proportions.&lt;/p&gt;

&lt;p&gt;Under the hood: a fleet analytics dashboard with sparkline KPIs, structured logging with correlation IDs, a health endpoint, and CI-enforced test coverage across all workspaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ivannovazzi/moveet.git
&lt;span class="nb"&gt;cd &lt;/span&gt;moveet &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dashboard at &lt;code&gt;localhost:5012&lt;/code&gt;. MIT licensed. &lt;a href="https://github.com/ivannovazzi/moveet" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Lots of new features coming soon. Performance boosts, traffic simulations, vehicle types and more.</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Mon, 16 Mar 2026 10:44:25 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/lots-of-new-features-coming-soon-performance-boosts-traffic-simulations-vehicle-types-and-more-1gpd</link>
      <guid>https://forem.com/ivannovazzi/lots-of-new-features-coming-soon-performance-boosts-traffic-simulations-vehicle-types-and-more-1gpd</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/ivannovazzi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3789934%2Ffb65f389-caa7-4b5e-9a49-9dd4c8e4c1c0.jpeg" alt="ivannovazzi"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/ivannovazzi/moveet-v2-incidents-recording-replay-icon-rail-ui-and-500-tests-5fl5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Moveet: incidents, recording &amp;amp; replay, fleet management, dispatch flows, and 500+ tests&lt;/h2&gt;
      &lt;h3&gt;Ivan Annovazzi ・ Mar 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Moveet: incidents, recording &amp; replay, fleet management, dispatch flows, and 500+ tests</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Mon, 16 Mar 2026 10:38:17 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/moveet-v2-incidents-recording-replay-icon-rail-ui-and-500-tests-5fl5</link>
      <guid>https://forem.com/ivannovazzi/moveet-v2-incidents-recording-replay-icon-rail-ui-and-500-tests-5fl5</guid>
      <description>&lt;p&gt;A few months ago I shipped the first version of &lt;a href="https://github.com/ivannovazzi/moveet" rel="noopener noreferrer"&gt;Moveet&lt;/a&gt; — an open-source real-time vehicle fleet simulator that runs vehicles on actual road networks using A* pathfinding, streams positions over WebSocket, and renders everything in a custom D3 SVG map without touching Leaflet or Mapbox.&lt;/p&gt;

&lt;p&gt;Since then a lot has landed. Here's what's new.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvo7i698aze0953cbgrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvo7i698aze0953cbgrm.png" alt=" " width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What shipped
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🚦 Fleet management
&lt;/h3&gt;

&lt;p&gt;Vehicles can now be grouped into named, colour-coded fleets. You create a fleet, assign vehicles to it, and the UI colours routes and markers per fleet. Useful when you want to simulate multiple operators or companies sharing the same road network.&lt;/p&gt;

&lt;p&gt;REST API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST   /fleets
GET    /fleets
DELETE /fleets/:id
POST   /fleets/:id/assign
POST   /fleets/:id/unassign
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fleet events are broadcast over WebSocket in real time (&lt;code&gt;fleet:created&lt;/code&gt;, &lt;code&gt;fleet:deleted&lt;/code&gt;, &lt;code&gt;fleet:assigned&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚠️ Incidents and dynamic rerouting
&lt;/h3&gt;

&lt;p&gt;You can now drop a road incident anywhere on the network. Every vehicle currently routing through the affected segment gets rerouted live — A* reruns from their current position, routing around the blocked edge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST   /incidents          # create incident, triggers rerouting
GET    /incidents          # list active
DELETE /incidents/:id      # clear it, vehicles return to normal paths
POST   /incidents/random   # random incident for testing
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Incident markers appear on the map. The &lt;code&gt;vehicle:rerouted&lt;/code&gt; WebSocket event fires for each affected vehicle.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎬 Session recording and replay
&lt;/h3&gt;

&lt;p&gt;Every simulation session can be recorded to a timestamped NDJSON file. The format is a header line followed by one event per line — direction assignments, vehicle snapshots, incidents, everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST   /recording/start
POST   /recording/stop
GET    /recordings
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replay loads any recording file and plays it back with full control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST   /replay/start    { "file": "path/to/recording.ndjson" }
POST   /replay/pause
POST   /replay/resume
POST   /replay/stop
POST   /replay/seek     { "timestamp": 12000 }
POST   /replay/speed    { "speed": 2 }
GET    /replay/status
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playback supports 1×, 2×, and 4× speed. The UI has an interpolated progress bar that advances smoothly between server ticks, so it doesn't jump every 500 ms.&lt;/p&gt;




&lt;h3&gt;
  
  
  🖥 UI redesign: icon rail + panel sidebar
&lt;/h3&gt;

&lt;p&gt;The controls were previously a floating overlay. They're now an icon rail on the left edge — a vertical strip of icon buttons that each toggle a panel.&lt;/p&gt;

&lt;p&gt;Panels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Icon&lt;/th&gt;
&lt;th&gt;Panel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;��&lt;/td&gt;
&lt;td&gt;Vehicles — list, filter, select&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗂&lt;/td&gt;
&lt;td&gt;Fleets — create, assign, colour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;Incidents — active list with badge count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⏺&lt;/td&gt;
&lt;td&gt;Recordings — start/stop/browse/replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👁&lt;/td&gt;
&lt;td&gt;Visibility — toggle map layers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡&lt;/td&gt;
&lt;td&gt;Speed — simulation speed controls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚙️&lt;/td&gt;
&lt;td&gt;Adapter — hot-swap source/sink plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The bottom dock holds live simulation controls (play/stop/reset/record) and collapses to a replay transport bar during playback.&lt;/p&gt;

&lt;p&gt;All panel components are built from a shared set of primitives (&lt;code&gt;PanelShell&lt;/code&gt;, &lt;code&gt;PanelHeader&lt;/code&gt;, &lt;code&gt;PanelSection&lt;/code&gt;) and a unified theme token set, so every panel looks consistent without per-component ad-hoc styling.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ Test coverage: from 410 to 502 tests
&lt;/h3&gt;

&lt;p&gt;The simulator test suite grew significantly. New files added:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;What it covers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rateLimiter.test.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Window enforcement, 429 responses, per-IP tracking, cleanup interval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;helpers.test.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;calculateBearing&lt;/code&gt;, &lt;code&gt;interpolatePosition&lt;/code&gt;, &lt;code&gt;calculateDistance&lt;/code&gt;, &lt;code&gt;nonCircularRouteEdges&lt;/code&gt;, &lt;code&gt;estimateRouteDuration&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serializer.test.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;serializeVehicle&lt;/code&gt; with and without fleet assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;config.test.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;verifyConfig&lt;/code&gt; — missing file, port range, speed ordering, all numeric constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SimulationController.test.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full lifecycle: &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt;, &lt;code&gt;getStatus&lt;/code&gt;, &lt;code&gt;setOptions&lt;/code&gt;, &lt;code&gt;getVehicles&lt;/code&gt;, &lt;code&gt;getInterval&lt;/code&gt;, all replay methods&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern for testing &lt;code&gt;SimulationController&lt;/code&gt; is worth noting: construction stubs &lt;code&gt;VehicleManager.prototype.setRandomDestination&lt;/code&gt; to prevent A* calls on the tiny test-network fixture, then restores it. Replay tests write a minimal NDJSON file to a temp dir before each test. Everything cleans up in &lt;code&gt;afterEach&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  📡 WebSocket: 100 ms batching + backpressure
&lt;/h3&gt;

&lt;p&gt;Vehicle position updates are now batched at 100 ms before broadcast. If a client's write buffer is backed up, the broadcaster skips it rather than growing an unbounded queue. This makes the simulator usable with 50–100 vehicles without the connection degrading.&lt;/p&gt;

&lt;p&gt;All event types the simulator broadcasts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vehicles          – batched position array (100ms window)
status            – simulation state (running / ready / interval)
options           – current StartOptions
heatzones         – HeatZoneFeature[]
direction         – active dispatch assignment
waypoint:reached  – vehicle reached a waypoint
route:completed   – vehicle completed its full route
reset             – simulation was reset
fleet:created / fleet:deleted / fleet:assigned
incident:created / incident:cleared
vehicle:rerouted  – live A* reroute triggered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔧 CI improvements
&lt;/h3&gt;

&lt;p&gt;The GitHub Actions workflow previously ran &lt;code&gt;npm ci&lt;/code&gt; three times in parallel — once per job. It now has a dedicated &lt;code&gt;setup&lt;/code&gt; job that installs once and caches &lt;code&gt;node_modules&lt;/code&gt; keyed on &lt;code&gt;package-lock.json&lt;/code&gt;. Lint, test, and build jobs restore from cache. The build job also caches &lt;code&gt;.turbo/&lt;/code&gt; with a per-commit key and branch-level restore keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture diagram (updated)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────┐
│  apps/ui                             │
│  React 19 · D3 7 · Vite · TS 5.8    │
│  Mercator SVG map, 1×–15× zoom       │
│  Icon rail · Panel sidebar · Dock    │
└──────────────┬───────────────────────┘
               │ REST + WebSocket
               ▼
┌──────────────────────────────────────┐
│  apps/simulator                      │
│  Express 4 · ws 8 · Turf.js 7       │
│  GeoJSON graph · A* · LRU cache      │
│  WS broadcaster · 100ms batching     │
└──────────────┬───────────────────────┘
               │ GET /vehicles · POST /sync
               ▼
┌──────────────────────────────────────┐
│  apps/adapter (optional)             │
│  Source plugins: static / graphql /  │
│  rest / mysql / postgres             │
│  Sink plugins: console / graphql /   │
│  rest / redpanda / redis / webhook   │
└──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/ivannovazzi/moveet/main/docker-compose.ghcr.yml
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.ghcr.yml up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:5012&lt;/code&gt;. No config, no API keys.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ivannovazzi/moveet" rel="noopener noreferrer"&gt;github.com/ivannovazzi/moveet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the A* implementation, the D3 renderer, or the recording format in the comments.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Your .env Files Are a Security Incident Waiting to Happen</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Wed, 11 Mar 2026 20:38:12 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/your-env-files-are-a-security-incident-waiting-to-happen-134b</link>
      <guid>https://forem.com/ivannovazzi/your-env-files-are-a-security-incident-waiting-to-happen-134b</guid>
      <description>&lt;p&gt;You've done it. I've done it. We've all done it.&lt;/p&gt;

&lt;p&gt;Pasted a &lt;code&gt;.env&lt;/code&gt; file into Slack. Committed &lt;code&gt;DATABASE_URL&lt;/code&gt; to a repo "just temporarily." Spent 45 minutes debugging prod because someone was running against staging credentials and nobody realized it. Onboarded a new dev by saying "ask Sarah for the &lt;code&gt;.env&lt;/code&gt; file" — and Sarah sends it over Gmail.&lt;/p&gt;

&lt;p&gt;This is how secrets get leaked. Not through sophisticated attacks. Through completely normal, everyday developer workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KeyEnv&lt;/strong&gt; is our answer to this. And today, we're officially launching with a batch of updates that make it genuinely ready for teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  What KeyEnv Actually Does
&lt;/h2&gt;

&lt;p&gt;KeyEnv replaces your scattered &lt;code&gt;.env&lt;/code&gt; files with a secure, centralized workflow that fits into how you already work — without changing how your apps consume environment variables.&lt;/p&gt;

&lt;p&gt;Secrets are encrypted at rest with AES-256-GCM, scoped per project and per environment (dev, staging, production), and access is managed per team member. There's a full audit trail. You can rotate secrets, compare environments, and view version history — all from the CLI or the web dashboard.&lt;/p&gt;

&lt;p&gt;The key idea: &lt;strong&gt;your app never needs a &lt;code&gt;.env&lt;/code&gt; file on disk.&lt;/strong&gt; Secrets are injected at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the CLI&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://keyenv.dev/install.sh | sh

&lt;span class="c"&gt;# Authenticate with a service token from the dashboard&lt;/span&gt;
keyenv login

&lt;span class="c"&gt;# Run your app — secrets are injected directly into the process&lt;/span&gt;
keyenv run &lt;span class="nt"&gt;--&lt;/span&gt; npm start
keyenv run &lt;span class="nt"&gt;--&lt;/span&gt; docker-compose up
keyenv run &lt;span class="nt"&gt;--&lt;/span&gt; python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No plaintext file sitting on disk. No risk of accidentally committing it. No "wait, which &lt;code&gt;.env&lt;/code&gt; is this?"&lt;/p&gt;

&lt;p&gt;If you're working with a legacy app that reads from a &lt;code&gt;.env&lt;/code&gt; file directly, &lt;code&gt;keyenv pull&lt;/code&gt; still works — but &lt;code&gt;keyenv run&lt;/code&gt; is the better path forward.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Workflow in 30 Seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See what's different between local and remote before pushing&lt;/span&gt;
keyenv diff

&lt;span class="c"&gt;# Push your local secrets up&lt;/span&gt;
keyenv push

&lt;span class="c"&gt;# Pull the latest from the server&lt;/span&gt;
keyenv pull

&lt;span class="c"&gt;# Manage secrets directly&lt;/span&gt;
keyenv &lt;span class="nb"&gt;set &lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abc123
keyenv get API_KEY
keyenv delete OLD_SECRET

&lt;span class="c"&gt;# Check the version history of a secret&lt;/span&gt;
keyenv &lt;span class="nb"&gt;history &lt;/span&gt;API_KEY

&lt;span class="c"&gt;# Switch between projects&lt;/span&gt;
keyenv switch my-other-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI is written in Rust — fast, single binary, no runtime dependencies. Releases are now signed with an Ed25519 keypair so you can verify what you're installing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Just Shipped
&lt;/h2&gt;

&lt;p&gt;We've been heads-down building. Here's what's live now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit log viewer&lt;/strong&gt; — full history of who accessed or changed what, when. Compliance teams will ask for this eventually. Now you have it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment comparison view&lt;/strong&gt; — see the diff between dev, staging, and production side by side. Catch the "why is this broken in prod but not locally?" problem before it happens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service token management&lt;/strong&gt; — generate scoped tokens for CI/CD pipelines and automated workflows, managed from the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulk import fix&lt;/strong&gt; — &lt;code&gt;.env&lt;/code&gt; files with empty values now import correctly. Sounds minor. Was definitely annoying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team plan at $4/user/month&lt;/strong&gt; — straightforward pricing. No tiers designed to confuse you into the wrong one.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;p&gt;There are other tools in this space. Here's the honest version:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Setup time&lt;/th&gt;
&lt;th&gt;Team sync&lt;/th&gt;
&lt;th&gt;No .env on disk&lt;/th&gt;
&lt;th&gt;Hosted SaaS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KeyEnv&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (keyenv run)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HashiCorp Vault&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (self-host)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infisical&lt;/td&gt;
&lt;td&gt;15-30 min&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (or self-host)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1Password Secrets&lt;/td&gt;
&lt;td&gt;20-40 min&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dotenv-vault&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Vault is powerful, but if you're a 3-person startup, you don't want to maintain Vault. Infisical is solid but the onboarding has more surface area than most teams need. dotenv-vault encrypts your &lt;code&gt;.env&lt;/code&gt; file — which is better than nothing, but it's still a file, and team sync is clunky. KeyEnv is designed to get you from "I have a problem" to "my team is using this" in under 10 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI/CD Works Too
&lt;/h2&gt;

&lt;p&gt;Service tokens mean you can pull secrets in any pipeline without storing credentials in your CI provider's environment variables UI (which is itself a kind of scattered &lt;code&gt;.env&lt;/code&gt; problem).&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="c1"&gt;# In your GitHub Actions workflow&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;Run tests&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;KEYENV_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.KEYENV_SERVICE_TOKEN }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keyenv run -- npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secrets stay in KeyEnv. Your CI environment only holds one bootstrap token with scoped access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;KeyEnv is live at &lt;strong&gt;&lt;a href="https://keyenv.dev" rel="noopener noreferrer"&gt;keyenv.dev&lt;/a&gt;&lt;/strong&gt;. The CLI install is one line, the free tier covers solo projects, and the team plan is $4/user/month.&lt;/p&gt;

&lt;p&gt;If you've got feedback — things that don't work the way you expect, features you'd want before switching from your current setup, anything — open an issue at &lt;strong&gt;&lt;a href="https://github.com/keyenv/keyenv" rel="noopener noreferrer"&gt;github.com/keyenv/keyenv&lt;/a&gt;&lt;/strong&gt; or reach out directly. We're actively building this and want to hear what's actually blocking people.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.env&lt;/code&gt; file had a good run. There's a better way now.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Simulate a Moving Fleet on Real Roads Before You Have a Single Real Vehicle</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Wed, 11 Mar 2026 20:33:18 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/simulate-a-moving-fleet-on-real-roads-before-you-have-a-single-real-vehicle-57gh</link>
      <guid>https://forem.com/ivannovazzi/simulate-a-moving-fleet-on-real-roads-before-you-have-a-single-real-vehicle-57gh</guid>
      <description>&lt;p&gt;You're building a fleet management dashboard. You need GPS positions updating in real time, vehicles following actual road networks, realistic speed changes through intersections, and enough concurrent units to stress-test your WebSocket layer. The problem: you don't have a fleet.&lt;/p&gt;

&lt;p&gt;Mocking this properly is harder than it sounds. Static fixtures go stale the moment your UI needs live movement. Random coordinate walks ignore roads entirely. Hand-rolling a simulator burns days you don't have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ivannovazzi/moveet" rel="noopener noreferrer"&gt;Moveet&lt;/a&gt;&lt;/strong&gt; solves this. It's an open-source, real-time vehicle fleet simulator that runs on actual road networks and streams GPS positions, routes, and traffic data over WebSockets.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Actually Does
&lt;/h2&gt;

&lt;p&gt;Moveet ingests a GeoJSON road graph (OpenStreetMap-derived or your own), builds a bidirectional weighted graph from it, and runs a fleet of simulated vehicles across it continuously.&lt;/p&gt;

&lt;p&gt;Each vehicle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finds routes using &lt;strong&gt;A* pathfinding&lt;/strong&gt; with a haversine heuristic&lt;/li&gt;
&lt;li&gt;Applies a &lt;strong&gt;realistic motion model&lt;/strong&gt; - acceleration on straights, deceleration into turns&lt;/li&gt;
&lt;li&gt;Emits position updates over &lt;strong&gt;WebSocket&lt;/strong&gt; as it moves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that, Moveet derives &lt;strong&gt;heat zones&lt;/strong&gt; from high-connectivity intersections in the road graph, giving you traffic-density regions that behave like real congestion patterns.&lt;/p&gt;

&lt;p&gt;There's also a built-in operator UI - a React + D3 renderer that draws routes directly onto SVG without touching Leaflet, Mapbox, or any tile provider. No API keys, no external dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture in Three Layers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/ui          (React + D3 route renderer)
     |  REST + WebSocket
     v
apps/simulator   (simulation engine - road graph, A*, motion model)
     |  GET /vehicles, POST /sync
     v
apps/adapter     (optional bridge to external fleet APIs)
     |  GraphQL + Kafka / Redpanda
     v
Your app or local dev stack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simulator is the core. The adapter is optional - use it when you need to sync simulated vehicle state into an external fleet management API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Up and Running in One Command
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/ivannovazzi/moveet/main/docker-compose.ghcr.yml
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.ghcr.yml up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:5012&lt;/code&gt;. You'll have a live fleet moving across a road network, streaming positions in real time.&lt;/p&gt;

&lt;p&gt;No local Node setup, no dependency resolution, no config files to wrangle before you see something move.&lt;/p&gt;




&lt;h2&gt;
  
  
  Plug In Your Own Data Sources and Sinks
&lt;/h2&gt;

&lt;p&gt;Moveet's plugin architecture is where it gets genuinely flexible for CI and staging environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt; control where vehicle and road data come from:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Local GeoJSON files, great for deterministic CI runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GraphQL&lt;/td&gt;
&lt;td&gt;Pull live road data from your own API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REST&lt;/td&gt;
&lt;td&gt;Any HTTP endpoint that returns road/vehicle data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL / PostgreSQL&lt;/td&gt;
&lt;td&gt;Seed directly from your existing database&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sinks&lt;/strong&gt; control where simulated positions go:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Console&lt;/td&gt;
&lt;td&gt;Quick local debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REST / GraphQL&lt;/td&gt;
&lt;td&gt;Push to your backend during integration tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka&lt;/td&gt;
&lt;td&gt;Stream into your existing event pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;Pub/sub for downstream consumers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook&lt;/td&gt;
&lt;td&gt;Fire-and-forget to any HTTP listener&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This means you can wire Moveet directly into your staging stack and have your actual application code consume simulated vehicle events - same path as production, simulated data.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;TypeScript throughout (ES2022), Node.js + Express + &lt;code&gt;ws&lt;/code&gt; on the server, React 19 + D3 v7 + Vite on the client, Turf.js for geospatial math, Vitest for testing, Turborepo for the monorepo build. Docker Compose for deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local development&lt;/strong&gt; - realistic moving data without a staging environment dependency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration testing&lt;/strong&gt; - deterministic scenarios via static source plugin, repeatable in CI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load testing&lt;/strong&gt; - scale vehicle count to stress-test your WebSocket handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo environments&lt;/strong&gt; - show a live-looking dashboard without exposing real fleet data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Get Involved
&lt;/h2&gt;

&lt;p&gt;The project is on GitHub at &lt;strong&gt;&lt;a href="https://github.com/ivannovazzi/moveet" rel="noopener noreferrer"&gt;github.com/ivannovazzi/moveet&lt;/a&gt;&lt;/strong&gt;. It's early-stage and actively developed.&lt;/p&gt;

&lt;p&gt;If you're building anything in the fleet space and have spent time wrestling with test data, take it for a spin. Issues, PRs, and feedback welcome.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How we replaced .env files across 5 microservices without touching the app code</title>
      <dc:creator>Ivan Annovazzi</dc:creator>
      <pubDate>Tue, 24 Feb 2026 16:39:11 +0000</pubDate>
      <link>https://forem.com/ivannovazzi/how-we-replaced-env-files-across-5-microservices-without-touching-the-app-code-12i1</link>
      <guid>https://forem.com/ivannovazzi/how-we-replaced-env-files-across-5-microservices-without-touching-the-app-code-12i1</guid>
      <description>&lt;p&gt;The .env file tax is real. Every time we onboard a new developer, someone has to share credentials over Slack. Every time we add a service, there's another &lt;code&gt;.env.example&lt;/code&gt; to maintain. By our fifth microservice, we had a mess.&lt;/p&gt;

&lt;p&gt;This is the story of how we moved all five services to a central secrets manager — without touching a single line of app code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem We Had
&lt;/h2&gt;

&lt;p&gt;Our stack looked something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;api-gateway&lt;/code&gt; — Node.js, reads 12 env vars&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;auth-service&lt;/code&gt; — Node.js, reads 8 env vars&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;billing-service&lt;/code&gt; — Node.js, reads 6 env vars&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;notification-service&lt;/code&gt; — Python, reads 5 env vars&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;analytics-worker&lt;/code&gt; — Go, reads 4 env vars&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each service had its own &lt;code&gt;.env.example&lt;/code&gt;. New developer? "Hey, ask someone for the values." Production values? "Check the secret Notion page." Rotation? "Good luck, touch every service manually."&lt;/p&gt;

&lt;p&gt;The breaking point came when we rotated a database password and missed one service. Three hours of debugging a production incident traced back to a stale &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Vault?
&lt;/h2&gt;

&lt;p&gt;HashiCorp Vault is the "right" answer at scale. But setting up Vault for a 5-person team means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running and maintaining a Vault cluster&lt;/li&gt;
&lt;li&gt;Setting up auth methods, policies, lease management&lt;/li&gt;
&lt;li&gt;Building tooling to inject secrets at runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We wanted something that felt more like &lt;code&gt;dotenv&lt;/code&gt; but with team access controls, encryption, and auditability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach: Treat Secrets as Ephemeral Artifacts
&lt;/h2&gt;

&lt;p&gt;The key mental shift is this: &lt;strong&gt;a &lt;code&gt;.env&lt;/code&gt; file is not configuration, it's a generated artifact.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of storing &lt;code&gt;.env&lt;/code&gt; files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Secrets live in an encrypted store (we used &lt;a href="https://keyenv.dev" rel="noopener noreferrer"&gt;keyenv.dev&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Each developer runs &lt;code&gt;keyenv pull&lt;/code&gt; to generate a fresh &lt;code&gt;.env&lt;/code&gt; for their environment&lt;/li&gt;
&lt;li&gt;CI/CD pipelines do the same in their build step&lt;/li&gt;
&lt;li&gt;The generated &lt;code&gt;.env&lt;/code&gt; is ephemeral — never committed, never shared&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What We Changed (Per Service)
&lt;/h2&gt;

&lt;p&gt;Here's the thing: &lt;strong&gt;we changed nothing about how the services consume secrets.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All five services already read from environment variables via &lt;code&gt;process.env&lt;/code&gt;, &lt;code&gt;os.environ&lt;/code&gt;, or &lt;code&gt;os.Getenv&lt;/code&gt;. They continued to do that. We just changed how the &lt;code&gt;.env&lt;/code&gt; got there.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Copy from teammate over Slack&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit manually&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keyenv pull &lt;span class="nt"&gt;--env&lt;/span&gt; development
&lt;span class="c"&gt;# .env generated from encrypted store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production deploys, we added one line to our CI pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keyenv pull &lt;span class="nt"&gt;--env&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No SDK integration. No sidecar containers. No code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-Environment Scoping
&lt;/h2&gt;

&lt;p&gt;The feature that made this work cleanly is environment inheritance. We defined secrets at three levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared&lt;/strong&gt; — keys used across all environments (service names, feature flags)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-environment&lt;/strong&gt; — &lt;code&gt;DATABASE_URL&lt;/code&gt;, &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; differ per env&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-service&lt;/strong&gt; — only relevant to one service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you run &lt;code&gt;keyenv pull --env staging&lt;/code&gt;, you get the merged result. Each developer gets the right values for their environment without needing to know the production secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rotation in One Place
&lt;/h2&gt;

&lt;p&gt;When we had to rotate our Stripe key (a quarterly security practice), the old workflow was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate new key in Stripe dashboard&lt;/li&gt;
&lt;li&gt;Update 3 services that use it&lt;/li&gt;
&lt;li&gt;Hope nothing breaks in production&lt;/li&gt;
&lt;li&gt;Realize you forgot the analytics worker&lt;/li&gt;
&lt;li&gt;Fix the analytics worker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The new workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate new key in Stripe dashboard&lt;/li&gt;
&lt;li&gt;&lt;code&gt;keyenv set STRIPE_SECRET_KEY &amp;lt;new_value&amp;gt; --env production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Deploy (each service pulls fresh on next start)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Done in 90 seconds. Full audit trail showing who changed what and when.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Gained
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero secrets in Slack&lt;/strong&gt; — new developers get access via team invite, not copy-paste&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No stale .env files&lt;/strong&gt; — pull always reflects current state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotation without touching services&lt;/strong&gt; — update the store, redeploy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit trail for compliance&lt;/strong&gt; — who accessed what and when&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What This Doesn't Solve
&lt;/h2&gt;

&lt;p&gt;To be clear about the limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime secret injection&lt;/strong&gt; — if you need secrets to change mid-run without restarting, you'll want something with dynamic leases (Vault)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure secrets&lt;/strong&gt; — Terraform state, cloud provider credentials are better handled by the cloud provider's native tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Very large teams&lt;/strong&gt; — at 50+ developers, the enterprise features of Doppler or Infisical start to matter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a 5-person team with 5 microservices in a multi-environment setup, this approach eliminated our entire class of "wrong credentials" incidents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The pattern that eliminated our credential chaos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Secrets belong in an encrypted store, not in files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; files are generated artifacts, not source-controlled config&lt;/li&gt;
&lt;li&gt;Every service gets its secrets the same way: pull at startup&lt;/li&gt;
&lt;li&gt;Apps don't need to know any of this — they still read env vars&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're still copying &lt;code&gt;.env&lt;/code&gt; files around, try treating them as generated artifacts for a week. The operational simplification is immediate.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
