<?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>DEV Community: Mustafa Mahmoud Atta</title>
    <description>The latest articles on DEV Community by Mustafa Mahmoud Atta (@mustafamahmoudatta111).</description>
    <link>https://dev.to/mustafamahmoudatta111</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%2F3876624%2F9f539e53-e263-4c13-8964-e520a2cd07df.png</url>
      <title>DEV Community: Mustafa Mahmoud Atta</title>
      <link>https://dev.to/mustafamahmoudatta111</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mustafamahmoudatta111"/>
    <language>en</language>
    <item>
      <title>We Replaced API Keys with Tokens That Self-Destruct in 5 Minutes</title>
      <dc:creator>Mustafa Mahmoud Atta</dc:creator>
      <pubDate>Thu, 23 Apr 2026 04:59:05 +0000</pubDate>
      <link>https://dev.to/mustafamahmoudatta111/we-replaced-api-keys-with-tokens-that-self-destruct-in-5-minutes-p1j</link>
      <guid>https://dev.to/mustafamahmoudatta111/we-replaced-api-keys-with-tokens-that-self-destruct-in-5-minutes-p1j</guid>
      <description>&lt;p&gt;Your CI job has a secret. It's been sitting in your environment variables for two years. You don't know exactly who has access to it. Rotating it means coordinating three teams. So you don't.&lt;/p&gt;

&lt;p&gt;That's not a process failure. That's what API keys are &lt;em&gt;designed&lt;/em&gt; to be: long-lived strings that survive forever because they have to.&lt;/p&gt;

&lt;p&gt;We're &lt;strong&gt;Moustafa Mahmoud Atta&lt;/strong&gt; and &lt;strong&gt;Abd El-Sabour Ashraf&lt;/strong&gt;, and we built &lt;a href="https://github.com/oathmesh/oathmesh" rel="noopener noreferrer"&gt;OathMesh&lt;/a&gt; to change that default.&lt;/p&gt;

&lt;p&gt;Every machine call gets a token that's cryptographically signed, scoped to a single action, and dead in &lt;strong&gt;≤ 5 minutes&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The whole idea in two lines of HTTP
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&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;Authorization: Bearer abc123xyz_still_valid_since_2022
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: OathMesh eyJhbGciOiJFZERTQSIsInR5cCI6Im9tK2p3dCJ9...
                        └── expires in 300 seconds. enforced. not optional.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leaked? By the time an attacker tries it, it's already dead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest comparison
&lt;/h2&gt;

&lt;p&gt;This is the real picture — no spin:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;API Keys&lt;/th&gt;
&lt;th&gt;Typical JWT&lt;/th&gt;
&lt;th&gt;Short-lived JWT + jti&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OathMesh&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifetime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;♾️ Forever&lt;/td&gt;
&lt;td&gt;Hours–Days&lt;/td&gt;
&lt;td&gt;5–15 min (if configured)&lt;/td&gt;
&lt;td&gt;⏱️ &lt;strong&gt;≤ 300s. Enforced.&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cryptography&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Just a string&lt;/td&gt;
&lt;td&gt;HS256 / RS256&lt;/td&gt;
&lt;td&gt;RS256 / ES256&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Ed25519 only&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replay protection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ jti blocklist (DIY)&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Built-in, required&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Action scoping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ Custom, optional&lt;/td&gt;
&lt;td&gt;⚠️ Custom, optional&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;&lt;code&gt;act&lt;/code&gt; required&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Policy engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Pkl rules, hot-reload&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ Roll your own&lt;/td&gt;
&lt;td&gt;⚠️ Roll your own&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Every allow &amp;amp; deny, NDJSON&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Leaked secret blast radius&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💀 Forever&lt;/td&gt;
&lt;td&gt;🩸 Hours&lt;/td&gt;
&lt;td&gt;🟡 Minutes&lt;/td&gt;
&lt;td&gt;🟢 &lt;strong&gt;≤ 5 min. Max.&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;😰 Manual + coordination&lt;/td&gt;
&lt;td&gt;⚠️ Varies&lt;/td&gt;
&lt;td&gt;⚠️ Varies&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Auto-expiry by design&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Honest take:&lt;/strong&gt; You &lt;em&gt;can&lt;/em&gt; get close to OathMesh with short-lived JWTs + a jti blocklist. What OathMesh adds is the opinionated wrapper: TTL enforcement you can't disable, &lt;code&gt;act&lt;/code&gt; scoping that's required (not optional), a built-in policy engine, and a full audit trail — out of the box, not DIY.&lt;/p&gt;

&lt;p&gt;Already running SPIFFE/SPIRE or cloud workload identity (AWS IRSA, GCP WI)? Great — those are excellent fits for Kubernetes-native setups. OathMesh is for teams who want this security model without the full service-mesh footprint. And if you want simpler than all of this, keep the API key. If you want safer, read on.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A real scenario: GitHub Actions → your deploy API
&lt;/h2&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%2Fwww.image2url.com%2Fr2%2Fdefault%2Fimages%2F1776920130119-5f0499aa-e0eb-437a-a430-ca1fa3d9e987.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%2Fwww.image2url.com%2Fr2%2Fdefault%2Fimages%2F1776920130119-5f0499aa-e0eb-437a-a430-ca1fa3d9e987.png" alt="image" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CI job never stores a secret. It requests a token with a 300-second TTL, uses it, and it's gone. Even if someone captures it from your logs — they get nothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  How verification works — 14 steps, fail-closed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fail-closed&lt;/strong&gt; means: if &lt;em&gt;any&lt;/em&gt; single step fails, the request is rejected immediately. No partial-valid state. No fallback. Just 401.&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%2Fwww.image2url.com%2Fr2%2Fdefault%2Fimages%2F1776920196530-bb8c3b78-ffe1-46af-afea-c09162f6ccfc.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%2Fwww.image2url.com%2Fr2%2Fdefault%2Fimages%2F1776920196530-bb8c3b78-ffe1-46af-afea-c09162f6ccfc.png" alt="image" width="800" height="1630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;What it checks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Valid JWS compact structure (3 segments)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Header: &lt;code&gt;typ = om+jwt&lt;/code&gt;, &lt;code&gt;alg = EdDSA&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Payload decoded, &lt;code&gt;iss&lt;/code&gt; extracted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;iss&lt;/code&gt; in trusted issuer list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;JWKS loaded (cached in-memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ed25519 signature verified — no algorithm confusion possible&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;iss&lt;/code&gt; re-verified post-signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;exp&lt;/code&gt; is in the future (±10s clock skew tolerance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;iat&lt;/code&gt; is not in the future&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;aud&lt;/code&gt; matches exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;All required claims present: &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;act&lt;/code&gt;, &lt;code&gt;jti&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Request hash binding checked (if present)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;jti&lt;/code&gt; checked against replay cache — seen before → rejected&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Policy evaluated → audit event emitted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Steps &lt;strong&gt;6&lt;/strong&gt; and &lt;strong&gt;13&lt;/strong&gt; are the heavy hitters. No algorithm confusion. No replay. No exceptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gateway mode — protect services you can't modify
&lt;/h2&gt;

&lt;p&gt;Already have APIs you can't change? Run OathMesh as a reverse proxy in front of them.&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%2Fwww.image2url.com%2Fr2%2Fdefault%2Fimages%2F1776920276079-b24bac9f-b412-400c-a2b2-8628f2edf44b.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%2Fwww.image2url.com%2Fr2%2Fdefault%2Fimages%2F1776920276079-b24bac9f-b412-400c-a2b2-8628f2edf44b.png" alt="image" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your upstream gets clean, pre-verified identity headers. Zero code changes required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Drop it into your stack in ~5 lines
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Go (chi)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OathMeshMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;// Fully typed caller context in your handler:&lt;/span&gt;
&lt;span class="n"&gt;caller&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallerFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c"&gt;// caller.Principal.Subject → "agent://ci/deploy-bot"&lt;/span&gt;
&lt;span class="c"&gt;// caller.Action            → "deploy"&lt;/span&gt;
&lt;span class="c"&gt;// caller.TokenID           → unique jti for this call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python (FastAPI)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;oathmesh&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;verify_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OathMeshError&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/deploy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;caller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;verify_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OathMeshError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deployed_by&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Next.js (App Router)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withOathMesh&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@oathmesh/oathmesh/next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oathmesh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withOathMesh&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trustedIssuers&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;oathmesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// typed 401 — missing, invalid, expired, replayed&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full examples for Express, Flask, Django, and chi are in the &lt;a href="https://github.com/oathmesh/oathmesh/tree/main/docs/quickstarts" rel="noopener noreferrer"&gt;quickstarts&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it in 3 commands
&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/oathmesh/oathmesh.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;oathmesh
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Mint a token (300s = the maximum, enforced by the issuer)&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;oathmesh ./bin/oathmesh mint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sub&lt;/span&gt; &lt;span class="s2"&gt;"agent://repo/acme/deploy-bot"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--aud&lt;/span&gt; &lt;span class="s2"&gt;"https://inventory.internal"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--act&lt;/span&gt; &lt;span class="s2"&gt;"deploy"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ttl&lt;/span&gt; 300 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--quiet&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: OathMesh &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://localhost:8081/inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run &lt;code&gt;./demo.sh&lt;/code&gt; for the full golden-path demo end to end.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest pros and cons
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What we got right ✅&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;300s max TTL is enforced in the issuer&lt;/strong&gt; — there's no config flag to make it longer. Intentional, not an oversight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ed25519 only&lt;/strong&gt; — one algorithm, the correct one. Algorithm confusion attacks aren't possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-closed verification&lt;/strong&gt; — all 14 steps must pass. No partial-valid state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full audit trail&lt;/strong&gt; — every allow and every deny logged as NDJSON. grep-able. Cloud-native.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gateway mode&lt;/strong&gt; — zero changes to your existing APIs.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MIT license&lt;/strong&gt; — take it, fork it, self-host it.&lt;br&gt;
&lt;strong&gt;What we know needs work ❌&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's v0.1.0&lt;/strong&gt; — rough edges exist. Read the threat model before running in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You need to run an Issuer service&lt;/strong&gt; — one more thing to deploy and keep alive. Real operational cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Horizontal scaling needs Redis&lt;/strong&gt; — the replay cache is in-memory by default. Multiple instances need a shared Redis. We're not hiding this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pkl for policies&lt;/strong&gt; — powerful, but not everyone knows Apple Pkl. A visual editor is on the roadmap.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Machine-to-machine only&lt;/strong&gt; — user auth is a different problem. Use OAuth2/OIDC for that.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - &lt;strong&gt;Your Ed25519 private key is your trust root&lt;/strong&gt; — unlike an API key (which compromises one service), a leaked signing key compromises &lt;em&gt;every&lt;/em&gt; service on your mesh. &lt;strong&gt;Store it in a secrets manager (Vault, AWS KMS, GCP Secret Manager) — not in an env var.&lt;/strong&gt; This is the one rule that matters most.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Use OathMesh if
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You're running CI/CD pipelines that call internal APIs&lt;/li&gt;
&lt;li&gt;You have service-to-service calls in a zero-trust or service mesh setup&lt;/li&gt;
&lt;li&gt;You're building AI agents that call protected services&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A leaked credential in your environment would cause real damage&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't use OathMesh (yet) if
&lt;/h2&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You need tokens that live longer than 5 minutes by design&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can't add infrastructure — the Issuer service is not optional&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - You need user-facing authentication — this is not the tool
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What's coming
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&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;Rust + Java SDKs&lt;/td&gt;
&lt;td&gt;🔜 Next release (v0.2.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mTLS + rate limiting in Gateway&lt;/td&gt;
&lt;td&gt;🗓️ Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual policy editor (no Pkl required)&lt;/td&gt;
&lt;td&gt;🗓️ Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit dashboard&lt;/td&gt;
&lt;td&gt;🗓️ Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab CI + GitHub App issuers&lt;/td&gt;
&lt;td&gt;🗓️ Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;We built OathMesh because we kept hitting the same wall: leaked credentials with no expiry and no audit trail. The fix shouldn't require a security team or an enterprise budget.&lt;/p&gt;

&lt;p&gt;It's early. It has rough edges. But the model is sound, the code is open, and the MIT license means you can take it wherever you need it.&lt;/p&gt;

&lt;p&gt;If it solves a problem you have — or if you think we're wrong about something — open an issue or start a discussion. We genuinely want to hear from you.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/oathmesh/oathmesh" rel="noopener noreferrer"&gt;github.com/oathmesh/oathmesh&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @oathmesh/sdk
pip &lt;span class="nb"&gt;install &lt;/span&gt;oathmesh
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/oathmesh/oathmesh/cmd/oathmesh@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Built by Moustafa Mahmoud Atta &amp;amp; Abd El-Sabour Ashraf — MIT License&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>opensource</category>
      <category>zerotrust</category>
    </item>
  </channel>
</rss>
