Category: Overig

  • Why Your DevOps Team Feels Stuck (And What You Can Actually Do About It)

    Why Your DevOps Team Feels Stuck (And What You Can Actually Do About It)

    You’ve probably heard it in meetings, whispered in Slack threads, or blurted out in frustration by a PM: “Why is it taking weeks to get this out the door?”

    At some point, every engineering org hits this wall. New features slow to a crawl. Deployments feel like a mini project in themselves. Product managers get antsy. Leadership starts eyeing engineering like it’s the bottleneck—like you’re the problem. Ouch.

    But here’s the twist: it’s not the people. It’s the pipeline.

    Let’s walk through what’s going wrong—and how to fix it without burning your team out or duct-taping your way through another quarter.


    The Silent Sludge in Your Pipeline

    If your deploys only happen once every couple of weeks, that’s not agility—that’s a waterfall wearing CI/CD as a Halloween costume.

    Here’s a pattern I see all the time:

    • Engineers work in long-lived feature branches.
    • Pull Requests pile up, waiting for review.
    • Builds only kick off after merges (so bugs sneak in after everyone gives the thumbs-up).
    • Staging environments are shared—or worse, broken.
    • Rolling back? Might as well light a candle and hope for the best.

    Now, no one sets out to build a pipeline like this. It just… kind of happens. Bit by bit. Like technical debt with a passport and a gym membership—it travels and grows.


    Commit-Time Builds: Because Waiting Until Merge Is Like Brushing After Eating Candy

    Let me ask you something: Why wait to build and test until after a PR is merged?

    That’s like assembling IKEA furniture after you invite your friends over for dinner.

    Shifting your CI to build and test on every commit (not just merges) does two things instantly:

    1. It gives developers near-instant feedback—no more “It worked on my branch, I swear.”
    2. It surfaces integration issues before they become everyone’s problem.

    This isn’t some theoretical dream. With tools like GitHub Actions, GitLab CI, CircleCI, or Buildkite, triggering builds per commit is dead simple. And paired with containerized test runners, the builds stay fast and isolated.

    Yes, your CI bill might tick up a bit. But how much is each delay really costing you?


    No Tests, No Party

    Here’s where it gets uncomfortable. You can’t fix this mess unless you get serious about tests. Like, ruthlessly consistent about them.

    That means:

    • Every commit runs your test suite.
    • Tests must pass before merging—no exceptions.
    • Flaky tests? Quarantine or delete them. Don’t argue. Fix or kill.

    Automated testing is your safety net. Without it, you’re just doing trust-based engineering. And in a growing org, that’s not a compliment.


    Temporary Environments, Permanent Relief

    Now let’s talk staging. Or, as some teams know it, “that weird server that’s broken again.”

    Reviewing features in a shared staging environment is chaos. Someone’s always testing the wrong thing. Or accidentally overwriting someone else’s changes. It’s like trying to rehearse a play on a bus during rush hour.

    Instead: ephemeral environments.

    With Infrastructure-as-Code tools like Terraform, Pulumi, or even just Docker Compose with some scripting, you can spin up full-featured environments per PR. Add a preview link. Let the PM or designer actually see what they’re approving.

    These can be torn down automatically after merge or after a few hours. Clean, fast, and way less arguing in the #deploy channel.


    Rollbacks Shouldn’t Involve Panic

    If your rollback plan involves Slack, manual SSH, and someone named “Stefan” who knows the scripts—you don’t have a rollback plan. You have a Stefan dependency.

    Use versioned artifacts. Container snapshots. Git tags. Whatever fits your stack. Just make sure you can redeploy a known-good version in seconds, not hours.

    Tools like ArgoCD, Flux, or even a simple “git reset + docker compose up” strategy can get you most of the way there.


    So What Actually Changes?

    When you put all this together—commit-time CI, enforced tests, ephemeral environments, automated rollbacks—you get a pipeline that breathes. Suddenly:

    • PMs don’t have to wait two weeks.
    • Engineers don’t dread deploys.
    • Bugs get caught earlier, when they’re cheaper to fix.
    • And leadership stops treating your team like a bottleneck.

    You shift from “move fast and break things” to “move fast and know when it breaks—and how to fix it fast.”


    But Wait—What About Culture?

    Ah yes. The human bit.

    Tech is never just tech. You’re not just changing pipelines; you’re changing habits. This stuff only sticks if your team feels safe experimenting and failing fast. You need buy-in. Some teams even gamify CI success rates. Others run weekly “deployment health” retros.

    There’s no silver bullet, but here’s a mantra I’ve found useful:

    “The goal isn’t speed. The goal is flow.”

    Speed can lead to mistakes. Flow builds trust. It means work moves smoothly through the system, without invisible friction or surprise blockers.


    Final Thought

    CI/CD isn’t just about faster deploys—it’s about confidence. When your pipeline supports your team instead of dragging them down, you ship more, stress less, and stop hearing that dreaded phrase: “Engineering is the bottleneck.”

    Because let’s be honest—most of the time, the real bottleneck isn’t engineering. It’s inertia.

    And once you fix that? Everything else moves faster.


    Want a sanity check on your current pipeline? Or just want to rant about flaky tests and broken staging servers? Shoot me a message. I’ve seen some stuff.

  • Why you should use GPG over other identity verification alternatives

    Why you should use GPG over other identity verification alternatives

    Introduction: Trust in the Digital Age

    In an era where digital trust is both critical and fragile, verifying the identity of someone who signs code, emails, or documents is paramount. Whether you’re pushing commits to an open-source project, sending secure communications, or distributing software, your reputation and the security of others depend on the authenticity of your cryptographic identity.

    Amid various modern alternatives like SSH signatures, Sigstore, or GitHub verification badges, GPG (GNU Privacy Guard) remains a gold standard. It’s time-tested, decentralized, cryptographically robust, and under your control. In this article, you’ll learn why GPG is still the right choice, how to set it up properly, and how to publish your key securely using a Web Key Directory (WKD).

    Why GPG still matters

    1. Decentralization without compromise

    Unlike centralized services like GitHub or GitLab that act as identity gatekeepers, GPG operates on a Web of Trust model. You can self-host your identity, share it on your own domain, and never depend on a third party to vouch for you.

    2. Granular trust control

    You decide who you trust. You can inspect and sign others’ keys, or let them sign yours. This model is more robust than blindly trusting a cloud provider that could be compromised or co-opted.

    3. Separation of duties via subkeys

    GPG allows you to use a secure master key (stored offline) and dedicated subkeys for everyday actions like signing Git commits. This minimizes risk while preserving security.

    4. Ubiquity across the open-source ecosystem

    GPG is widely supported in Linux distributions, Git, email clients, and package managers. It’s also the cornerstone of software distribution security for projects like Debian and Arch Linux.

    Understanding GPG Key Architecture: Master vs Subkeys

    The master key

    • Used to certify (sign) other keys and UIDs.
    • Should be kept offline (e.g., air-gapped USB device).
    • Acts as your long-term identity root.

    Subkeys

    • Used for specific tasks:
    • Signing subkey: for signing Git commits, emails, and files.
    • Encryption subkey: for receiving encrypted messages.
    • Authentication subkey: for SSH login or VPN access.
    • Can be kept on daily-use machines or smartcards (e.g., YubiKey).

    This model ensures that even if a subkey is compromised, your core identity remains secure.

    Step-by-step: creating a master key and signing subkey

    Generate your master key

    Use --full-generate-key to set up a secure root key:

    gpg --full-generate-key

    Choose the following options when prompted:

    • Key type: RSA and RSA
    • Key size: 4096
    • Expiry: 1y (can be extended later)
    • Name and email: Your real identity
    • Passphrase: Choose a strong one!

    List your key

    gpg --list-secret-keys --keyid-format=long

    Take note of your key ID. (The part after the rsa4096/ or ed25519/ and before the date)

    Add a signing subkey

    gpg --edit-key YOUR_KEY_ID

    Inside the GPG prompt:

    addkey
    Choose: RSA (sign only), size 4096, expiry 1y
    save

    Export and back up your keys

    Export your public key:

    gpg --armor --export [email protected] > publickey.asc

    Export and back up your private key (store safely!):

    gpg --armor --export-secret-keys > master-private.asc

    Publishing your GPGkey via WKD (Web Key Directory)

    The Web Key Directory allows your domain to serve your public key in a standardized way. Tools like gpg --locate-keys can then automatically find your key at https://yourdomain.com/.well-known/openpgpkey/.

    1. Create your WKD directory structure

    On your web server (e.g. Nginx):

    mkdir -p /var/www/yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu

    2. Generate WKD hash

    gpg --with-wkd-hash --list-keys [email protected]

    This gives you a hex-based filename, something like:

    hu/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    3. Export public key in WKD format

    gpg --export --armor [email protected] > openpgpkey.asc

    Then copy it:

    cp openpgpkey.asc /var/www/yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    4. Serve WKD directory (testing purposes only)

    You can serve this directory locally using Python 3 for testing:

    cd /var/www/yourdomain.com
    python3 -m http.server 8080

    Then test with:

    gpg --auto-key-locate wkd --locate-keys [email protected]

    ⚠️ Note: This method is for testing only. For production, use a proper web server like Nginx or Apache that can serve .well-known/ paths correctly and over HTTPS.

    Using Your GPG Subkey to Sign Git Commits

    Once your GPG subkey is set up and available on your system, you can use it to sign Git commits. This gives your code contributions a cryptographic signature of authenticity.

    To sign a commit:

    git commit -S -m "Your commit message"

    If Git is configured to use your GPG subkey, this command will prompt for your passphrase (unless cached) and produce a signed commit.

    ℹ️ Setting up Git to use your GPG subkey (via user.signingkey and commit.gpgsign) is outside the scope of this article, but many tutorials and Git documentation cover it in detail.

    You can verify a commit was signed with:

    git log --show-signature

    Conclusion: Control, credibility, and cryptographic hygiene

    GPG is a battle-tested, decentralized way to assert identity across borders and platforms. While GitHub’s verification is convenient, and Sigstore is promising, GPG remains the only solution that gives you full control over your cryptographic reputation.

    By separating your master key from your subkeys and hosting your public key via WKD, you build a resilient, verifiable, and portable trust system — one that’s independent of any single platform.

    So don’t just sign your commits. Own your entire identity.


    Need help building a WKD or integrating GPG into your DevSecOps workflows? Contact ushy to get enterprise-grade cryptographic hygiene built into your pipelines.

  • Mapping complex systems without getting lost in documentation

    Mapping complex systems without getting lost in documentation

    Good infrastructure is maintainable, scalable, robust, resilient and secure. And it stays this way by putting processes into place which perform upkeep on all of these factors. So, how can you ensure that these upkeep processes are healthy and understood by everyone?

    People don’t think in text

    Our brains aren’t made for thinking in text. Try to remember the last page of the last book you read.

    Now try to remember the route you took from your childhood bedroom to the kitchen.

    We are visual-spatial thinkers. And as such, our documentation should be primarily visual-spatial.

    Flowcharts, even when done on the back of a napkin, can show you more than a full-length page of documentation.

    But why settle for napkins? You can use tools like excalidraw to draw them out, or even better; use a code-based tool to have them drawn for you.

    That way you can save the code in your versioncontrol system and see how your diagrams – and your infra, or your understanding of it – evolved over time.

    Let’s practice a little

    I use a notetaking tool called obsidian. In it, a widely used language for marking up flowcharts is used: mermaid.

    Mermaid is easily integrated into any pipeline for documentation builds. Gitlab even has it integrated into their markdown renderer!

    So, let’s learn by building.

    Step 1: Start with a basic flowchart

    flowchart TD
        A[Start] --> B[Do something] --> C[Finish]
    

    This is a basic top-down flowchart:

    • flowchart TD tells Mermaid we’re drawing a top-down flowchart.
    • The arrows (-->) connect nodes.
    • Each node has an ID (e.g. A) and a label (e.g. Start).

    Try changing the labels or rearranging the arrows. That’s the quickest way to get a feel for how it works.

    Step 2: Add conditional logic

    Let’s make it interactive by adding a decision.

    flowchart TD
        A[Start] --> B{Do you agree?}
        B -- Yes --> C[Continue]
        B -- No --> D[Stop here]
    

    Here we introduce:

    • {curly braces} to represent a decision node.
    • Multiple arrows coming out of the same node with labels (Yes and No).

    Now we have a mini flow that resembles real-world logic.

    Step 3: Modularizing with subgraphs

    As diagrams grow, structure helps. Let’s group nodes into a logical block.

    flowchart TD
        subgraph Process
            A[Start] --> B{Do you agree?}
            B -- Yes --> C[Continue]
            B -- No --> D[Stop here]
        end
        C --> E[Wrap up]
    
    • subgraph NAME groups related nodes.
    • end closes the group.
    • The outer node E connects to the subgraph.

    This is great for readability and managing large diagrams.

    Step 4: Change direction and style

    Want things to flow left to right?

    flowchart LR
        A[Login] --> B{Is password correct?}
        B -- Yes --> C[Dashboard]
        B -- No --> D[Show error]
    
    • LR means Left to Right.
    • You can also use RL (Right to Left), BT (Bottom to Top).

    Step 5: Make some sequence diagrams and GANTT charts!

    I think you get the gist of it. Let’s show you some more basic examples. How about a sequence diagram?

    sequenceDiagram
        Alice->>+John: Hello John, how are you?
        Alice->>+John: John, can you hear me?
        John-->>-Alice: Hi Alice, I can hear you!
        John-->>-Alice: I feel great!

    Or even a GANTT chart, for all of you aspiring project managers:

    gantt
        title A Gantt Diagram
        dateFormat  YYYY-MM-DD
        section Section
        A task           :a1, 2025-01-01, 30d
        Another task     :after a1  , 20d
        section Another
        Task in sec      :2025-01-12  , 12d
        another task      : 24d
    

    And if you want to get even fancier, just go visit the docs at mermaidjs.

    This skill is crucial

    While this might seem like a fun inocuous little tutorial for mermaidjs, the point I’m trying to make is this:

    There is no excuse for not having diagrams for your infrastructure, your network and your application services — and there is tremendous value in them.

    I have, in fact, on occasion helped technical teams solve their own problems just by diagramming it out. So do yourself a favor. Get your team to do diagrams instead of plain text.

    And if they’re not up for it, why not give me a call?

  • Auryn: a CLI pipeline DSL

    Auryn: a CLI pipeline DSL

    Auryn: A Domain-Specific Language for CLI Pipelines

    Sometimes you just can’t help but write a bash script to chain together a couple of tools. Sometimes those bash scripts get out of hand, and you wish you had something that could effectively be the linux pipe symbol, but also clean and format your data for the next CLI program.

    Auryn has your back.

    The challenge with CLI pipeline tooling

    Every pipeline engineer has attempted to chain tools together using Bash scripts or integrate them into Jenkins pipelines. The reality is that there’s a lack of standardized input and output formats across CLI tools.

    Each tool serves a different purpose, making automation cumbersome—even though the process is often repetitive:

    1. Read a file.
    2. Optionally transform the input.
    3. Apply the tool with specific parameters.
    4. Save the (potentially massive) output to a file.
    5. Clean the output for the next tool, which will…
    6. Repeat the cycle.

    Introducing Auryn: a CLI glue pipeline DSL

    Enter Auryn—a domain-specific language (DSL) designed to streamline shell pipelines. Think of it as a platypus: seemingly uncertain of its identity, yet functioning exceptionally well because of it.

    Here’s a glimpse of Auryn in action:

    run "bash cert_transparency_check.sh __DOMAIN__"
      parsewith cert_transparency_check 
      output "__OUTPUT_DIR__/found_domains.txt"
      as $domain_list

    In this snippet:

    • A script is executed with a constant (__DOMAIN__) as its only known truth.
    • The output is parsed using a simple parser you write in a shell script, tailored to the unique output of each CLI tool.
    • The parsed text can be saved to a file and stored in a variable ($domain_list) for use in subsequent tools.

    Why Auryn Matters

    Auryn addresses the fragmentation in CLI tooling by providing a unified language to define and manage shell workflows. It empowers security engineers to automate complex pipelines without being bogged down by inconsistent tool interfaces.

    The inspiration for Auryn

    Tom learnt about CPU pipelining and thought the mental model applied to running bash scripts as well. A couple of hours talking to an LLM later, Auryn was born.

    The cycle is:

    • Instruction Fetch
      • Get data
    • Instruction Decode
      • Parse the data for CLI input
    • Execute
      • Run the CLI program with the supplied parameters
    • Write to memory
      • Put the result into a variable
    • Write back
      • (Optionally) write the raw output to a file

    Auryn is the result of a brain blip, and was put together in a couple of hours yet results in an immensely useful output.

    Get Started with Auryn

    Ready to tame the beast? Grab a copy of Auryn from the project’s GitHub page: https://github.com/kapott/auryn. Feel free to expand upon it. While the repository doesn’t have a license yet, it will be free, as in beer.

  • Marketing portal crashes – or –  How to handle performance pain during campaign peaks

    Marketing portal crashes – or – How to handle performance pain during campaign peaks

    Marketing fires off another flashy campaign. Emails are sent, ads are clicked, users flood the customer portal like a swarm of caffeine-fueled shoppers on Black Friday. And then—it hits.

    Pages load like they’re stuck in molasses. Or worse, nothing loads at all. The system groans under the weight. Users complain. Sales stall. And someone in upper management starts asking that dreaded question: “Why weren’t we ready for this?”

    You feel the sting, not just because it’s your infrastructure, but because deep down, you knew this might happen. Again.

    Let’s unpack this. Not just with tech speak and bullet lists, but with some honest reflection—and a few solid ideas you can actually use.

    When “Success” Becomes a System Failure

    Here’s the irony: the portal’s underperformance usually stems from something good—growth. More users, more activity, more data flying around.

    But legacy systems? They don’t celebrate your wins. They choke on them.

    One day it’s a monolith that hums quietly at 15% load. The next, it’s burning CPU like it’s auditioning for a role in Mad Max: Fury Load. And that tiny, single-threaded component you inherited five CTOs ago? It’s now holding your entire digital reputation hostage.

    What’s worse is that customers don’t care why it’s slow. They just know it’s not working. They’re trying to pay their bill, check their status, file a claim—and they’re getting a spinning wheel of doom. Cue the angry tweets, lost conversions, and that cold, creeping sense of dread.


    So… What Can You Do?

    Let’s cut the fluff. The fix isn’t a motivational poster in the dev room. It’s targeted, iterative change.

    Here’s a roadmap that’s worked for real teams facing this exact pain:


    Start With Load Testing, Not Guesswork

    You wouldn’t try to tune a race car without knowing where it breaks down at high speed, right?

    Same thing here. Run proper load tests. Simulate real user traffic. Ramp it up. Push it beyond what your campaigns expect. You’ll spot bottlenecks faster than you can say “timeout error.”

    Often, it’s not the whole system that fails—just one poorly designed function. One slow database query. One dependency that starts to domino under pressure.

    And honestly? You might not like what you find. But knowing is better than waking up to a downed system.


    Find the Rotten Core (Hello, Legacy)

    Every seasoned engineer has faced it: a dusty piece of logic that no one touches because “it just works.”

    Until it doesn’t.

    Sometimes it’s a synchronous job queue. Other times it’s a memory-hungry reporting module that slams your backend when traffic spikes. We once found a SOAP connector—yes, SOAP—that was quietly blocking dozens of threads under load. Brutal.

    This is where profiling tools and call tracing shine. Tools like Jaeger, Prometheus, or even good old strace can light up the exact moment things go sideways.


    Cache Like You Mean It

    Here’s where Redis or Varnish come in handy. They’re not magic—but close.

    The idea’s simple: don’t ask your backend the same thing 5000 times per minute. If something doesn’t change often (like pricing info, account summaries, static content), cache it aggressively. Front it with a CDN. Make it boring.

    The payoff? Less stress on the core. Fewer round-trips. Happier users.


    Go Stateless—or at Least Less State-Obsessed

    Monoliths don’t scale well. Especially ones clinging to session state like a toddler to a teddy bear.

    You don’t need to break the whole thing into a thousand microservices overnight. That’s a recipe for burnout and missed deadlines. But do peel off the high-traffic routes. Things like login, dashboard views, or payment status.

    Move those to lightweight, stateless services. Use JWTs. Offload session management. You’ll sleep better.

    And yeah, containerizing helps. But not if you’re dragging old habits into shiny new pods.


    Autoscaling Isn’t a Luxury—It’s Survival

    Your Kubernetes setup might be stable now. But is it ready to flex?

    Autoscaling isn’t just a checkbox in a YAML file. It needs thoughtful metrics. CPU alone won’t cut it. Use custom metrics if you must—queue lengths, request latency, memory pressure.

    And for the love of uptime, test the autoscaler. Don’t assume it kicks in just because you told it to.

    Think of it like hiring backup staff before a sale. You want them trained and ready—not arriving after the shelves are already empty.


    The Real Fix: Culture, Not Just Code

    Let’s be honest—technical fixes are only part of the equation. The other half? Communication.

    When Marketing spins up a campaign, does Engineering even know? Are there alerting thresholds tied to business events? Does anyone talk about performance before users start yelling?

    If not, that’s the first change to make.

    Create a culture where campaigns and capacity planning go hand in hand. Where load testing isn’t a “once-a-year” task, but a habit. Where developers get curious about why a certain endpoint spikes, not just how to make it faster.


    In the End (Not That Kind of Ending)

    Systems will fail. That’s a given. But the teams who recover fast—and earn user trust—are the ones who get ahead of the next peak. Who know their limits. And who make just enough time to fix the stuff no one else sees coming.

    Next time the portal groans under pressure, let it be because you planned for it. Not because you hoped it wouldn’t happen again.

    And if you’re still waiting for someone to sign off on the load test budget—just show them last campaign’s downtime stats. That usually does the trick.


    TL;DR

    • Simulate traffic with real-world load tests
    • Hunt and kill your bottlenecks (legacy code, slow queries, blocking threads)
    • Add a cache layer with Redis, Varnish, or both
    • Break off high-traffic routes into stateless services
    • Use autoscaling like you actually believe in it
    • Tie marketing and infra planning closer together

    Need help convincing the rest of the team? Send them this post. Or better yet—print it out, tape it to the fridge in the break room, and add a sticky note:

    “This is why we can’t have nice things—unless we fix it.”

    Want help turning this into an action plan? Let’s talk.

  • Migration checklist

    Migration checklist

    Basic information

    • System or platform name
      • What are the internal names, project names, jargon, nicknames, pet names for the platform(s) and environment(s)?
    • What environments are there?
      • Classic DTAP or another model?
    • Business purpose of the platform (what does it do for the company?)
    • Owner / single point of contact for the platform
      • If there is no single point of contact, contacts for each environment or separated responsibility within the platform
        • Software deployment lifecycle (SDLC)
        • Infrastructure
        • Datacenter team
        • Crisis management
        • Information security
        • Architecture
    • Who uses it (per environment) ?
      • Internal users (+estimated number)
      • External users (+estimated number)
    • If non-standard DTAP
      • Criticality level per environment

    People & Processes

    • How do users interact with the system daily?
    • Which business processes rely on this system?
    • Any undocumented tribal knowledge?
    • Any user groups at risk during migration?

    Risks & Constraints

    • What absolutely cannot go down during migration?
    • Any critical dates (e.g. billing cycles, audits)?
    • Sensitive data or legal/compliance constraints?
    • Outstanding incidents, bugs, or known issues?

    Architecture

    • Monolith, microservices or hybrid?
      • Schema’s, flowcharts, graphics, data flow diagrams?
    • On-prem, cloud or hybrid?
    • Running on VM’s, bare metal or containers?
    • OS details (types, versions?)
    • Languages / frameworks used in application(s)?
    • Is there a CI/CD pipeline? How is code deployed?

    Network & Infrastructure

    • Existing network diagrams
      • If nonexistent, sketch one
    • Inbound
      • Ports
      • Protocols
      • Load balancers
    • Outbound
      • APIs
      • Third party services
    • Firewalls, NAT, proxies?
    • DNS structure / domain naming?
      • Internal
      • External

    Data & Storage (state)

    • Databases
      • Type(s)
      • Version(s)
      • Size(s)
    • Where is the data physically stored?
      • Does this location matter for the company?
    • Any data that _must_ not move (e.g. compliance?)
    • Existing backups? Last tested when?
    • Any shared filesystems, blob/object storage?

    Application landscape

    • Which applications are running?
      • Don’t forget cronjobs / systemd timers
      • Don’t forget custom scripts
    • What depends on what?
      • Dependency graph from app. perspective
        • Sketch one if it’s not there
    • Any blackbox components?
      • Closed source
      • Undocumented
      • Unknown-operation application/code?
    • Upcoming (or passed) or EOL components?

    Security & Access

    • What authentication methods are used?
      • Local
      • LDAP
      • SSO
      • OAuth?
    • User roles / access control model?
      • Is there a matrix table for it?
      • If not – make it or have it made
    • How are secrets handled?
      • Hardcoded
      • Ansible vault
      • env files
      • etc.
    • Network segmentation
    • Firewall rules
    • Last security audit?
      • Any open issues?

    Monitoring & Logging

    • What logs exist?
    • Where are they stored?
    • What is their (mandatory) retention?
    • Is monitoring in place?
      • Prometheus, Zabbix..
    • Are there alerts?
      • Who gets them?
    • Any trends in resource usage?

    External Dependencies

    • Are there integrations with third-party API’s or services?
    • API keys or tokens in use?
    • Any licenses, usage limits or contracts?

    Ready for Migration Planning

    • Is the environment fully mapped?
    • Can parts of the system be moved independently?
    • What goes first? What can wait?
    • What needs testing before production cutover?
  • Hello world!

    Software should be simple. Our mission is to simplify your business problems into eloquent automation you will want to use every day.