Mission DevSecOpsPlatform EngineeringT-00:10
Launch your product securely.
DevSecOps EngineerPlatform Engineer
I turn security frameworks and compliance into platform code.
Kubernetes, AWS, CI/CD, and observability, with security built into the platform.
Open to remote and overseas DevSecOps rolesOpen to remote and overseas Platform Engineering rolesAvailable now
Tech stack
- Languages
- GoPythonBash
- Cloud
- AWSAzureGCP
- Container
- Kubernetes (EKS)HelmDocker
- CI/CD
- GitHub ActionsArgoCDFluxCD
- IaC
- Terraform
- Supply chain
- Sigstore/CosignSBOMTrivy
- Policy
- OPAKyvernoOSCAL
- Secrets
- VaultAWS KMS
- Observability
- PrometheusGrafanaLokiOpenTelemetryFluent Bit
- GRC
- PKIISO 27001ISMS-PEU CRANIST CSFNIST SP 800-53NIST SP 800-30NIST SSDFNIST SP 800-204
Proof, not keywords
- LND #11219 merged ↗
- Aperture metrics PR #297 ↗
- Aperture fix merged via #292 ↗
- 6 security reports (2 confirmed, 4 under review)
- Private EKS validator ↗
Scroll to launch ↓
Design
I put security into the design before anything gets built.
Threat models, blast-radius maps, and requirements for three AWS reference architectures.
Personal projectstest flights2
Security Requirements plugin Shift-left tooling skips the first stage: what a service must satisfy 19 verifiable requirements for a paid service, 3 no NIST baseline coversSee how I fixed it Hide
- Problem
- DevSecOps preaches shift left, but requirements analysis, the first stage of the lifecycle, has almost no tooling. Nothing states what a service must satisfy before code exists.
- Solution
- A Claude Code plugin that derives requirements from the service's characteristics, operating environment, compliance obligations, and stack: FIPS 199 impact, an 800-53B baseline, STRIDE and LINDDUN threats, regulatory overlays, and verifiable requirements.
- Result
- Ran on the Lightning-paid OpenCTI scan service: 11 service-specific threats, 19 verifiable requirements, 3 of them for risks no NIST baseline control expresses.
- Lesson
- Let the model interpret; let scripts own control IDs, baselines, and approvals. A wrong recovery objective quietly rewrites dozens of requirements.
AWS SaaS Security Design Review One requirement set doesn't fit every AWS deployment model 3 architectures, 31 threats; ECS and EKS broke at different boundariesSee how I fixed it Hide
- Problem
- A requirements plugin is only as good as the architectures it has been run on. I needed to see it handle three different deployment models end to end, not one demo.
- Solution
- Ran the Security Requirements plugin through its whole lifecycle on three AWS reference architectures (ECS SaaS, EKS SaaS, and a serverless movie-voting API): impact, threats, blast radius, responsibility, requirements, refresh, evidence, CI/CD verification, and regulatory overlays.
- Result
- Three reviews in 29 posts, with threat models of 13 (ECS), 10 (EKS), and 8 (serverless). In the serverless review, a route-dispatch mismatch surfaced as a threat-only requirement the 800-53 baseline doesn't express.
- Lesson
- The deployment model changes the threat model, even between two container platforms. On ECS, tenant isolation broke at the routing layer: a shared mapping Lambda that could rewrite any tenant's routes, and services trusting a forged tenant header. On EKS, it broke at boundaries ECS didn't have: ingress rules routing into another tenant's namespace, namespace isolation without NetworkPolicy, and a service account picking up the wrong AWS role. One requirement set copied across all three would have missed each of these.
Build
I build platforms that are secure before the first workload ships.
I set up the organization, environment, and activities for building software securely, based on NIST SSDF and NIST SP 800-204.
Personal projectstest flights3
Ethereum Hoodi Validator on Private EKSStateful validator workload on private EKS Public client images run right next to a slashable signing key Validator active on Hoodi; revoking the signer's role stopped signing at onceSee how I fixed it Hide
- Problem
- Staking operators run consensus and validator clients pulled from public registries, right next to the signing key, so a swapped image or a stolen credential means a slashable signature. Running the hardware yourself adds patching, uptime, and key custody on top.
- Solution
- Moved the node to a private EKS cluster with no public API endpoint. Every upstream client image is reviewed, pinned by digest, checked against an allowlist, and mirrored into private ECR with immutable tags and KMS encryption, published through GitHub OIDC with a single-purpose role. Kyverno admission blocks workloads that don't meet the baseline.
- Result
- Validator 1559065 went active on Hoodi and had attestations included on chain. During live rollout, admission rejected a sidecar image pinned by tag instead of digest until it was re-pinned. In a revocation drill, pulling the signer's Vault role stopped signing at once; after reactivation with the same slashing-protection volume, its next attestation was finalized.
- Lesson
- Every control has a cost. I started with about ten private registries and every scanner I could add, then cut back to the controls that answer a real risk.
LND node on Kubernetes A Running Pod can come back as a different Lightning node Same identity, channels, and backups after Pod replacement and Helm upgradeSee what I learned Hide
- Problem
- A learning project: I wanted to see what Kubernetes actually guarantees for a stateful node, and what it doesn't. A Lightning node is its wallet, channel database, and static channel backups, so a Pod that restarts successfully can still come back as a different node.
- Solution
- Packaged LND as a Helm chart: a StatefulSet with per-node volumes, default-deny NetworkPolicy, RPC kept apart from P2P, wallet unlock left as an operator step, and optional monitoring sidecars. Then wrote a test for every claim the chart makes.
- Result
- After a forced Pod replacement and a helm upgrade, the same node identity, channels, volumes, and SCB hash came back. Reproduced on macOS arm64 and WSL amd64 from the same revision, with a synced testnet node, an active public channel, and payments both ways.
- Lesson
- For stateful workloads, Running is only where testing starts. Kubernetes gives scheduling and storage; LND still owns identity and channel state, and the operator still owns the seed.
Lightning payments for OpenCTI A settled invoice can leave an order unpaid, or pay for two 250 sat on testnet: 402 → 200, order and receipt committed as paidSee what I learned Hide
- Problem
- A practice project for L402. Getting a Lightning payment to unlock an API call is easy; making it count once, for the right order, is the part to learn. The payment and the order database are separate systems, so a settled invoice can leave an order unpaid or be replayed against another.
- Solution
- Put Aperture in front of the scan API as an L402 gate, then checked the preimage and challenge, looked the settlement up on the merchant node, wrote receipts through a durable outbox, and made claims safe to retry.
- Result
- On testnet, a 250-sat invoice settled, the API went from 402 to 200, and the order, challenge, and receipt all committed as paid.
- Lesson
- A 200 OK isn't proof of payment; the receipt has to match the order it pays for.
Open source contributionsproactive minds3
OSCAL CompassFixed a KeyError in compliance-trestle (merged) and opened a GitHub Actions DevSecOps pipeline plugin for compliance-to-policy.
- Write-ups
SEAL Frameworks: private registries and mirrorsGuidance on private registries and package mirrors so builds pull only reviewed artifacts.
- Code
- PR #627 ↗
- 6 security reports submitted to blockchain companies2 confirmed by the vendor with fixes scheduled for the next release; 4 submitted and under review. Product names and details stay private until fixes ship.
Deploy
I make pipelines prove their own integrity, and I fix what I find upstream.
24 controls mapped to 12 threat areas in my own CI/CD, plus contributions to SEAL, and a fuzzer bug in Trail of Bits' Gosentry fixed the same day.
Personal projectstest flights1
Pipeline security controls The pipeline itself can ship a forged artifact to production 24 controls; every Action and image pinned; 25 workflows → 8See how I fixed it Hide
- Problem
- The pipeline that builds and ships the validator is itself an attack path: a stolen token, a swapped action, or a forged evidence file reaches the production cluster without touching application code. NIST SP 800-218 (SSDF) and SP 800-204D say what to protect, not which controls one repository needs.
- Solution
- Mapped SSDF practices and 800-204D's CI/CD threats to 24 controls in the node-operator pipeline, each tied to the threat it answers: default-deny token permissions and SHA-pinned actions, untrusted PR code treated as data, fail-closed scanner gates, OPA policy-as-code, GitHub OIDC instead of long-lived keys, and digest-bound SBOM and provenance.
- Result
- Controls enforced in CI and release workflows, with evidence bound to the exact commit. OpenSSF Scorecard: all 84 GitHub Actions and 39 container images pinned, least-privilege tokens, no dangerous workflow patterns, and 30 of 30 merged PRs CI-tested. Consolidated 25 workflows into 8. A mirror step shown to accept a wrong image digest now rejects it.
- Lesson
- More scanners slowed delivery without making it safer; fewer tools, containerized and pinned, did more. A control earns its place by the threat it answers.
Open source contributionsproactive minds4
SEAL Frameworks: Policy as CodeGuidance on enforcing security policy through the CI/CD pipeline for the Security Alliance frameworks.
- Code
- Pull requests ↗
Aperture: flaky L402 test fixFixed a rare (1 in 256) flake in TestTamperedL402 that could fail CI at random. Merged with authorship kept via #292.
Gosentry: a fuzzer that died silentlyReported a LibAFL corpus bug in Trail of Bits' Gosentry where fuzzing stopped but go test still passed; fixed the same day.
- Code
- Issue #210 ↗
LND: short reads in address decoders, fixedReported that NodeAnnouncement2's fixed-width address decoders accept short reads. Fixed upstream in PR #11219 for LND 0.22.0, with a co-author credit on the fix.
Operate
I make running systems observable, and cheap enough to keep defending.
A read-only on-call agent with SLO alerts and drills for a Lightning payment gate, per-outcome L402 metrics upstream in Aperture, and 28 cloud security checks in Prowler.
Personal projectstest flights1
Kagent on-call agent for the L402 gate Alerts took 16 min to notice a dead component Detection 5 min; blind drill paged in 2 min 21 sSee how I fixed it Hide
- Problem
- The L402 payment gate had no playbooks and no automated first responder, and its alerts took 16 minutes to notice a dead component on low traffic. Every diagnosis started from a blank terminal.
- Solution
- Put a Kagent agent on call with read-only, code-first diagnosis tools and GitOps-managed access, then reviewed the whole setup against the Google SRE Book: playbooks that mitigate first, an SLO with burn-rate alerts on a synthetic probe, a dashboard, and an eval harness that grades the agent against the playbook.
- Result
- Detection went from 16 min 22 s to 5 min 14 s. In a blind drill after the upgrade, pages fired in 2 min 21 s and the agent named the broken component, lnd-merchant. The agent's pass rate on the eval set rose from 47% to 72–80%.
- Lesson
- At this model size, a fact in the tool output beat another rule in the prompt. The agent skipped a tool the system message told it to call; a next_check field in the result it had just read is harder to skip.
Open source contributionsproactive minds3
Aperture: L402 metricsPer-outcome Prometheus counters for Aperture's L402 mint and verify paths. A maintainer reviewed the proposal and asked for a PR; it's open as #297.
Aperture: security event proposalStructured L402 security events that carry HMAC references instead of raw macaroons and preimages.
Prowler: 28 Azure and GCP checksPosture checks for AKS, Cosmos DB, Databricks, Entra ID, Cloud SQL, Secret Manager, and more.
- Code
- Pull requests ↗
Every control, its owner, and its evidence on one screen.

“Know the enemy and know yourself, and you will not be imperiled in a hundred battles.”
How well do you know your organization's security activities?
Design for failure.
Automate the recovery.
Prove the control.
Jinsoo Yang
Security controls you can read, run, and prove.
Global reach
Field notes
What Shouldn't I Miss in the AI Era? →New models and tools arrive every day. The one thing I don't want to hand over to AI is my own understanding, and the responsibility that comes with it.How Do I Contribute to Open Source, and Why Do I Do It? →How I get to know an unfamiliar open-source project, and why I contribute. Open source taught me a lot, and I want other people to learn alongside me.How I work
Self-starter
I get to security problems before they ship.
Maintainer of the Supply Chain section of the SEAL Frameworks, with 50 contributions and 6 security reports sent upstream.
Fast learner
I learn a new stack by running something real on it.
To learn Web3 infrastructure, I built and ran an Ethereum testnet validator on EKS. Picked up 12+ credentials along the way, Kubestronaut included.
Problem solver
I turn slow, manual work into code.
Compliance used to live in documents. I moved it into OSCAL: controls, policies, evidence, and owners in one dashboard the team can query from Slack and Jira.