<?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>Debug School</title>
    <description>The most recent home feed on Debug School.</description>
    <link>https://www.debug.school</link>
    <atom:link rel="self" type="application/rss+xml" href="https://www.debug.school/feed"/>
    <language>en</language>
    <item>
      <title>Windows 11 context menu</title>
      <dc:creator>Suyash Sambhare</dc:creator>
      <pubDate>Fri, 08 May 2026 05:15:07 +0000</pubDate>
      <link>https://www.debug.school/suyash/windows-11-context-menu-5ap</link>
      <guid>https://www.debug.school/suyash/windows-11-context-menu-5ap</guid>
      <description>&lt;h2&gt;
  
  
  Ye Olde Right Click menu
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/HcbRRdNTbAN1GZSadlJtbkbkcwY3a_-yaBDnb84WC4o/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvZmMyOTN4/eHVvY3IyODljamRi/a2gucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/HcbRRdNTbAN1GZSadlJtbkbkcwY3a_-yaBDnb84WC4o/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvZmMyOTN4/eHVvY3IyODljamRi/a2gucG5n" alt="Old Right Click" width="255" height="784"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Design engineer in Microsoft probably thinking, "&lt;em&gt;Hmm, the context menu when you right-click on a file is quite cluttered, lets fix that by moving everything to an overflow option called &lt;strong&gt;Show more options&lt;/strong&gt;&lt;/em&gt;"&lt;/p&gt;

&lt;h2&gt;
  
  
  Le New Right Click Menu
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/lGgKUrLM6yB2AyV6DFn_qVir5Q851RFtfF12pDUj3XU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbXNna3Ny/OXFlbzJyeGthY3Yy/Y3EucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/lGgKUrLM6yB2AyV6DFn_qVir5Q851RFtfF12pDUj3XU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbXNna3Ny/OXFlbzJyeGthY3Yy/Y3EucG5n" alt="New Right Click" width="340" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Much better!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/N2vp10-FiR8AxJIwwQrnKAIL7RbhziPNLqsWfacZdwM/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbmxsaWY1/a2k4YmtjM3oxMWFh/b3UucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/N2vp10-FiR8AxJIwwQrnKAIL7RbhziPNLqsWfacZdwM/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbmxsaWY1/a2k4YmtjM3oxMWFh/b3UucG5n" alt="Windows 11" width="164" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>windows</category>
      <category>microsoft</category>
      <category>design</category>
    </item>
    <item>
      <title>Google Sign-In on Play Store — Troubleshooting Guide</title>
      <dc:creator>Abhishek singh</dc:creator>
      <pubDate>Thu, 07 May 2026 07:31:43 +0000</pubDate>
      <link>https://www.debug.school/abhishek/google-sign-in-on-play-store-troubleshooting-guide-4328</link>
      <guid>https://www.debug.school/abhishek/google-sign-in-on-play-store-troubleshooting-guide-4328</guid>
      <description>&lt;h1&gt;
  
  
  Google Sign-In on Play Store — Troubleshooting Guide
&lt;/h1&gt;

&lt;p&gt;A complete, copy-pasteable guide for resolving the &lt;strong&gt;#1 cause of Google&lt;br&gt;
Sign-In failure on Play Store builds&lt;/strong&gt;: SHA-1 fingerprint mismatch between&lt;br&gt;
your local builds and the Play App Signing key.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Confirmed working: &lt;strong&gt;2026-05-07&lt;/strong&gt;. Project: &lt;code&gt;motoshare_driver_admin&lt;/code&gt;,&lt;br&gt;
Cloud project: &lt;code&gt;motoshare-a61eb&lt;/code&gt; (display name &lt;code&gt;Motoshare&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  1. Symptoms
&lt;/h2&gt;

&lt;p&gt;The bug always presents the same way:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;flutter run&lt;/code&gt; (debug build on emulator)&lt;/td&gt;
&lt;td&gt;Google Sign-In &lt;strong&gt;works&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;flutter run --release&lt;/code&gt; on a USB-connected phone&lt;/td&gt;
&lt;td&gt;Google Sign-In &lt;strong&gt;works&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Side-loaded local APK / AAB&lt;/td&gt;
&lt;td&gt;Google Sign-In &lt;strong&gt;works&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App downloaded from Play Store / Internal testing track&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Google Sign-In silently fails&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Common error patterns reported by users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Sign-In failed" toast right after picking an account&lt;/li&gt;
&lt;li&gt;Account picker opens but never returns&lt;/li&gt;
&lt;li&gt;Plugin throws &lt;code&gt;PlatformException(sign_in_failed, …, status: 12500)&lt;/code&gt; or status &lt;code&gt;10&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your Sign-In fails on any other build (e.g. emulator), this is &lt;strong&gt;not the&lt;br&gt;
bug&lt;/strong&gt; described here — see Section 7 — When this guide doesn't apply.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Why it happens — the root cause
&lt;/h2&gt;

&lt;p&gt;When you upload an &lt;code&gt;.aab&lt;/code&gt; to Google Play, Google &lt;strong&gt;strips your signature and&lt;br&gt;
re-signs the app with their own key&lt;/strong&gt; (the "App signing by Google Play"&lt;br&gt;
feature, mandatory since 2021). This means three different signing keys are&lt;br&gt;
in play, with three different SHA-1 fingerprints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                                  SHA-1 fingerprint
                                                  ─────────────────
[1]  flutter run / debug builds      → debug key      AA:BB:CC:...
[2]  flutter build appbundle         → upload key     DD:EE:FF:...
[3]  Install from Play Store         → Google's       11:22:33:...
                                       app signing
                                       key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google Sign-In's Android client checks the running app's signature against&lt;br&gt;
the SHA-1s registered on its OAuth Android client. If the SHA-1 doesn't&lt;br&gt;
match, sign-in is rejected before any UI even appears.&lt;/p&gt;

&lt;p&gt;Most developers register only key &lt;a href="https://www.debug.schooldebug"&gt;1&lt;/a&gt; or &lt;a href="https://www.debug.schoolupload"&gt;2&lt;/a&gt;. Key [3] is&lt;br&gt;
controlled by Google and lives in Play Console — it's not on your machine,&lt;br&gt;
which is why this bug never reproduces locally.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Architecture: where SHA-1s need to be
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                                  ┌──────────────────────┐
                                                  │  Google Cloud OAuth  │
   Phone runs APK signed with one of these keys → │  Android client(s)   │
                                                  │  for package         │
   ┌──────────┐  ┌──────────┐  ┌──────────────┐   │  com.cotocus...      │
   │ Debug    │  │ Upload   │  │ App signing  │ ←─┤  Each client holds   │
   │ keystore │  │ keystore │  │ (Google)     │   │  exactly ONE SHA-1   │
   └──────────┘  └──────────┘  └──────────────┘   └──────────────────────┘
        │             │              │
        SHA-1 [1]    SHA-1 [2]      SHA-1 [3]   ← all three must be
                                                  registered against the
                                                  same package name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because each Android OAuth client only allows &lt;strong&gt;one&lt;/strong&gt; SHA-1, you need&lt;br&gt;
&lt;strong&gt;multiple Android OAuth clients with the same package name&lt;/strong&gt;, one per&lt;br&gt;
SHA-1. Google's auth servers accept whichever SHA-1 matches the running&lt;br&gt;
build.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. The fix — step by step
&lt;/h2&gt;

&lt;p&gt;Total time: ~5 minutes. &lt;strong&gt;No app rebuild or new release upload required.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1 — Get the &lt;strong&gt;App signing key&lt;/strong&gt; SHA-1 from Play Console
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://play.google.com/console" rel="noopener noreferrer"&gt;Play Console&lt;/a&gt; → select your app.&lt;/li&gt;
&lt;li&gt;Left sidebar → &lt;strong&gt;Test and release&lt;/strong&gt; → &lt;strong&gt;App integrity&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Find the &lt;strong&gt;App signing&lt;/strong&gt; section. Click to expand if needed.&lt;/li&gt;
&lt;li&gt;Two certificates appear:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App signing key certificate         ← copy SHA-1 from THIS one
  SHA-1: 11:22:33:44:55:66:...
  SHA-256: ...

Upload key certificate
  SHA-1: DD:EE:FF:...
  SHA-256: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Copy the &lt;strong&gt;SHA-1&lt;/strong&gt; under &lt;strong&gt;App signing key certificate&lt;/strong&gt;. It will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9A:04:19:E7:58:68:3A:70:7B:7D:5B:D1:28:91:36:5C:B1:3C:5E:D7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(40 hex chars separated by colons.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; copy from &lt;em&gt;App signing key certificate&lt;/em&gt;, &lt;strong&gt;not&lt;/strong&gt; from&lt;br&gt;
&lt;em&gt;Upload key certificate&lt;/em&gt;. They are different keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2 — Add a new Android OAuth client in Google Cloud Console
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt; → make sure
the project selector at the top shows the &lt;strong&gt;same project&lt;/strong&gt; that your app
uses for Sign-In. (For this repo: &lt;code&gt;Motoshare&lt;/code&gt; / &lt;code&gt;motoshare-a61eb&lt;/code&gt;.)&lt;/li&gt;
&lt;li&gt;Sidebar → &lt;strong&gt;APIs &amp;amp; Services&lt;/strong&gt; → &lt;strong&gt;Credentials&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Top bar → &lt;strong&gt;+ CREATE CREDENTIALS&lt;/strong&gt; → &lt;strong&gt;OAuth client ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill in:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Application type&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Android&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;your-app&amp;gt; (play signing)&lt;/code&gt; — anything descriptive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package name&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Same as your existing Android client&lt;/strong&gt; (e.g. &lt;code&gt;com.cotocus.motoshare_driver_admin&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-1 certificate fingerprint&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Paste the SHA-1 from Step 1&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now see &lt;strong&gt;two Android OAuth clients&lt;/strong&gt; in the Credentials list,&lt;br&gt;
both with the same package name but different SHA-1s. &lt;strong&gt;Keep both&lt;/strong&gt; — do&lt;br&gt;
not delete the original. The original SHA-1 still serves your local debug&lt;br&gt;
and upload-signed builds.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3 — Verify OAuth consent screen is published
&lt;/h3&gt;

&lt;p&gt;This is a frequent secondary cause of Play Store failures.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sidebar → &lt;strong&gt;Google Auth Platform&lt;/strong&gt; → &lt;strong&gt;Audience&lt;/strong&gt; (formerly "OAuth
consent screen").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publishing status&lt;/strong&gt; must be &lt;strong&gt;In production&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User type&lt;/strong&gt; should be &lt;strong&gt;External&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If status says &lt;strong&gt;Testing&lt;/strong&gt;, click &lt;strong&gt;PUBLISH APP&lt;/strong&gt; → confirm.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Apps in &lt;strong&gt;Testing&lt;/strong&gt; mode only allow sign-in for explicitly-added test&lt;br&gt;
users. Anyone else gets an error regardless of SHA-1.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Step 4 — Confirm the Web client ID in Flutter code
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;google_sign_in&lt;/code&gt; plugin needs the &lt;strong&gt;Web&lt;/strong&gt; client ID to issue ID tokens,&lt;br&gt;
&lt;strong&gt;not&lt;/strong&gt; the Android client ID. Open your code where &lt;code&gt;GoogleSignIn&lt;/code&gt; is&lt;br&gt;
configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/src/shared/providers.dart&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;_googleWebClientId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;'335553606969-k446g8jjeclomdbf4etgdgdgdfdddrhs5.apps.googleusercontent.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type is &lt;strong&gt;Web application&lt;/strong&gt; (not Android)&lt;/li&gt;
&lt;li&gt;It lives in the &lt;strong&gt;same Cloud project&lt;/strong&gt; as your Android clients&lt;/li&gt;
&lt;li&gt;The string matches the &lt;code&gt;Client ID&lt;/code&gt; shown for the Web OAuth client in
Cloud Console → Credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this repo it should match &lt;code&gt;motoshare-driver-admin-web&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 — Test
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No rebuild, no new release upload&lt;/strong&gt; — these changes are server-side.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wait &lt;strong&gt;5–10 minutes&lt;/strong&gt; for Google's auth servers to propagate.&lt;/li&gt;
&lt;li&gt;On a real phone:

&lt;ul&gt;
&lt;li&gt;Uninstall the Play Store version&lt;/li&gt;
&lt;li&gt;Re-install from Play Store (Internal testing track is fine)&lt;/li&gt;
&lt;li&gt;Tap Google Sign-In → pick account → should succeed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  5. Verifying you actually fixed it
&lt;/h2&gt;

&lt;p&gt;After ~10 minutes of normal use, return to the new OAuth client in Cloud&lt;br&gt;
Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APIs &amp;amp; Services → Credentials → click your new "(play signing)" client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at &lt;strong&gt;Last used date&lt;/strong&gt; in the right-hand panel. If it shows today's&lt;br&gt;
date, your Play Store APK is actively reaching this client and SHA-1&lt;br&gt;
matching is working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last used date    May 7, 2026 (Note: this data could be delayed by a day
                  or more.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That timestamp is the strongest single signal that the fix worked.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Common pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pitfall: copied the &lt;em&gt;Upload key&lt;/em&gt; SHA-1 instead of &lt;em&gt;App signing key&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Both certificates are listed on the same page in Play Console. They are&lt;br&gt;
different keys. Only the &lt;strong&gt;App signing key certificate&lt;/strong&gt; SHA-1 fixes this&lt;br&gt;
bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall: deleted the old Android client
&lt;/h3&gt;

&lt;p&gt;Don't. The original client's SHA-1 covers your &lt;strong&gt;debug&lt;/strong&gt; and &lt;strong&gt;release-built&lt;br&gt;
local&lt;/strong&gt; APKs. Deleting it breaks &lt;code&gt;flutter run&lt;/code&gt; and any side-loaded build.&lt;br&gt;
Keep both Android clients alive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall: pasted the SHA-1 into the wrong Cloud project
&lt;/h3&gt;

&lt;p&gt;Many teams have multiple Cloud projects (in this repo there's also a&lt;br&gt;
&lt;code&gt;Motoshare Old&lt;/code&gt;). The SHA-1 must go into the &lt;strong&gt;same project&lt;/strong&gt; that your&lt;br&gt;
Web client lives in — otherwise the Web ID token issued at runtime can't&lt;br&gt;
be matched to the Android client. Confirm the project selector at the top&lt;br&gt;
of Cloud Console matches the one referenced by &lt;code&gt;_googleWebClientId&lt;/code&gt; in&lt;br&gt;
your Flutter code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall: tried to "Link Cloud project" in Play Console and got "Project not found"
&lt;/h3&gt;

&lt;p&gt;That's a separate feature for the &lt;strong&gt;Play Integrity API&lt;/strong&gt;, unrelated to&lt;br&gt;
Google Sign-In. The error means your account isn't an Owner on the Cloud&lt;br&gt;
project. Skip this — Sign-In doesn't require it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall: still in Testing mode
&lt;/h3&gt;

&lt;p&gt;Even with the right SHA-1, an OAuth consent screen in &lt;strong&gt;Testing&lt;/strong&gt; mode&lt;br&gt;
blocks every Google account that isn't on the test users list. Push to&lt;br&gt;
&lt;strong&gt;In production&lt;/strong&gt; under Audience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall: passing the Android client ID as &lt;code&gt;serverClientId&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;google_sign_in&lt;/code&gt; plugin needs the &lt;strong&gt;Web&lt;/strong&gt; client ID. Android client&lt;br&gt;
IDs don't issue ID tokens. Symptom: account picker works, but the backend&lt;br&gt;
ID-token verification fails afterward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall: &lt;code&gt;google-services.json&lt;/code&gt; references the wrong project
&lt;/h3&gt;

&lt;p&gt;If your repo uses Firebase tooling, an outdated &lt;code&gt;android/app/google-services.json&lt;/code&gt;&lt;br&gt;
can pin Sign-In to a stale project. Re-download it from Firebase Console&lt;br&gt;
(or delete it entirely if you don't use Firebase — &lt;code&gt;google_sign_in&lt;/code&gt; works&lt;br&gt;
without it as long as &lt;code&gt;serverClientId&lt;/code&gt; is hard-coded).&lt;/p&gt;




&lt;h2&gt;
  
  
  7. When this guide doesn't apply
&lt;/h2&gt;

&lt;p&gt;This guide solves &lt;strong&gt;only&lt;/strong&gt; the Play-Store-specific Sign-In failure. If&lt;br&gt;
Sign-In is broken in &lt;strong&gt;all&lt;/strong&gt; environments (emulator, local APK, Play&lt;br&gt;
Store), you have a different bug. Likely culprits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrong package name in the OAuth client (must exactly match &lt;code&gt;applicationId&lt;/code&gt; in &lt;code&gt;android/app/build.gradle.kts&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;serverClientId&lt;/code&gt; pointing at a deleted or wrong-project Web client&lt;/li&gt;
&lt;li&gt;OAuth consent screen requesting unverified sensitive scopes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;google_sign_in&lt;/code&gt; plugin version mismatch with &lt;code&gt;google_sign_in_android&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MainActivity&lt;/code&gt; not extending &lt;code&gt;FlutterFragmentActivity&lt;/code&gt; (for plugins that need it)&lt;/li&gt;
&lt;li&gt;Backend rejecting the ID token (audience or issuer mismatch in Keycloak)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;code&gt;adb logcat | grep -iE "googlesignin|gms|auth"&lt;/code&gt; while reproducing the&lt;br&gt;
failure to capture the exact error code. Status code reference:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;12500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SIGN_IN_FAILED&lt;/code&gt; — usually SHA-1 / package mismatch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;12501&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User cancelled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;12502&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sign-in already in progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DEVELOPER_ERROR&lt;/code&gt; — config wrong (SHA-1, package, or client ID)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Status &lt;code&gt;10&lt;/code&gt; and &lt;code&gt;12500&lt;/code&gt; are the SHA-1-related ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Reference: this repo's exact configuration (post-fix)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloud project:     Motoshare (motoshare-a61eb), project number 338933606969

OAuth 2.0 Client IDs:
  motoshare-driver-admin-web                Web      338933606969-k446...
  motoshare-driver-admin-app                Android  88:6C:D0:27:21:3F:2E:DA:2E:EE:E3:A6:51:A0:4F:50:1B:F1:F3:20  (debug/upload)
  motoshare-driver-admin-app (play signing) Android  9A:04:19:E7:58:48:3A:70:6B:7D:5B:D1:28:91:36:5C:B1:4C:3E:D7  (Play App Signing)

Both Android clients use package: com.cotocus.motoshare_driver_admin

Flutter code (lib/src/shared/providers.dart):
  serverClientId = '338933606969-k446g8jjeclomdbf4ete2efmgr06rhs5.apps.googleusercontent.com'
                   (matches motoshare-driver-admin-web)

Audience: External, Publishing status: In production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Quick-start checklist for future apps
&lt;/h2&gt;

&lt;p&gt;When wiring Google Sign-In on a new Android app, do all of this &lt;strong&gt;before&lt;/strong&gt;&lt;br&gt;
the first Play Store release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Create one &lt;strong&gt;Android&lt;/strong&gt; OAuth client with your &lt;strong&gt;debug&lt;/strong&gt; keystore SHA-1&lt;/li&gt;
&lt;li&gt;[ ] Create one &lt;strong&gt;Web&lt;/strong&gt; OAuth client → use its Client ID as &lt;code&gt;serverClientId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Set OAuth consent screen → &lt;strong&gt;External&lt;/strong&gt;, &lt;strong&gt;In production&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Upload first AAB to Play Console → enable App signing&lt;/li&gt;
&lt;li&gt;[ ] Read the &lt;strong&gt;App signing key SHA-1&lt;/strong&gt; from Play Console → App integrity&lt;/li&gt;
&lt;li&gt;[ ] Create a &lt;strong&gt;second&lt;/strong&gt; Android OAuth client with that SHA-1, same package name&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.debug.schoolOptional"&gt; &lt;/a&gt; Create a third Android client with your &lt;strong&gt;upload key&lt;/strong&gt; SHA-1 if you sometimes share an upload-signed APK directly with testers&lt;/li&gt;
&lt;li&gt;[ ] Confirm Sign-In works on Play Store install (Last used date updates within minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tracking SHA-1s up front saves a release cycle of debugging later.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Reusing this guide
&lt;/h2&gt;

&lt;p&gt;This guide is project-agnostic apart from Section 8. To adapt it for&lt;br&gt;
another app, change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Cloud project ID, project number, and client IDs in Section 8&lt;/li&gt;
&lt;li&gt;The package name (&lt;code&gt;com.cotocus.motoshare_driver_admin&lt;/code&gt;) in Section 4&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;serverClientId&lt;/code&gt; constant location and value in Steps 4 and Section 8&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Why Google Sign-In Works on Your Emulator But Breaks on Play Store</title>
      <dc:creator>Abhishek singh</dc:creator>
      <pubDate>Thu, 07 May 2026 07:29:41 +0000</pubDate>
      <link>https://www.debug.school/abhishek/why-google-sign-in-works-on-your-emulator-but-breaks-on-play-store-28jo</link>
      <guid>https://www.debug.school/abhishek/why-google-sign-in-works-on-your-emulator-but-breaks-on-play-store-28jo</guid>
      <description>&lt;h1&gt;
  
  
  Why Google Sign-In Works on Your Emulator But Breaks on Play Store — and How to Fix It in 5 Minutes
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Suggested tags: #Flutter, #Android, #GoogleSignIn, #PlayStore, #OAuth, #DebuggingDiaries&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reading time: ~6 minutes&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Bug That Only Shows Up After You Ship
&lt;/h2&gt;

&lt;p&gt;Picture this: you've spent two weeks wiring Google Sign-In into your&lt;br&gt;
Android app. It works perfectly on your emulator. It works on your&lt;br&gt;
teammate's Pixel. The QA build, side-loaded over USB, works. You ship a&lt;br&gt;
release build to Google Play, hit the green publish button, and crack open&lt;br&gt;
a celebratory chai.&lt;/p&gt;

&lt;p&gt;Five minutes later, a tester pings you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hey, I downloaded the app from Play Store and Google Sign-In just…&lt;br&gt;
doesn't do anything. Account picker doesn't even open."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You blink. You check your phone. You uninstall the dev build, install from&lt;br&gt;
Play Store… and watch the same thing happen. &lt;strong&gt;The exact same APK that&lt;br&gt;
worked thirty minutes ago is now broken.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Welcome to one of Android's most confusing OAuth bugs. The good news?&lt;br&gt;
There is one specific cause, and it takes about five minutes to fix once&lt;br&gt;
you know what to do. The bad news is that almost no one finds the answer&lt;br&gt;
on the first Stack Overflow page they read, because the symptoms point at&lt;br&gt;
ten different culprits.&lt;/p&gt;

&lt;p&gt;This article walks through what's actually going wrong, why it only&lt;br&gt;
happens on Play Store builds, and the precise fix that resolves it without&lt;br&gt;
a single line of code change or a new release upload.&lt;/p&gt;


&lt;h2&gt;
  
  
  Three Signatures, One Confused OAuth Client
&lt;/h2&gt;

&lt;p&gt;Here's the thing nobody tells you when you set up Google Sign-In: your&lt;br&gt;
Android app gets signed with &lt;strong&gt;at least three different cryptographic&lt;br&gt;
keys&lt;/strong&gt; during its lifetime, and Google checks all of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                                  SHA-1 fingerprint
                                                  ─────────────────
[1]  flutter run / debug builds      → debug key      AA:BB:CC:...
[2]  flutter build appbundle         → upload key     DD:EE:FF:...
[3]  Install from Play Store         → Google's       11:22:33:...
                                       app signing
                                       key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run your app from Android Studio or &lt;code&gt;flutter run&lt;/code&gt;, the APK is&lt;br&gt;
signed with your machine's local &lt;strong&gt;debug keystore&lt;/strong&gt;. That's signature [1].&lt;/p&gt;

&lt;p&gt;When you build a release AAB and upload it to Play Store, you sign it with&lt;br&gt;
your &lt;strong&gt;upload keystore&lt;/strong&gt; — usually one you generated when you first set up&lt;br&gt;
Play signing. That's signature [2].&lt;/p&gt;

&lt;p&gt;And here's the kicker: &lt;strong&gt;Google does not actually distribute the AAB you&lt;br&gt;
uploaded.&lt;/strong&gt; Since 2021, Play Console mandates a feature called &lt;em&gt;App&lt;br&gt;
signing by Google Play&lt;/em&gt;. When you upload your &lt;code&gt;.aab&lt;/code&gt;, Google strips your&lt;br&gt;
signature, repackages the bundle into APKs for different device profiles,&lt;br&gt;
and &lt;strong&gt;re-signs them with their own internal key&lt;/strong&gt;. That's signature [3].&lt;br&gt;
You never see this key. You can't access it. It lives entirely on&lt;br&gt;
Google's servers.&lt;/p&gt;

&lt;p&gt;So when a real user installs your app from the Play Store, the APK&lt;br&gt;
running on their phone is signed with a key your machine has never&lt;br&gt;
touched.&lt;/p&gt;

&lt;p&gt;Why does this matter for Google Sign-In? Because Google's OAuth servers&lt;br&gt;
verify the signature of the calling app on every Sign-In request. The&lt;br&gt;
SHA-1 fingerprint of whichever key signed the running APK has to match&lt;br&gt;
one that's registered against your Android OAuth client in Google Cloud&lt;br&gt;
Console.&lt;/p&gt;

&lt;p&gt;When you set up Google Sign-In during development, you almost certainly&lt;br&gt;
registered key &lt;a href="https://www.debug.schooldebug"&gt;1&lt;/a&gt; or key &lt;a href="https://www.debug.schoolupload"&gt;2&lt;/a&gt;. You probably never&lt;br&gt;
registered key [3], because Play hadn't generated it yet — the App&lt;br&gt;
signing key is created the first time you upload an AAB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the bug.&lt;/strong&gt; Your Cloud Console only knows about [1] and/or [2].&lt;br&gt;
The Play Store APK is signed with [3]. Google's OAuth servers see a key&lt;br&gt;
they don't recognize and silently drop the request. From the user's&lt;br&gt;
perspective, Sign-In just… does nothing.&lt;/p&gt;


&lt;h2&gt;
  
  
  Symptoms That Lead Everyone Astray
&lt;/h2&gt;

&lt;p&gt;The reason this bug eats hours of debugging time is that the failure mode&lt;br&gt;
is almost too clean. There's no crash. No red snackbar. No useful logcat&lt;br&gt;
line that says "SHA-1 mismatch."&lt;/p&gt;

&lt;p&gt;What you see instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Google account picker briefly flickers, then closes&lt;/li&gt;
&lt;li&gt;A toast saying &lt;em&gt;"Sign-in failed"&lt;/em&gt; appears with no further detail&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PlatformException(sign_in_failed, …, status: 12500)&lt;/code&gt; — but only if
you're already running through &lt;code&gt;adb logcat&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sometimes status code &lt;code&gt;10&lt;/code&gt;, which is documented as &lt;code&gt;DEVELOPER_ERROR&lt;/code&gt; and
is even more cryptic than &lt;code&gt;12500&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Status code &lt;code&gt;12500&lt;/code&gt; and &lt;code&gt;10&lt;/code&gt; are the two fingerprints of this specific&lt;br&gt;
bug. If you grep your logcat output and see either, stop reading Stack&lt;br&gt;
Overflow threads about scopes, plugin versions, or token expiry — you&lt;br&gt;
have a SHA-1 problem.&lt;/p&gt;

&lt;p&gt;The other reason people miss this for so long: &lt;strong&gt;everything works in&lt;br&gt;
development.&lt;/strong&gt; Emulator? Works. Local USB build? Works. Internal testing&lt;br&gt;
track &lt;em&gt;if you side-load the APK from a download link&lt;/em&gt;? Works. The bug&lt;br&gt;
only appears once Google's app-signing pipeline gets involved, and that&lt;br&gt;
only happens when a real user clicks Install on the Play Store listing.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Fix, Plain and Simple
&lt;/h2&gt;

&lt;p&gt;Now that we've named the cause, the fix is mechanical. There are exactly&lt;br&gt;
two pieces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find out what SHA-1 Google's app-signing key has&lt;/strong&gt; (this lives in
Play Console)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tell Google's OAuth servers about it&lt;/strong&gt; (by registering it on a new
Android OAuth client in Cloud Console)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No code changes. No rebuild. No new release upload. The whole thing is&lt;br&gt;
configuration on Google's side, and the change propagates to your live&lt;br&gt;
Play Store build within minutes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1 — Get the App Signing key SHA-1 from Play Console
&lt;/h3&gt;

&lt;p&gt;Open &lt;a href="https://play.google.com/console" rel="noopener noreferrer"&gt;Play Console&lt;/a&gt;, select your app,&lt;br&gt;
and navigate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test and release  →  App integrity  →  App signing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see two certificates listed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App signing key certificate&lt;/strong&gt; ← copy the SHA-1 from this one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload key certificate&lt;/strong&gt; ← &lt;em&gt;not&lt;/em&gt; this one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distinction matters. The Upload key is yours; the App signing key is&lt;br&gt;
Google's. Only the App signing key SHA-1 fixes this bug. Both certificates&lt;br&gt;
are listed on the same page, which is exactly why people copy the wrong&lt;br&gt;
one.&lt;/p&gt;

&lt;p&gt;The SHA-1 looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9A:04:19:E7:48:48:3A:70:6B:5D:5B:h1:28:91:36:5C:B1:4C:3E:D7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;40 hexadecimal characters separated by colons. Copy it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Register a new Android OAuth client with that SHA-1
&lt;/h3&gt;

&lt;p&gt;Open &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;, make sure&lt;br&gt;
the project selector at the top points to &lt;strong&gt;the same Cloud project your&lt;br&gt;
app uses for Sign-In&lt;/strong&gt;. (This is a common pitfall — if your team has&lt;br&gt;
multiple Cloud projects, getting this wrong silently breaks everything.)&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APIs &amp;amp; Services  →  Credentials  →  + CREATE CREDENTIALS  →  OAuth client ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fill in the form:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Application type&lt;/td&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;your-app&amp;gt; (play signing)&lt;/code&gt; — anything descriptive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package name&lt;/td&gt;
&lt;td&gt;The exact &lt;code&gt;applicationId&lt;/code&gt; from your &lt;code&gt;build.gradle&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SHA-1 certificate fingerprint&lt;/td&gt;
&lt;td&gt;The one you copied from Play Console&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Click Create.&lt;/p&gt;

&lt;p&gt;You will now have &lt;strong&gt;two Android OAuth clients with the same package name&lt;/strong&gt;&lt;br&gt;
in your project — one with your debug/upload SHA-1, one with the Play&lt;br&gt;
App Signing SHA-1. &lt;strong&gt;Don't delete the original.&lt;/strong&gt; Both serve a purpose.&lt;br&gt;
Google's OAuth servers accept whichever SHA-1 matches the running build,&lt;br&gt;
so this configuration covers all three signing keys at once.&lt;/p&gt;

&lt;p&gt;The reason you need two separate clients is a quirk of Cloud Console:&lt;br&gt;
each Android OAuth client allows exactly one SHA-1. Firebase users get a&lt;br&gt;
nicer UI here (multiple fingerprints per app), but if you're working with&lt;br&gt;
the Cloud Console directly, the workaround is to make a second client.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3 — Make sure your OAuth consent screen is published
&lt;/h3&gt;

&lt;p&gt;This is the secondary cause that bites about a third of teams who fix&lt;br&gt;
the SHA-1 issue. Even with a perfect SHA-1 setup, if your OAuth consent&lt;br&gt;
screen is in &lt;strong&gt;Testing&lt;/strong&gt; mode, only emails on the test users list can&lt;br&gt;
sign in. Everyone else gets an error indistinguishable from the SHA-1&lt;br&gt;
bug.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APIs &amp;amp; Services  →  OAuth consent screen  →  Audience
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Google recently renamed parts of this UI to &lt;em&gt;Google Auth Platform&lt;/em&gt;; the&lt;br&gt;
location of the &lt;strong&gt;Audience&lt;/strong&gt; sub-page is the same.)&lt;/p&gt;

&lt;p&gt;Look at &lt;strong&gt;Publishing status&lt;/strong&gt;. If it says &lt;em&gt;Testing&lt;/em&gt;, click &lt;strong&gt;PUBLISH&lt;br&gt;
APP&lt;/strong&gt; and confirm. For apps requesting only basic scopes (&lt;code&gt;email&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;profile&lt;/code&gt;, &lt;code&gt;openid&lt;/code&gt;), publishing is instant. For apps requesting&lt;br&gt;
sensitive or restricted scopes, Google will ask for verification, which&lt;br&gt;
can take days — but Sign-In flows for typical apps don't need those&lt;br&gt;
scopes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4 — Verify your Flutter code uses the Web client ID
&lt;/h3&gt;

&lt;p&gt;This one isn't strictly part of the fix, but it's worth confirming. The&lt;br&gt;
&lt;code&gt;google_sign_in&lt;/code&gt; plugin needs the &lt;strong&gt;Web&lt;/strong&gt; OAuth client ID to issue ID&lt;br&gt;
tokens, &lt;strong&gt;not&lt;/strong&gt; the Android client ID. If your code has the Android&lt;br&gt;
client ID in &lt;code&gt;serverClientId&lt;/code&gt;, the account picker will work but the&lt;br&gt;
backend ID-token verification will fail.&lt;/p&gt;

&lt;p&gt;Open the file where you initialize &lt;code&gt;GoogleSignIn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Should look something like this — the ID belongs to a Web-type client.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;_googleWebClientId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;'338935556969-k446g8gdgbdxgbdgvdefmgr06rhs5.apps.googleusercontent.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-reference it with Cloud Console. The corresponding OAuth client&lt;br&gt;
should be type &lt;strong&gt;Web application&lt;/strong&gt;, not Android. If it's Android, that's&lt;br&gt;
your second bug.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5 — Test
&lt;/h3&gt;

&lt;p&gt;This is the part that surprises people the most: &lt;strong&gt;you don't need to&lt;br&gt;
rebuild&lt;/strong&gt;. The fix is purely server-side on Google's auth infrastructure.&lt;br&gt;
Wait 5 to 10 minutes for propagation, then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uninstall the Play Store version of your app from a real phone&lt;/li&gt;
&lt;li&gt;Re-install from Play Store (Internal testing track is fine)&lt;/li&gt;
&lt;li&gt;Open the app, tap Google Sign-In, pick an account&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It should now work end-to-end. The same &lt;code&gt;.aab&lt;/code&gt; you uploaded yesterday now&lt;br&gt;
authenticates correctly because Google's OAuth servers learned a new&lt;br&gt;
SHA-1.&lt;/p&gt;


&lt;h2&gt;
  
  
  How to Verify You Actually Fixed It
&lt;/h2&gt;

&lt;p&gt;After ten minutes of normal use, return to your new OAuth client in Cloud&lt;br&gt;
Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APIs &amp;amp; Services  →  Credentials  →  click your "(play signing)" client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the right-hand panel you'll see a &lt;strong&gt;Last used date&lt;/strong&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last used date    May 7, 2026 (Note: this data could be delayed by a day
                  or more.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it shows today, congratulations: your Play Store APK is hitting this&lt;br&gt;
exact OAuth client and the SHA-1 is matching successfully. That timestamp&lt;br&gt;
is the single strongest signal that the fix worked. Until I saw it on my&lt;br&gt;
own client, I couldn't quite believe a config change was enough — and&lt;br&gt;
neither will the next person you tell about this fix.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pitfalls Worth Calling Out
&lt;/h2&gt;

&lt;p&gt;A few traps that snagged me along the way and might snag you too:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't copy the Upload key SHA-1.&lt;/strong&gt; Both certificates are right next to&lt;br&gt;
each other on the App signing page. Only the App signing key SHA-1&lt;br&gt;
matters here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't delete the old Android OAuth client.&lt;/strong&gt; It still serves your&lt;br&gt;
debug builds and any side-loaded local APKs. Two clients, same package&lt;br&gt;
name, different SHA-1s, both alive — that's correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make sure you're in the right Cloud project.&lt;/strong&gt; Teams with multiple&lt;br&gt;
Cloud projects (legacy ones, separate projects per environment) often&lt;br&gt;
land on the wrong one. The Web client ID hard-coded in your Flutter app&lt;br&gt;
tells you which project Sign-In is bound to — match it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't try to "Link Cloud project" in Play Console looking for this&lt;br&gt;
fix.&lt;/strong&gt; That feature is for the Play Integrity API, which is unrelated to&lt;br&gt;
Google Sign-In, and the most common error you'll see ("Project not&lt;br&gt;
found") just means your account isn't an Owner on the Cloud project. You&lt;br&gt;
don't need to link anything for Sign-In to work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't pass the Android client ID as &lt;code&gt;serverClientId&lt;/code&gt;.&lt;/strong&gt; It has to be&lt;br&gt;
the Web client ID. The plugin will sometimes appear to work and then&lt;br&gt;
break in obscure ways during ID token verification.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Lesson In Distributed Trust
&lt;/h2&gt;

&lt;p&gt;The deeper takeaway here is that mobile app authentication, post-Play&lt;br&gt;
App Signing, involves &lt;em&gt;three&lt;/em&gt; trusted parties: your team's local&lt;br&gt;
keystores, Google Play's signing infrastructure, and Google's identity&lt;br&gt;
servers. They have to agree on the identity of your app, and the only&lt;br&gt;
way they agree is via SHA-1 fingerprints registered against an OAuth&lt;br&gt;
client.&lt;/p&gt;

&lt;p&gt;It's elegant when it works. It's baffling when it doesn't, because the&lt;br&gt;
machinery is invisible until something breaks. The first time you ship a&lt;br&gt;
production Android app with Google Sign-In, this bug is almost&lt;br&gt;
inevitable. The good news is that you only need to learn it once.&lt;/p&gt;

&lt;p&gt;If you're setting up Google Sign-In on a new Android app, here's the&lt;br&gt;
checklist I now run &lt;strong&gt;before&lt;/strong&gt; the first Play Store release:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One Android OAuth client with the debug keystore SHA-1&lt;/li&gt;
&lt;li&gt;One Android OAuth client with the upload keystore SHA-1 (if you ever
share upload-signed APKs directly with testers)&lt;/li&gt;
&lt;li&gt;One Web OAuth client → use its Client ID as &lt;code&gt;serverClientId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;OAuth consent screen → External, In production&lt;/li&gt;
&lt;li&gt;Upload first AAB, enable App signing, immediately read the App
signing key SHA-1 from Play Console&lt;/li&gt;
&lt;li&gt;Create a third Android OAuth client with that SHA-1, same package name&lt;/li&gt;
&lt;li&gt;Confirm Sign-In works on Play Store install — Last used date updates
within minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tracking all the SHA-1s up front saves a release cycle of debugging&lt;br&gt;
later, and you'll never wonder again why something that works on three&lt;br&gt;
separate emulators doesn't work after Play touched it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;If this article saved you an evening, pass it on to whoever sets up&lt;br&gt;
Sign-In on the next Android app at your company. The official Google&lt;br&gt;
documentation describes most of these moving parts individually, but I&lt;br&gt;
have yet to see them connected in one place — and the failure mode is&lt;br&gt;
common enough that I suspect every Android team rediscovers it&lt;br&gt;
independently.&lt;/p&gt;

&lt;p&gt;Five minutes, two new lines in Cloud Console, no rebuild. That's all it&lt;br&gt;
takes to turn "Sign-In is broken on Play Store" into a story you tell&lt;br&gt;
junior devs at standup.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Win Updates via PS1</title>
      <dc:creator>Suyash Sambhare</dc:creator>
      <pubDate>Wed, 22 Apr 2026 03:07:02 +0000</pubDate>
      <link>https://www.debug.school/suyash/win-updates-via-ps1-51gb</link>
      <guid>https://www.debug.school/suyash/win-updates-via-ps1-51gb</guid>
      <description>&lt;p&gt;PowerShell file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sfc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/scannow&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;chkdsk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/scan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/perf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;winget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--all&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;install-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PSWindowsUpdate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;import-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PSWindowsUpdate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;get-WindowsUpdate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AcceptAll&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;shutdown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/r&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.debug.school/images/Uv2LWqoLI0EG9Jse57Fcj_eO9CpKxUE4kwC7Z0hqUyE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvMjdpZXRz/cWZjZ3VsbG80bmV1/bGcucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/Uv2LWqoLI0EG9Jse57Fcj_eO9CpKxUE4kwC7Z0hqUyE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvMjdpZXRz/cWZjZ3VsbG80bmV1/bGcucG5n" alt="Art6" width="798" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Requires reboot&lt;/p&gt;

</description>
      <category>devops</category>
      <category>updates</category>
      <category>powershell</category>
      <category>windows</category>
    </item>
    <item>
      <title>AWS Secrets Manager in GitLab CI/CD</title>
      <dc:creator>Suyash Sambhare</dc:creator>
      <pubDate>Wed, 15 Apr 2026 05:45:02 +0000</pubDate>
      <link>https://www.debug.school/suyash/aws-secrets-manager-in-gitlab-cicd-35cc</link>
      <guid>https://www.debug.school/suyash/aws-secrets-manager-in-gitlab-cicd-35cc</guid>
      <description>&lt;h1&gt;
  
  
  Using AWS Secrets Manager in GitLab CI/CD
&lt;/h1&gt;

&lt;p&gt;GitLab lets you &lt;strong&gt;fetch secrets directly from AWS Secrets Manager at job runtime&lt;/strong&gt;, instead of hard‑coding or manually syncing secrets into GitLab CI variables.&lt;/p&gt;

&lt;p&gt;This gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Centralized secret management (AWS)&lt;/li&gt;
&lt;li&gt;  Short‑lived credentials (OIDC)&lt;/li&gt;
&lt;li&gt;  No secrets stored permanently in GitLab&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How the Integration Works (Architecture)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;GitLab Runner&lt;/strong&gt; authenticates to AWS

&lt;ul&gt;
&lt;li&gt;  Via &lt;strong&gt;IAM Role&lt;/strong&gt; attached to the runner host&lt;/li&gt;
&lt;li&gt;  Or via &lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt; using GitLab ID tokens&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; During the &lt;strong&gt;“Resolving secrets”&lt;/strong&gt; phase:

&lt;ul&gt;
&lt;li&gt;  Runner calls AWS Secrets Manager&lt;/li&gt;
&lt;li&gt;  Fetches the secret value&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; GitLab:

&lt;ul&gt;
&lt;li&gt;  Writes the secret to a &lt;strong&gt;temporary file&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Exposes the file path as an &lt;strong&gt;environment variable&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Your job uses the secret&lt;/li&gt;
&lt;li&gt; Temp files are removed when the job finishes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Secrets never appear in the job logs unless you echo them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. IAM Role
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No tokens or role assumptions needed.&lt;/p&gt;

&lt;p&gt;With Kubernetes runners, the IAM role must be on the &lt;strong&gt;runner manager&lt;/strong&gt;, not just the pod.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. OpenID Connect
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  GitLab issues a short‑lived OIDC token&lt;/li&gt;
&lt;li&gt;  AWS STS exchanges it for temporary credentials&lt;/li&gt;
&lt;li&gt;  AWS_ROLE_ARN defines what role is assumed
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id_tokens&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_ID_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts.amazonaws.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_ROLE_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::123456789012:role/gitlab-secrets-role&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Defining Secrets in Jobs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Standard Form
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets/database&lt;/span&gt;
      &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;password&lt;/code&gt; field is extracted from JSON&lt;/li&gt;
&lt;li&gt;  Value is exposed as &lt;code&gt;$DATABASE_PASSWORD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;file: false&lt;/code&gt; → variable, not file path&lt;/li&gt;
&lt;/ul&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets/api#api_key&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Format:&lt;br&gt;
    secret-name[#json-field]&lt;/p&gt;


&lt;h2&gt;
  
  
  Working with Full JSON Secrets
&lt;/h2&gt;

&lt;p&gt;If you don’t specify &lt;code&gt;field&lt;/code&gt;, GitLab retrieves the &lt;strong&gt;entire value&lt;/strong&gt;.&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="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;FULL_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets/api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FULL_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.api_key'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Secret Versioning
&lt;/h2&gt;

&lt;p&gt;You can pin secrets to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A &lt;strong&gt;version stage&lt;/strong&gt; (recommended)&lt;/li&gt;
&lt;li&gt;  A &lt;strong&gt;specific version ID&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version_stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWSCURRENT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&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="na"&gt;version_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;01234567-89ab-cdef-0123-456789abcdef&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You cannot specify both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cross‑Account Access
&lt;/h2&gt;

&lt;p&gt;To read secrets from another AWS account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use &lt;strong&gt;OIDC&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Use the &lt;strong&gt;full secret ARN&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:secretsmanager:us-east-1:987654321098:secret:shared-api-keys-AbCdEf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IAM role must trust:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  GitLab’s OIDC provider&lt;/li&gt;
&lt;li&gt;  The specific GitLab project/group&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Per‑Secret Overrides (Advanced)
&lt;/h2&gt;

&lt;p&gt;You can override AWS settings &lt;strong&gt;per secret&lt;/strong&gt;:&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="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-app-secrets/database&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-west-1&lt;/span&gt;
  &lt;span class="na"&gt;role_arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::123456789012:role/eu-role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  File vs Variable (&lt;code&gt;file: true | false&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Default behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  GitLab writes secret to a temp file&lt;/li&gt;
&lt;li&gt;  Environment variable contains &lt;strong&gt;file path&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DATABASE_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want the raw value:&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="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DATABASE_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File mode is safer for binaries &amp;amp; large secrets.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.debug.school/images/8oeaNiIZtq8GHJssMI6HeJvbuORImPiesbZ3kKXvEG4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvb3FqMWFp/dW5yb3A4NGh3MW5r/NTkucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/8oeaNiIZtq8GHJssMI6HeJvbuORImPiesbZ3kKXvEG4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvb3FqMWFp/dW5yb3A4NGh3MW5r/NTkucG5n" alt="AWS" width="450" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Use AWS Secrets Manager secrets in GitLab CI/CD
&lt;/h1&gt;

&lt;p&gt;You can use secrets stored in &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;&lt;br&gt;
in your GitLab CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have access to AWS Secrets Manager in your AWS account.&lt;/li&gt;
&lt;li&gt;Configure authentication using one of the following methods:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Role&lt;/strong&gt;: Use the IAM role assigned to your GitLab Runner instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenID Connect&lt;/strong&gt;: &lt;a href="//../cloud_services/aws/_index.md"&gt;Configure OpenID Connect in AWS&lt;/a&gt; to retrieve temporary credentials.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;a href="//../variables/_index.md#for-a-project"&gt;CI/CD variables to your project&lt;/a&gt; to provide details about your AWS configuration:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS_REGION&lt;/code&gt;: The AWS region where your secrets are stored.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_ROLE_ARN&lt;/code&gt;: The ARN of the AWS IAM role to assume (required when using OpenID Connect).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_ROLE_SESSION_NAME&lt;/code&gt;: Optional. Custom session name for the assumed role.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Use AWS Secrets Manager secrets in a CI/CD job
&lt;/h2&gt;
&lt;h3&gt;
  
  
  With IAM Role authentication
&lt;/h3&gt;

&lt;p&gt;You can use a secret stored in AWS Secrets Manager in a job by defining it with the&lt;br&gt;
&lt;code&gt;aws_secrets_manager&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;This method uses the IAM role assigned to your GitLab Runner instance. When using the&lt;br&gt;
&lt;a href="https://docs.gitlab.com/runner/executors/kubernetes/" rel="noopener noreferrer"&gt;Kubernetes executor&lt;/a&gt; or &lt;a href="https://docs.gitlab.com/runner/runner_autoscale/" rel="noopener noreferrer"&gt;autoscaling&lt;/a&gt;,&lt;br&gt;
make sure the IAM role is applied to your runner manager.&lt;/p&gt;

&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitLab Runner 18.3 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;

&lt;span class="na"&gt;database-migration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Running database migration..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mysql -h $DB_HOST -u $DB_USER -p$DATABASE_PASSWORD &amp;lt; migration.sql&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Migration completed successfully."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  With OpenID Connect authentication
&lt;/h3&gt;

&lt;p&gt;For enhanced security, you can use OpenID Connect to authenticate with AWS and assume a specific IAM role.&lt;br&gt;
By default, the runner looks for an ID token named &lt;code&gt;AWS_ID_TOKEN&lt;/code&gt;. For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;AWS_ROLE_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::123456789012:role/gitlab-secrets-role'&lt;/span&gt;

&lt;span class="na"&gt;database-migration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id_tokens&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;AWS_ID_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts.amazonaws.com'&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Connecting to production database..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;psql postgresql://$DB_USER:$DATABASE_PASSWORD@$DB_HOST:5432/$DB_NAME -c "SELECT version();"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Database connection successful."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also specify a custom token using the &lt;code&gt;token&lt;/code&gt; option. For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;AWS_ROLE_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::123456789012:role/gitlab-secrets-role'&lt;/span&gt;

&lt;span class="na"&gt;database-migration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id_tokens&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CUSTOM_AWS_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts.amazonaws.com'&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CUSTOM_AWS_TOKEN&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Connecting to production database with custom token..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;psql postgresql://$DB_USER:$DATABASE_PASSWORD@$DB_HOST:5432/$DB_NAME -c "SELECT version();"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Database connection successful."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Short form syntax
&lt;/h3&gt;

&lt;p&gt;You can use a simplified syntax by specifying the secret ID as a string.&lt;br&gt;
You can optionally specify a field by separating it with a &lt;code&gt;#&lt;/code&gt; character.&lt;br&gt;
For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;

&lt;span class="na"&gt;api-deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app-secrets/api#api_key'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;FULL_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app-secrets/api'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying API with specific field..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl --header "Authorization&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer $API_KEY" https://api.example.com/deploy&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Using full secret..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl --header "Authorization&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer $(cat $FULL_SECRET | jq --raw-output '.api_key')" https://api.example.com/status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Secret versioning
&lt;/h2&gt;

&lt;p&gt;AWS Secrets Manager supports multiple versions of secrets. You can specify a particular version&lt;br&gt;
using either &lt;code&gt;version_id&lt;/code&gt; or &lt;code&gt;version_stage&lt;/code&gt;. For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;

&lt;span class="na"&gt;production-deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod-app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
        &lt;span class="na"&gt;version_stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWSCURRENT'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;STAGING_DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod-app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
        &lt;span class="na"&gt;version_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;01234567-89ab-cdef-0123-456789abcdef'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying to production with current secret version..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy-prod.sh --db-password $DATABASE_PASSWORD&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Testing with specific secret version..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test-with-version.sh --db-password $STAGING_DATABASE_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cross-account secret access
&lt;/h2&gt;

&lt;p&gt;To retrieve secrets from another AWS account, you must use the full ARN.&lt;br&gt;
For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;AWS_ROLE_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::123456789012:role/cross-account-secrets-role'&lt;/span&gt;

&lt;span class="na"&gt;cross-account-deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id_tokens&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;AWS_ID_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts.amazonaws.com'&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;SHARED_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:secretsmanager:us-east-1:987654321098:secret:shared-api-keys-AbCdEf'&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;production_key'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Accessing shared secret from another account..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl --header "Authorization&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer $SHARED_API_KEY" https://shared-api.example.com/deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Per-secret configuration overrides
&lt;/h2&gt;

&lt;p&gt;You can override global AWS settings on a per-secret basis. For example:&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="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;AWS_ROLE_ARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::123456789012:role/default-role'&lt;/span&gt;

&lt;span class="na"&gt;multi-region-deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id_tokens&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;AWS_ID_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts.amazonaws.com'&lt;/span&gt;
    &lt;span class="na"&gt;EU_AWS_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts.amazonaws.com'&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;EU_DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
        &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu-west-1'&lt;/span&gt;
        &lt;span class="na"&gt;role_arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::123456789012:role/eu-deployment-role'&lt;/span&gt;
        &lt;span class="na"&gt;role_session_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gitlab-eu-deployment'&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$EU_AWS_TOKEN&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;US_DATABASE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aws_secrets_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secret_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-app-secrets/database&lt;/span&gt;
        &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password'&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying to EU region..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy-to-eu.sh --db-password $EU_DATABASE_PASSWORD&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying to US region..."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy-to-us.sh --db-password $US_DATABASE_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In these examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aud&lt;/code&gt;: The audience, which must match the audience used when &lt;a href="//../cloud_services/aws/_index.md"&gt;creating the federated identity credentials&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secret_id&lt;/code&gt;: The name or ARN of the secret in AWS Secrets Manager. To retrieve a secret from another account, you must use an ARN.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;field&lt;/code&gt;: Is the specific key in the JSON secret to retrieve. If not specified, the entire secret is retrieved.
Field access is only supported for flat JSON secrets (top-level keys only) and supports string, number, and boolean values.
For example:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt;: Accesses the &lt;code&gt;password&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api_key&lt;/code&gt;: Accesses the &lt;code&gt;api_key&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;token&lt;/code&gt;: Specifies which ID token to use for authentication. If not specified, the runner looks for a token named &lt;code&gt;AWS_ID_TOKEN&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;version_id&lt;/code&gt;: Is the unique identifier of a specific version of the secret.
If you don't specify either &lt;code&gt;version_id&lt;/code&gt; or &lt;code&gt;version_stage&lt;/code&gt;, AWS Secrets Manager returns the &lt;code&gt;AWSCURRENT&lt;/code&gt; version.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;version_stage&lt;/code&gt;: The staging label of the version of the secret to retrieve (such as &lt;code&gt;AWSCURRENT&lt;/code&gt; or &lt;code&gt;AWSPENDING&lt;/code&gt;).
You cannot specify both &lt;code&gt;version_id&lt;/code&gt; and &lt;code&gt;version_stage&lt;/code&gt; for the same secret.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: Overrides the global &lt;code&gt;AWS_REGION&lt;/code&gt; for this specific secret.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;role_arn&lt;/code&gt;: Overrides the global &lt;code&gt;AWS_ROLE_ARN&lt;/code&gt; for this specific secret.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;role_session_name&lt;/code&gt;: Overrides the global &lt;code&gt;AWS_ROLE_SESSION_NAME&lt;/code&gt; for this specific secret.&lt;/li&gt;

&lt;li&gt;GitLab fetches the secret from AWS Secrets Manager and stores the value in a temporary file.
The path to this file is stored in a CI/CD variable, similar to
&lt;a href="//../variables/_index.md#use-file-type-cicd-variables"&gt;file type CI/CD variables&lt;/a&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Refer to &lt;a href="//../cloud_services/aws/_index.md#troubleshooting"&gt;OIDC for AWS troubleshooting&lt;/a&gt; for general&lt;br&gt;
problems when setting up OIDC with AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: &lt;code&gt;no EC2 IMDS role found&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The following error might happen if both of these conditions are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The CI/CD job is configured to use IAM role authentication.&lt;/li&gt;
&lt;li&gt;The job is executed by a runner with the &lt;a href="https://docs.gitlab.com/runner/executors/kubernetes/" rel="noopener noreferrer"&gt;Kubernetes executor&lt;/a&gt; hosted on AWS EKS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resolving secrets
Resolving secret "MY_AWS_SECRET"...
Using "aws_secrets_manager" secret resolver...
ERROR: Job failed (system failure): resolving secrets: operation error Secrets Manager: GetSecretValue, get identity: get credentials: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds: GetMetadata, canceled, context deadline exceeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Resolving secrets&lt;/code&gt; step is handled by the runner manager. This step accesses IAM credentials&lt;br&gt;
cached in &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html" rel="noopener noreferrer"&gt;EC2 IMDS&lt;/a&gt;.&lt;br&gt;
If the IAM role has not been applied to the runner manager, the &lt;code&gt;Resolving secrets&lt;/code&gt; step fails.&lt;/p&gt;

&lt;p&gt;To address this error, apply the correct IAM role to the runner manager.&lt;/p&gt;

&lt;p&gt;Applying the IAM role to the runner pods that are spawned and managed by the runner manager does not resolve this issue.&lt;/p&gt;

&lt;p&gt;Ref: &lt;a href="https://docs.gitlab.com/ci/secrets/aws_secrets_manager/" rel="noopener noreferrer"&gt;https://docs.gitlab.com/ci/secrets/aws_secrets_manager/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Git Multi-Remote Workflow:It Multiple Remote Management Guide</title>
      <dc:creator>Abhishek singh</dc:creator>
      <pubDate>Wed, 01 Apr 2026 12:41:13 +0000</pubDate>
      <link>https://www.debug.school/abhishek/it-multiple-remote-management-guide-2163</link>
      <guid>https://www.debug.school/abhishek/it-multiple-remote-management-guide-2163</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Git Multi-Remote Workflow: How to Manage and Push Code to Multiple Repositories (origin &amp;amp; old-origin) Like a Pro
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In real-world development—especially when you're working as a CTO or managing multiple environments—you often face a situation like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One repo for &lt;strong&gt;production&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;One repo for &lt;strong&gt;backup / legacy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;One repo for &lt;strong&gt;client delivery&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;One repo for &lt;strong&gt;internal development&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And suddenly, your simple Git workflow becomes complex.&lt;/p&gt;

&lt;p&gt;You ask:&lt;br&gt;
👉 &lt;em&gt;“How do I manage different codebases across multiple repos without breaking anything?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This guide solves that completely.&lt;/p&gt;

&lt;p&gt;By the end, you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand how Git multi-remote works&lt;/li&gt;
&lt;li&gt;Switch between repos safely&lt;/li&gt;
&lt;li&gt;Handle different code in &lt;code&gt;origin&lt;/code&gt; and &lt;code&gt;old-origin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use production-level strategies (branch isolation, syncing, migration)&lt;/li&gt;
&lt;li&gt;Avoid dangerous mistakes like overwriting code&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is Multi-Remote in Git&lt;/li&gt;
&lt;li&gt;Understanding origin vs old-origin&lt;/li&gt;
&lt;li&gt;Core Concept: Code vs Remote (Most Important)&lt;/li&gt;
&lt;li&gt;Real Workflow Scenarios&lt;/li&gt;
&lt;li&gt;Step-by-Step Implementation (Production Setup)&lt;/li&gt;
&lt;li&gt;Managing Different Codebases Safely&lt;/li&gt;
&lt;li&gt;Architecture-Level Strategy (CTO Thinking)&lt;/li&gt;
&lt;li&gt;Comparison: Single Repo vs Multi Repo&lt;/li&gt;
&lt;li&gt;Common Mistakes &amp;amp; Fixes&lt;/li&gt;
&lt;li&gt;Expert Pro Tips&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Meta Description&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  1. What is Multi-Remote in Git
&lt;/h2&gt;

&lt;p&gt;Git allows you to connect your local project to &lt;strong&gt;multiple remote repositories&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;origin      → New Repo
old-origin  → Old Repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;multi-remote setup&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Understanding origin vs old-origin
&lt;/h2&gt;

&lt;p&gt;In your case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;origin      &lt;span class="o"&gt;=&lt;/span&gt; New repository
old-origin  &lt;span class="o"&gt;=&lt;/span&gt; Old repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are just &lt;strong&gt;aliases&lt;/strong&gt;, not actual code.&lt;/p&gt;

&lt;p&gt;👉 Important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remote names do NOT store code. They only define destinations.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Core Concept: Code vs Remote (MOST IMPORTANT)
&lt;/h2&gt;

&lt;p&gt;This is where most developers get confused.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reality:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local branch&lt;/td&gt;
&lt;td&gt;Your actual code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote (origin)&lt;/td&gt;
&lt;td&gt;Where you push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote (old-origin)&lt;/td&gt;
&lt;td&gt;Another destination&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;👉 Key Rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Git always pushes &lt;strong&gt;your current local branch code&lt;/strong&gt;, not remote code.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Real Workflow Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: Push same code to both repos
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
git push old-origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Scenario 2: Different code in both repos
&lt;/h3&gt;

&lt;p&gt;This is your real case.&lt;/p&gt;

&lt;p&gt;You must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull code from respective repo&lt;/li&gt;
&lt;li&gt;Switch context locally&lt;/li&gt;
&lt;li&gt;Then push&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Step-by-Step Implementation (Production Setup)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Add both remotes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote rename origin old-origin
git remote add origin git@github.com:yourusername/new-repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: Fetch all data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch origin
git fetch old-origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3: Create separate local branches
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; new-main origin/main
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; old-main old-origin/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Now your structure:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Code Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;new-main&lt;/td&gt;
&lt;td&gt;New repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;old-main&lt;/td&gt;
&lt;td&gt;Old repo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Step 4: Work with branches
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Use new repo code:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout new-main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Use old repo code:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout old-main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 5: Push correctly
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Push new repo code:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout new-main
git push origin new-main:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Push old repo code:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout old-main
git push old-origin old-main:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Managing Different Codebases Safely
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Golden Rule:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;One branch = One source of truth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Never mix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;origin/main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;old-origin/main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new-main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;old-main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  If you want to copy code between repos
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Copy old repo → new repo
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout old-main
git push origin old-main:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Copy new repo → old repo
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout new-main
git push old-origin new-main:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Architecture-Level Strategy (CTO Thinking)
&lt;/h2&gt;

&lt;p&gt;In enterprise systems, this is how multi-repo is used:&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backup system&lt;/td&gt;
&lt;td&gt;old-origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration&lt;/td&gt;
&lt;td&gt;move code gradually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-client deployment&lt;/td&gt;
&lt;td&gt;separate repos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version isolation&lt;/td&gt;
&lt;td&gt;repo-based control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Architecture Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                LOCAL SYSTEM
              /              \
     new-main branch      old-main branch
          |                    |
       origin               old-origin
     (new repo)           (old repo)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;th&gt;Best Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single Repo&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;No flexibility&lt;/td&gt;
&lt;td&gt;Small projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi Remote&lt;/td&gt;
&lt;td&gt;Flexible&lt;/td&gt;
&lt;td&gt;Needs discipline&lt;/td&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi Branch&lt;/td&gt;
&lt;td&gt;Controlled&lt;/td&gt;
&lt;td&gt;Slight complexity&lt;/td&gt;
&lt;td&gt;Dev teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi Repo&lt;/td&gt;
&lt;td&gt;Full isolation&lt;/td&gt;
&lt;td&gt;Hard to sync&lt;/td&gt;
&lt;td&gt;Large org&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  9. Common Mistakes (Real-World)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Mistake 1: Pushing wrong code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without checking branch&lt;/p&gt;

&lt;p&gt;✅ Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ❌ Mistake 2: Mixing repos
&lt;/h3&gt;

&lt;p&gt;Using one branch for both repos&lt;/p&gt;

&lt;p&gt;✅ Fix:&lt;br&gt;
Use separate branches&lt;/p&gt;


&lt;h3&gt;
  
  
  ❌ Mistake 3: Force pushing blindly
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ Fix:&lt;br&gt;
Use only when required&lt;/p&gt;


&lt;h3&gt;
  
  
  ❌ Mistake 4: Not fetching remotes
&lt;/h3&gt;

&lt;p&gt;Leads to outdated code&lt;/p&gt;

&lt;p&gt;✅ Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch origin
git fetch old-origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Expert Pro Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔥 Pro Tip 1: Always verify before push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
git branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔥 Pro Tip 2: Use aliases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gpo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git push origin main"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gpol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git push old-origin main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔥 Pro Tip 3: Protect main branch
&lt;/h3&gt;

&lt;p&gt;Use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protected branches in GitHub&lt;/li&gt;
&lt;li&gt;PR-based deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🔥 Pro Tip 4: Use staging branches
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;staging-main
production-main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔥 Pro Tip 5: Use tagging for releases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v1.0
git push origin v1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  11. FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Can I push different code to two repos?
&lt;/h3&gt;

&lt;p&gt;Yes. Use separate local branches.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Can I sync both repos automatically?
&lt;/h3&gt;

&lt;p&gt;Yes, but better to control manually for safety.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. What happens if both repos have different history?
&lt;/h3&gt;

&lt;p&gt;You must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;merge OR&lt;/li&gt;
&lt;li&gt;force push carefully&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Is multi-remote safe?
&lt;/h3&gt;

&lt;p&gt;Yes, if you follow branch discipline.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Should I use this in production?
&lt;/h3&gt;

&lt;p&gt;Yes, especially for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migration&lt;/li&gt;
&lt;li&gt;backup&lt;/li&gt;
&lt;li&gt;multi-client systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  12. Conclusion
&lt;/h2&gt;

&lt;p&gt;Managing multiple Git repositories is not complex—if you follow the right architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Remote = destination&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Branch = code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Always separate codebases using branches&lt;/li&gt;
&lt;li&gt;Never mix repositories blindly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Your Action Plan:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Setup &lt;code&gt;origin&lt;/code&gt; and &lt;code&gt;old-origin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;new-main&lt;/code&gt; and &lt;code&gt;old-main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Always switch branch before push&lt;/li&gt;
&lt;li&gt;Push carefully based on destination&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Fix invalid checkpoint record in Postgres</title>
      <dc:creator>Suyash Sambhare</dc:creator>
      <pubDate>Tue, 31 Mar 2026 22:10:21 +0000</pubDate>
      <link>https://www.debug.school/suyash/fix-invalid-checkpoint-record-in-postgres-2jd0</link>
      <guid>https://www.debug.school/suyash/fix-invalid-checkpoint-record-in-postgres-2jd0</guid>
      <description>&lt;p&gt;When PostgreSQL is unable to detect a valid checkpoint from which to begin the recovery procedure, it displays the error &lt;strong&gt;"PANIC: could not locate a valid checkpoint record."&lt;/strong&gt; This may occur if the PostgreSQL container is not restarted safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevent data corruption
&lt;/h2&gt;

&lt;p&gt;For stateful applications like databases, the first method is to use &lt;em&gt;Stateful Sets&lt;/em&gt; rather than &lt;em&gt;Deployments&lt;/em&gt;; this is a Kubernetes best practice. To preserve the stability and integrity of your database, Stateful Sets offer assurances regarding the ordering and uniqueness of pods. Additionally, stateful sets will manage restarts more effectively.&lt;/p&gt;

&lt;p&gt;The second method is to safely shut down your PostgreSQL instance by using a &lt;code&gt;preStop&lt;/code&gt; hook in Kubernetes.&lt;br&gt;
Before a pod is terminated in Kubernetes, the &lt;code&gt;preStop&lt;/code&gt; hook is invoked. Once the &lt;code&gt;preStop&lt;/code&gt; hook is finished, the pod's termination process starts.&lt;/p&gt;

&lt;p&gt;Apply the preStop hook in a Kubernetes configuration for PostgreSQL:&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;postgres&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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;postgres&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
        &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&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;postgres-data&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data&lt;/span&gt;
        &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;preStop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/bin/sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_ctl&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-D&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/var/lib/postgres/data&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-w&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-t&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;60&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;fast&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stop"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;volumeClaimTemplates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;metadata&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;postgres-data&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ReadWriteOnce"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1Gi&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;preStop&lt;/code&gt; hook runs the &lt;code&gt;pg_ctl&lt;/code&gt; stop command to shut down the PostgreSQL server. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-D /var/lib/postgres/data&lt;/code&gt; option specifies the directory where the data base files live, &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-w&lt;/code&gt; waits until the server shuts down, &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-t 60&lt;/code&gt; specifies the wait timeout in seconds, and &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-m fast&lt;/code&gt; means to do a "fast" shutdown, which rolls back all active transactions, disconnects clients immediately and shuts down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For AWS ECS deployments you can set the &lt;strong&gt;Min and max running tasks&lt;/strong&gt; respectively: &lt;code&gt;0% min and 100% max&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/3sOLGvIVNYsWsOjGFtrydWLkWXh8LGBFD1cJIiIGLew/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvZW0wOTMx/cnR3NGlrNTFocXBs/MTkucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/3sOLGvIVNYsWsOjGFtrydWLkWXh8LGBFD1cJIiIGLew/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvZW0wOTMx/cnR3NGlrNTFocXBs/MTkucG5n" alt="Postgres" width="128" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recover from an error state
&lt;/h2&gt;

&lt;p&gt;This can be fixed by running &lt;code&gt;pg_resetwal&lt;/code&gt; command by connecting to the container. &lt;br&gt;
However, since the pod is in &lt;code&gt;crashLoopBackOff&lt;/code&gt; state, we will not be able to connect to the container.&lt;br&gt;
We must first make the pod to be in stable state to execute the command.&lt;/p&gt;

&lt;p&gt;Please follow the steps below for manual execution of the command &lt;code&gt;pg_resetwal&lt;/code&gt; in a container. &lt;br&gt;
This process requires the pod to be in a stable state, and not in a crash loop. &lt;br&gt;
You can achieve this by introducing a delay in the &lt;code&gt;postgres-deployment.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scale down the deployment by reducing the replica count of the deployment to zero using the following command: &lt;code&gt;kubectl scale deployment postgres - replicas=0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Introduce a delay: Modify the &lt;code&gt;postgres-deployment.yaml&lt;/code&gt; file to include a sleep command that delays the initialization process by 600 seconds, keeping the pod in the initializing state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is an example of how to add it:&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="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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;postgres&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/bin/bash"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; 
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;600;"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Scale up the deployment: Bring the deployment back up by increasing the replica count to one using the following command: &lt;code&gt;kubectl scale deployment postgres - replicas=1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;pg_resetwal&lt;/code&gt; in the pod: Once the pod reaches the initializing state, run the &lt;code&gt;pg_resetwal&lt;/code&gt; command by executing into the pod. Here is how you can do this:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; postgres - /bin/bash
su postgres
pg_resetwal /var/lib/postgres/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Proceed despite the warning: If you do receive the warning, continue the process by forcing the &lt;code&gt;pg_resetwal&lt;/code&gt; command as follows: &lt;code&gt;pg_resetwal /var/lib/posgtgres/data -f&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After running this command, you should receive a confirmation message stating “Write-ahead log reset.”&lt;br&gt;
After you have successfully executed the pg_resetwal command, you should be able to restart the PostgreSQL server. &lt;br&gt;
Remember that the &lt;code&gt;pg_resetwal&lt;/code&gt; command is a measure of last resort and carries the risk of data loss or inconsistency.&lt;br&gt;
Always make sure to maintain regular backups of your PostgreSQL databases and consider setting up high availability and replication solutions for your production databases.&lt;/p&gt;

&lt;p&gt;Ref: &lt;a href="https://medium.com/@adnanitdev/fix-for-error-panic-could-not-locate-a-valid-checkpoint-record-in-postgres-or-citus-running-in-b03d8341a258" rel="noopener noreferrer"&gt;https://medium.com/@adnanitdev/fix-for-error-panic-could-not-locate-a-valid-checkpoint-record-in-postgres-or-citus-running-in-b03d8341a258&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>postgres</category>
      <category>data</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Different kind of prompt used by Spring AI</title>
      <dc:creator>rakesh kumar</dc:creator>
      <pubDate>Fri, 27 Mar 2026 06:17:45 +0000</pubDate>
      <link>https://www.debug.school/rakeshdevcotocus_468/different-kind-of-prompt-used-by-spring-ai-1o3j</link>
      <guid>https://www.debug.school/rakeshdevcotocus_468/different-kind-of-prompt-used-by-spring-ai-1o3j</guid>
      <description>&lt;p&gt;Main kinds of prompts Spring AI helps you make&lt;br&gt;
A. &lt;strong&gt;System prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This tells the model how to behave.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“You are a helpful banking assistant.”
“Answer in simple English.”
“Do not return unsafe medical advice.”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Role: controls tone, style, rules, and boundaries. Spring AI docs say system messages are generated by the system to guide the conversation.&lt;/p&gt;

&lt;p&gt;B. &lt;strong&gt;User prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the actual question or request from the user.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;“What is compound interest?”
“Summarize this document.”
“Translate this to Hindi.”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Role: carries the direct input to the model. Spring AI identifies user messages as the direct inputs from the user.&lt;/p&gt;

&lt;p&gt;C. &lt;strong&gt;Multi-message chat prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This combines multiple messages together, usually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;one or more system messages
one user message
sometimes previous conversation messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Role: gives the model better context than a single plain text prompt. Spring AI’s prompt model is message-based, not just one raw string.&lt;/p&gt;

&lt;p&gt;D. &lt;strong&gt;Template prompt / parameterized prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a prompt with placeholders like {name}, {topic}, {question}.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;“Explain {topic} to a beginner.”&lt;br&gt;
“Write an email to {customerName} about {issue}.”&lt;/p&gt;

&lt;p&gt;Role: makes prompts reusable and dynamic. The docs note that prompts often contain placeholders substituted at runtime. The prompt docs also compare this to a view template or SQL with placeholders.&lt;/p&gt;

&lt;p&gt;E. &lt;strong&gt;External file prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can keep prompts outside Java code in template files instead of hardcoding them. That makes them reusable and easier to version and maintain. Your screenshot mentions .st, .mustache, and .ftl, and Spring AI’s docs show PromptTemplate rendering support, including the default StPromptTemplate based on StringTemplate.&lt;/p&gt;

&lt;p&gt;F. &lt;strong&gt;RAG prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is used when you want the model to answer using retrieved context from documents or a vector database. Spring AI’s RAG support lets you customize a PromptTemplate that merges the user query with retrieved context, and the docs specify placeholders such as query and question_answer_context.&lt;/p&gt;

&lt;p&gt;G. &lt;strong&gt;Memory-aware prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;LLMs are stateless by default, so Spring AI adds chat memory features to carry useful prior context into later interactions. This helps create prompts that include conversation context without you manually rebuilding it each time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Why these prompt types matter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Different prompt types solve different problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System prompt → behavior and rules
User prompt → actual request
Template prompt → reuse and dynamic input
RAG prompt → answer from documents/context
Memory-aware prompt → continue a conversation naturally
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is why Spring AI is useful: it gives structure around prompt creation instead of making you manually assemble raw HTTP JSON every time. ChatClient builds prompt parts fluently, and Advisors can add memory, retrieved documents, and more advanced behavior.&lt;/p&gt;

&lt;p&gt;3) Coding examples&lt;/p&gt;

&lt;p&gt;These examples are written in the normal Spring AI style and may need small adjustment depending on your exact Spring AI version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Simple system prompt + user prompt&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;@RestController
@RequestMapping("/ai")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/explain")
    public String explain(@RequestParam String topic) {
        return chatClient.prompt()
                .system("You are a Java teacher. Explain in simple English.")
                .user("Explain this topic for a beginner: {topic}")
                .param("topic", topic)
                .call()
                .content();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.system(...) creates a system prompt
.user(...) creates a user prompt
.param(...) fills the template placeholder dynamically
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matches the docs’ message-based prompt model and runtime placeholder substitution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2: Reusable template prompt&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;@Service
public class EmailPromptService {

    private final ChatClient chatClient;

    public EmailPromptService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String generateEmail(String customerName, String issue) {
        return chatClient.prompt()
                .system("You are a professional customer support writer.")
                .user("""
                      Write a polite support email to {customerName}.
                      The issue is: {issue}
                      Keep the tone professional and short.
                      """)
                .param("customerName", customerName)
                .param("issue", issue)
                .call()
                .content();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this is useful&lt;/p&gt;

&lt;p&gt;Same prompt structure, different values. That is the main idea of template prompts. Spring AI docs explicitly describe placeholders being replaced based on user requests or application code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 3: Prompt from external template file idea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose you keep a prompt file like:&lt;/p&gt;

&lt;p&gt;src/main/resources/prompts/greeting.st&lt;/p&gt;

&lt;p&gt;Hello, my name is {name}. Can you greet me back nicely?&lt;/p&gt;

&lt;p&gt;Then your code can render and send it through Spring AI. A simplified example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class GreetingService {

    private final ChatClient chatClient;

    public GreetingService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String greet(String name) {
        String template = "Hello, my name is {name}. Can you greet me back nicely?";

        return chatClient.prompt()
                .user(template)
                .param("name", name)
                .call()
                .content();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI supports prompt templating and documents PromptTemplate; for RAG templates it uses StPromptTemplate by default, based on StringTemplate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 4: RAG-style prompt&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;String answer = chatClient.prompt()
        .system("Answer only from the provided context. If unsure, say you do not know.")
        .user("""
              Question: {question}

              Context:
              {context}
              """)
        .param("question", "What is the refund policy?")
        .param("context", """
                Refunds are allowed within 7 days of purchase
                if the product has not been activated.
                """)
        .call()
        .content();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the basic idea behind a RAG prompt: merge the user query with retrieved context. Spring AI’s RAG docs describe custom templates that combine query and retrieved context for answering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 5: Memory-aware chat idea&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;String response = chatClient.prompt()
        .system("You are a helpful assistant that remembers prior discussion context.")
        .user("Continue our last discussion and summarize the final decision.")
        .call()
        .content();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In real apps, chat memory is usually backed by Spring AI chat memory support, because LLMs are stateless by default and Spring AI adds memory abstractions to maintain useful context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/ICe4A002KxlSMhQ7niFzTjYFqMi_0lTB3IjFZt6SMdk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvajFzeGUy/NmNqeG93NTkwNHl0/a2wucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/ICe4A002KxlSMhQ7niFzTjYFqMi_0lTB3IjFZt6SMdk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvajFzeGUy/NmNqeG93NTkwNHl0/a2wucG5n" alt=" " width="632" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Very simple summary
&lt;/h2&gt;

&lt;p&gt;Spring AI mainly helps you create these prompt types:&lt;/p&gt;

&lt;p&gt;System prompts&lt;br&gt;
User prompts&lt;br&gt;
Multi-message prompts&lt;br&gt;
Template prompts&lt;br&gt;
External-file prompts&lt;br&gt;
RAG prompts&lt;br&gt;
Memory-aware prompts&lt;/p&gt;

&lt;p&gt;So the big benefit is not just “send text to GPT.”&lt;br&gt;
The real benefit is: Spring AI gives prompt structure, reuse, context, and maintainability inside a Spring Boot application&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Spring AI Simplifies REST API Integration in Modern Applications</title>
      <dc:creator>rakesh kumar</dc:creator>
      <pubDate>Fri, 27 Mar 2026 05:30:34 +0000</pubDate>
      <link>https://www.debug.school/rakeshdevcotocus_468/how-spring-ai-simplifies-rest-api-integration-in-modern-applications-2l5c</link>
      <guid>https://www.debug.school/rakeshdevcotocus_468/how-spring-ai-simplifies-rest-api-integration-in-modern-applications-2l5c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Theory explanation&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Why not call REST API directly from controller?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Main purpose of Spring AI interface&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Why this helps in modern applications&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Simple flow&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Coding example&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Theory explanation
&lt;/h2&gt;

&lt;p&gt;In a normal Spring Boot project, you can call an AI provider directly with REST APIs.&lt;br&gt;
That means your code builds HTTP requests, adds headers, sends JSON, handles authentication, parses responses, and manages retries by itself.&lt;/p&gt;

&lt;p&gt;That works for small demos, but in modern applications it becomes repetitive and hard to maintain.&lt;/p&gt;

&lt;p&gt;Spring AI simplifies this by putting a Spring-style abstraction layer between your application and the AI provider. Instead of writing low-level HTTP code everywhere, you work with higher-level APIs such as ChatClient, ChatModel, Prompt, and advisors. The Spring AI reference describes ChatClient as a fluent API for communicating with an AI model, where prompts are built from messages like user and system messages.&lt;/p&gt;

&lt;p&gt;So the difference is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REST API only = low-level transport and manual integration
Spring AI = structured, reusable, Spring-friendly AI integration built on top of provider APIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why not call REST API directly from controller?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Because controller should mainly handle:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;incoming request
validation
response

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;If controller also manages:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI API payloads
prompt structure
JSON parsing
retries
embeddings
vector DB lookup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.debug.school/images/9hbPDkP6r2Gl5J8i7l9u6_i1Dxb-LPpYTDmd--0F2uI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvY3h3d2t2/YmV1cDZrMXUxcHg1/NGIucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/9hbPDkP6r2Gl5J8i7l9u6_i1Dxb-LPpYTDmd--0F2uI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvY3h3d2t2/YmV1cDZrMXUxcHg1/NGIucG5n" alt=" " width="453" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;then controller becomes overloaded.&lt;/p&gt;

&lt;p&gt;Spring AI keeps the design cleaner.&lt;br&gt;
&lt;a href="https://www.debug.school/images/ojHOMRmwwKQKQczz2ElO1MPlZiLxFg9nYrHaURwUetg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbDVjM2ht/bzV5eWEyNGRlaHln/dmIucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/ojHOMRmwwKQKQczz2ElO1MPlZiLxFg9nYrHaURwUetg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbDVjM2ht/bzV5eWEyNGRlaHln/dmIucG5n" alt=" " width="715" height="277"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Main purpose of Spring AI interface
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It hides low-level HTTP complexity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You do not need to manually build every REST request.&lt;/p&gt;

&lt;p&gt;Instead of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create URL
create headers
add API key
build JSON
send request
parse result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use a cleaner Spring-style approach.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It gives a Spring-friendly programming model&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Spring Boot, developers like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependency injection
beans
service classes
reusable configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI fits that style.&lt;/p&gt;

&lt;p&gt;So AI feels like a normal Spring service, not like raw external API handling.&lt;/p&gt;

&lt;p&gt;Example idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatClient
EmbeddingModel
PromptTemplate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are easier to use in service classes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It reduces vendor lock-in&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Different providers have different request and response formats.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Without Spring AI:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenAI code looks one way
another provider looks different
switching providers means code change in many places
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;With Spring AI&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;you work through a common abstraction
changing provider becomes easier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It supports AI-specific features, not just REST calling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI integration is not just sending text over HTTP.&lt;/p&gt;

&lt;p&gt;It often includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt templating
chat memory
embeddings
vector search
RAG flow
structured output
model options
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;REST API does not give these patterns automatically.&lt;/p&gt;

&lt;p&gt;Spring AI provides structure for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It makes enterprise code cleaner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In small demo projects, direct REST calls are okay.&lt;/p&gt;

&lt;p&gt;But in real applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chatbot
recommendation engine
support assistant
document Q&amp;amp;A
healthcare AI assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you need better architecture.&lt;/p&gt;

&lt;p&gt;Spring AI helps separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;controller
business logic
AI interaction
prompt layer
retrieval layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the project easier to scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this helps in modern applications
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cleaner code&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without Spring AI, you often repeat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API URL handling
auth token or API key setup
request body creation
response parsing
provider-specific JSON mapping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI centralizes that interaction. Its model API is designed as a portable interface across providers, which makes the code cleaner and more maintainable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Easier provider switching&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you call one provider directly using raw REST, your code usually becomes tightly coupled to that provider’s request and response format.&lt;/p&gt;

&lt;p&gt;Spring AI’s Chat Model API is designed to be portable, so moving across supported providers requires fewer code changes than rewriting raw REST integration everywhere.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prompt handling becomes structured&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Modern AI apps need more than one plain input string. They need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;system instructions
user prompts
templates
placeholders
context injection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI supports prompts as structured message collections and supports prompt templating, which is much better than building JSON strings manually for each REST call.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Better support for advanced AI patterns&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Modern applications often need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chat memory
embeddings
RAG
tool calling
streaming responses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI supports these patterns directly through its APIs and advisors, which is far beyond “just send a REST request.”&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Better fit with Spring Boot architecture&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Spring developers prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependency injection
beans
service classes
configuration properties
starter-based setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI fits naturally into that model. For example, the official docs provide Spring Boot starter-based configuration such as spring-ai-starter-model-openai, and the getting-started docs state support for Spring Boot 3.4.x and 3.5.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple flow
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Direct REST approach&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller → Service → Manual HTTP Client → AI Provider API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Spring AI approach&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller → Service → ChatClient / ChatModel → AI Provider API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI still uses provider APIs underneath, but your application code stays much simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding example
&lt;/h2&gt;

&lt;p&gt;Below is a simple Spring Boot example using Spring AI with OpenAI-style integration.&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Maven dependencies&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;&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.ai&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-ai-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;YOUR_VERSION&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;

&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.ai&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-ai-starter-model-openai&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official Spring AI OpenAI chat docs show spring-ai-starter-model-openai as the starter artifact.&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;application.properties&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;spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.options.model=gpt-4o-mini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring AI provides Spring Boot auto-configuration for the OpenAI chat client through properties under the Spring AI namespace.&lt;/p&gt;

&lt;p&gt;3) &lt;strong&gt;Service class&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;package com.example.demo.service;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;

@Service
public class AiService {

    private final ChatClient chatClient;

    public AiService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String explainTopic(String topic) {
        return chatClient.prompt()
                .system("You are a helpful Java and Spring expert. Explain in simple English.")
                .user("Explain this topic in a modern application context: {topic}")
                .param("topic", topic)
                .call()
                .content();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses ChatClient’s fluent API to build a prompt from system and user messages, with runtime parameters. That is exactly the sort of prompt-building model described in the official docs.&lt;/p&gt;

&lt;p&gt;4) &lt;strong&gt;REST controller&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;package com.example.demo.controller;

import com.example.demo.service.AiService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AiController {

    private final AiService aiService;

    public AiController(AiService aiService) {
        this.aiService = aiService;
    }

    @GetMapping("/api/explain")
    public String explain(@RequestParam String topic) {
        return aiService.explainTopic(topic);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your application exposes a normal REST endpoint, but internally it uses Spring AI rather than a manually coded HTTP call to the model provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/9dZjWh6b4U7oicTYDGqu_GFiJ_q7TSaptCcfJoLOKpc/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvamhsMHBr/MTk1OXNpcWwxMDZ6/eTQucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/9dZjWh6b4U7oicTYDGqu_GFiJ_q7TSaptCcfJoLOKpc/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvamhsMHBr/MTk1OXNpcWwxMDZ6/eTQucG5n" alt=" " width="342" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to integrate AI models into Spring Boot applications using Spring AI</title>
      <dc:creator>rakesh kumar</dc:creator>
      <pubDate>Fri, 27 Mar 2026 03:39:18 +0000</pubDate>
      <link>https://www.debug.school/rakeshdevcotocus_468/how-to-integrate-ai-models-into-spring-boot-applications-using-spring-ai-amp</link>
      <guid>https://www.debug.school/rakeshdevcotocus_468/how-to-integrate-ai-models-into-spring-boot-applications-using-spring-ai-amp</guid>
      <description>&lt;p&gt;&lt;strong&gt;What is Spring AI?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Spring AI Architecture&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Purpose of Spring AI&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Role of Spring AI&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;How Flow Works&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Common Questions (with Answers)&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Spring AI?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/_-xR9Nw_BnWWUecHPUpfYtdUlRLgeTEu2NlClfK0dzs/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvNGl6Y25i/aWVmbDJwcjl4bWFt/N3EucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/_-xR9Nw_BnWWUecHPUpfYtdUlRLgeTEu2NlClfK0dzs/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvNGl6Y25i/aWVmbDJwcjl4bWFt/N3EucG5n" alt=" " width="688" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Spring AI is a framework that helps developers integrate AI models (like GPT, LLMs, embeddings, etc.) into Spring Boot applications easily.&lt;/p&gt;

&lt;p&gt;Instead of manually calling AI APIs and handling complexity, Spring AI provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clean Java-based abstraction
Easy integration with AI providers (OpenAI, Azure, etc.)
Support for chat, embeddings, vector DB, prompt templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 In simple words:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Spring AI = Bridge between Spring Boot apps and AI models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎯 &lt;/p&gt;

&lt;h2&gt;
  
  
  Spring AI Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/Xs3VJf5GJv7PQ0BX1A9MHBi3eOcTOLWs-NC4iw70vU8/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvYjUxMGtw/cXpvNWcwNHl5emE0/aDEucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/Xs3VJf5GJv7PQ0BX1A9MHBi3eOcTOLWs-NC4iw70vU8/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvYjUxMGtw/cXpvNWcwNHl5emE0/aDEucG5n" alt=" " width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Spring AI Works&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users give input to Spring Boot REST controller.&lt;/li&gt;
&lt;li&gt;Spring AI  processes the user input using Prompt Templates or ChatClient (to call LLM)&lt;/li&gt;
&lt;li&gt;ChatClient connects to external AI providers (OpenAI, Ollama, etc.)&lt;/li&gt;
&lt;li&gt;And response is returned to the Spring Boot app and sent back to the user
. 
&lt;strong&gt;Multi-Provider Support&lt;/strong&gt;: Spring AI framework supports connecting to multiple LLM providers like
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenAI (ChatGPT)
Azure OpenAI
Hugging Face (Transformers)
Ollama (for local models)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chat Client API&lt;/strong&gt;: Standardizes communication with LLMs using a fluent API, regardless of provider differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt Templates&lt;/strong&gt;: Developers can define dynamic prompts with variables using Spring Expression Language (SpEL).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embedding and Vector Store Integration&lt;/strong&gt;: Spring AI supports converting text into embeddings like numeric vector and storing them in vector databases like PostgreSQL with pgvector.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured Outputs ( Like POJO Mapping)&lt;/strong&gt;: Spring AI model output (like JSON) directly to the Java POJOs class using annotations and converters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features of Spring AI&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose of Spring AI
&lt;/h2&gt;

&lt;p&gt;The main purpose is to simplify AI integration in enterprise Java applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Goals:&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;Reduce boilerplate code for AI API calls
Provide standardized APIs (like Spring Data, Spring MVC)
Enable production-ready AI apps
Support scalable and maintainable architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧩 &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/FY8Np1unpTsJoihNjo8zyy6EaqZaRgkMqIHWU7sZSYI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvOWJrc3U4/bG5mZ3FrbzEwOWN0/MTMucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/FY8Np1unpTsJoihNjo8zyy6EaqZaRgkMqIHWU7sZSYI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvOWJrc3U4/bG5mZ3FrbzEwOWN0/MTMucG5n" alt=" " width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Role of Spring AI
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AI Integration Layer&lt;/strong&gt;
Connects your Spring Boot app with AI models
Handles API calls, authentication, retries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction Provider&lt;/strong&gt;
You don’t write raw HTTP calls
Use simple Java interfaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Management&lt;/strong&gt;
Helps structure prompts cleanly
Supports templates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Handling (Vector + Embeddings)&lt;/strong&gt;
Store &amp;amp; search semantic data
Used in RAG (Retrieval-Augmented Generation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Enablement&lt;/strong&gt;
Works with microservices
Secure, scalable, production-ready
🏗️ Architecture (Colorful + Labeled Representation)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://www.debug.school/images/zNBH2tvUSYxle2_iBZkJi20nljpVu8jSIOd2_LMFm1c/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbXF5N2Vq/amFoc3UwdDJiZnFz/MjEucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/zNBH2tvUSYxle2_iBZkJi20nljpVu8jSIOd2_LMFm1c/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvbXF5N2Vq/amFoc3UwdDJiZnFz/MjEucG5n" alt=" " width="387" height="693"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Flow Works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User sends request → Frontend
Request hits → Spring Boot Controller
Service calls → Spring AI layer
Spring AI sends request → AI Model (GPT)
AI response → back to Spring Boot → UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❓ &lt;/p&gt;

&lt;h2&gt;
  
  
  Common Questions (with Answers)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Why use Spring AI instead of direct API calls?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 Because it reduces complexity, gives structure, and is production-ready.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Can Spring AI work with Laravel backend?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 No directly. Spring AI is for Java ecosystem.&lt;br&gt;
For Laravel, you use OpenAI SDK or HTTP APIs manually.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is Spring AI suitable for microservices?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 Yes, very suitable. You can create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI microservice
Chat service
Recommendation engine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What features does Spring AI provide?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chat completion
Embeddings
Prompt templates
Vector DB integration
RAG support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;When should you choose Spring AI?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 Choose it when:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your backend is Spring Boot
You need enterprise AI integration
You want clean, scalable architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;When NOT to use Spring AI?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👉 Avoid if:&lt;/p&gt;

&lt;p&gt;Your backend is Laravel/PHP&lt;br&gt;
Small project (simple API call enough)&lt;br&gt;
🔥 &lt;strong&gt;Final Simple Summary&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;Purpose: Simplify AI integration
Role: Bridge Spring Boot ↔ AI Models
Benefit: Clean, scalable, enterprise-ready AI apps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://medium.com/@0_bibhuti/spring-ai-build-intelligent-applications-with-ease-c3c28c7bb15d" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=BFk_nAJapYc&amp;amp;list=PL0zysOflRCen1TeDUm-ebl9T-WbJygCGE&amp;amp;index=5" rel="noopener noreferrer"&gt;utube&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>WhatsApp vs SMS Pricing in India: Complete Guide for OTP, Marketing &amp; Notifications (2026)</title>
      <dc:creator>Abhishek singh</dc:creator>
      <pubDate>Tue, 24 Mar 2026 05:31:23 +0000</pubDate>
      <link>https://www.debug.school/abhishek/whatsapp-vs-sms-pricing-in-india-complete-guide-for-otp-marketing-notifications-2026-53l7</link>
      <guid>https://www.debug.school/abhishek/whatsapp-vs-sms-pricing-in-india-complete-guide-for-otp-marketing-notifications-2026-53l7</guid>
      <description>&lt;p&gt;&lt;a href="https://www.debug.school/images/OCVvg7DvZlNa4hMSm0yg2ep2LNLZkrUkLqz8mrwxeR4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvMXRlYTEw/dDFtdGo0NnA0M2Vr/dHcucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://www.debug.school/images/OCVvg7DvZlNa4hMSm0yg2ep2LNLZkrUkLqz8mrwxeR4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly93d3cu/ZGVidWcuc2Nob29s/L3VwbG9hZHMvYXJ0/aWNsZXMvMXRlYTEw/dDFtdGo0NnA0M2Vr/dHcucG5n" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;WhatsApp vs SMS Pricing in India: Complete Guide for OTP, Marketing &amp;amp; Notifications (2026)&lt;/strong&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're building a product, SaaS platform, or customer communication system in India, one question always comes up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I use WhatsApp or SMS for sending OTPs, marketing messages, or notifications?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance, both seem similar — they deliver messages to users. But when you go deeper, you’ll realize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pricing models are completely different&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use cases vary significantly&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost impact can be huge at scale&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide will give you a &lt;strong&gt;complete, practical, and real-world understanding&lt;/strong&gt; of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WhatsApp pricing (Meta direct)&lt;/li&gt;
&lt;li&gt;SMS pricing (Twilio India)&lt;/li&gt;
&lt;li&gt;Cost comparison for OTP, marketing, and notifications&lt;/li&gt;
&lt;li&gt;Best strategy for your business&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you won’t need any other resource.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Table of Contents&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is WhatsApp Business API?&lt;/li&gt;
&lt;li&gt;What is SMS (Twilio)?&lt;/li&gt;
&lt;li&gt;WhatsApp Pricing Model (India)&lt;/li&gt;
&lt;li&gt;SMS Pricing Model (India)&lt;/li&gt;
&lt;li&gt;WhatsApp vs SMS Cost Comparison Table&lt;/li&gt;
&lt;li&gt;OTP Pricing Comparison&lt;/li&gt;
&lt;li&gt;Marketing Pricing Comparison&lt;/li&gt;
&lt;li&gt;Notification Pricing Comparison&lt;/li&gt;
&lt;li&gt;Real-Life Use Cases&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Common Mistakes to Avoid&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;1. What is WhatsApp Business API?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;WhatsApp Business API (Meta) allows businesses to send:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OTPs (Authentication)&lt;/li&gt;
&lt;li&gt;Notifications (Utility)&lt;/li&gt;
&lt;li&gt;Marketing campaigns&lt;/li&gt;
&lt;li&gt;Customer support messages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rich media (images, buttons, links)&lt;/li&gt;
&lt;li&gt;Two-way communication&lt;/li&gt;
&lt;li&gt;High engagement rates&lt;/li&gt;
&lt;li&gt;Template-based messaging&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;2. What is SMS (Twilio)?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SMS is the traditional way to send messages using telecom networks.&lt;/p&gt;

&lt;p&gt;With Twilio, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send OTPs&lt;/li&gt;
&lt;li&gt;Send alerts&lt;/li&gt;
&lt;li&gt;Run marketing campaigns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Works on &lt;strong&gt;all phones&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No internet required&lt;/li&gt;
&lt;li&gt;Simple implementation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;3. WhatsApp Pricing Model (India)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;WhatsApp pricing is &lt;strong&gt;not fixed per message&lt;/strong&gt;. It depends on:&lt;/p&gt;

&lt;h3&gt;
  
  
  Message Categories:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication (OTP)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Utility (notifications)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marketing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service (support replies)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Important Rules:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Service messages = FREE (within 24 hours)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User replies open a 24-hour window&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Charges apply only for template messages outside window&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Approx Pricing (India):
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Approx Cost (INR)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authentication (OTP)&lt;/td&gt;
&lt;td&gt;₹0.30 – ₹0.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utility (Notification)&lt;/td&gt;
&lt;td&gt;₹0.20 – ₹0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketing&lt;/td&gt;
&lt;td&gt;₹0.80 – ₹2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service (Reply)&lt;/td&gt;
&lt;td&gt;FREE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;4. SMS Pricing Model (India)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SMS pricing is &lt;strong&gt;simple but expensive&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Twilio India Pricing:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$0.0832 ≈ ₹7.8 per SMS segment&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Important Points:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Charged &lt;strong&gt;per segment (160 characters)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Long messages = &lt;strong&gt;multiple SMS charges&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No free window&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;5. WhatsApp vs SMS Cost Comparison Table&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;WhatsApp Cost (INR)&lt;/th&gt;
&lt;th&gt;SMS Cost (INR)&lt;/th&gt;
&lt;th&gt;Winner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OTP&lt;/td&gt;
&lt;td&gt;₹0.30 – ₹0.80&lt;/td&gt;
&lt;td&gt;₹7.80&lt;/td&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketing&lt;/td&gt;
&lt;td&gt;₹0.80 – ₹2.50&lt;/td&gt;
&lt;td&gt;₹7.80+&lt;/td&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notification&lt;/td&gt;
&lt;td&gt;₹0.20 – ₹0.60&lt;/td&gt;
&lt;td&gt;₹7.80&lt;/td&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support Reply&lt;/td&gt;
&lt;td&gt;FREE&lt;/td&gt;
&lt;td&gt;₹7.80&lt;/td&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;6. OTP Pricing Comparison&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WhatsApp OTP:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Category: Authentication&lt;/li&gt;
&lt;li&gt;Cost: ~₹0.30 – ₹0.80&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SMS OTP:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cost: ~₹7.80 per SMS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;If you send &lt;strong&gt;1000 OTPs&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Channel&lt;/th&gt;
&lt;th&gt;Total Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;td&gt;₹300 – ₹800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMS&lt;/td&gt;
&lt;td&gt;₹7800&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Insight:
&lt;/h3&gt;

&lt;p&gt;👉 WhatsApp is &lt;strong&gt;10x cheaper&lt;/strong&gt; for OTP&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;7. Marketing Pricing Comparison&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WhatsApp Marketing:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cost: ₹0.80 – ₹2.50&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  SMS Marketing:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cost: ₹7.80+&lt;/li&gt;
&lt;li&gt;Plain text only&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example (1000 messages):
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Channel&lt;/th&gt;
&lt;th&gt;Total Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;td&gt;₹800 – ₹2500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMS&lt;/td&gt;
&lt;td&gt;₹7800+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Insight:
&lt;/h3&gt;

&lt;p&gt;👉 WhatsApp gives &lt;strong&gt;better ROI + lower cost&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;8. Notification Pricing Comparison&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WhatsApp Notification:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Utility messages&lt;/li&gt;
&lt;li&gt;Cost: ₹0.20 – ₹0.60&lt;/li&gt;
&lt;li&gt;FREE if within support window&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SMS Notification:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cost: ₹7.80&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example (1000 messages):
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Channel&lt;/th&gt;
&lt;th&gt;Total Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;td&gt;₹200 – ₹600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMS&lt;/td&gt;
&lt;td&gt;₹7800&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Insight:
&lt;/h3&gt;

&lt;p&gt;👉 Notifications are &lt;strong&gt;extremely cheap on WhatsApp&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;9. Real-Life Use Cases&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Case 1: E-commerce Platform
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Order confirmation → WhatsApp&lt;/li&gt;
&lt;li&gt;Delivery updates → WhatsApp&lt;/li&gt;
&lt;li&gt;Backup → SMS (if WhatsApp fails)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Case 2: Banking App
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OTP → WhatsApp + SMS fallback&lt;/li&gt;
&lt;li&gt;Alerts → WhatsApp&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Case 3: SaaS Platform
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Login OTP → WhatsApp&lt;/li&gt;
&lt;li&gt;Notifications → WhatsApp&lt;/li&gt;
&lt;li&gt;Marketing → WhatsApp campaigns&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;10. Best Practices&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Use Hybrid Strategy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Primary: WhatsApp&lt;/li&gt;
&lt;li&gt;Fallback: SMS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Optimize Message Length
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SMS charges per segment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Use WhatsApp Templates Smartly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Avoid unnecessary marketing messages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Track Delivery Rates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Improve ROI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Use Automation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Trigger-based messaging&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;11. Common Mistakes to Avoid&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;❌ Sending all messages via SMS&lt;br&gt;
❌ Ignoring WhatsApp free window&lt;br&gt;
❌ Not using fallback system&lt;br&gt;
❌ Sending long SMS (cost increases)&lt;br&gt;
❌ Poor template approval strategy&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;12. FAQs&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Is WhatsApp cheaper than SMS in India?
&lt;/h3&gt;

&lt;p&gt;Yes, &lt;strong&gt;5x–20x cheaper&lt;/strong&gt; depending on use case.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Can WhatsApp replace SMS completely?
&lt;/h3&gt;

&lt;p&gt;No, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some users don’t use WhatsApp&lt;/li&gt;
&lt;li&gt;SMS is still needed as fallback&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Which is better for OTP?
&lt;/h3&gt;

&lt;p&gt;👉 &lt;strong&gt;WhatsApp (cost) + SMS (reliability)&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Are WhatsApp replies free?
&lt;/h3&gt;

&lt;p&gt;Yes, within &lt;strong&gt;24-hour service window&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Why is SMS expensive?
&lt;/h3&gt;

&lt;p&gt;Because it uses &lt;strong&gt;telecom networks + carrier charges&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;13. Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you are building a modern application in India:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Best Strategy&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;WhatsApp as primary channel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;SMS as backup only&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Comparison:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WhatsApp = Cheap + Rich + Interactive&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SMS = Expensive + Basic + Reliable&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Actionable Advice:
&lt;/h3&gt;

&lt;p&gt;Start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WhatsApp OTP system&lt;/li&gt;
&lt;li&gt;WhatsApp notifications&lt;/li&gt;
&lt;li&gt;SMS fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce cost by &lt;strong&gt;80–90%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Improve user experience&lt;/li&gt;
&lt;li&gt;Increase engagement&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>How to use Codex for Coding?</title>
      <dc:creator>Rajesh Kumar</dc:creator>
      <pubDate>Thu, 19 Mar 2026 13:29:24 +0000</pubDate>
      <link>https://www.debug.school/rajesh_kumar/how-to-use-codex-for-coding-2d04</link>
      <guid>https://www.debug.school/rajesh_kumar/how-to-use-codex-for-coding-2d04</guid>
      <description>&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  📁 Project Context
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;p&gt;Directory Structure:&lt;br&gt;
-- /desd/dsds/dsds/dsd&lt;/p&gt;

&lt;p&gt;Artifacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database Schema: hospital.sql&lt;/li&gt;
&lt;li&gt;Model: xgay.php&lt;/li&gt;
&lt;li&gt;Controller: ddsadad.php&lt;/li&gt;
&lt;li&gt;View: dskjsahdkja.php&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  🎯 Role Definition
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;p&gt;Act as a senior software engineer with 15+ years of experience in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP (Core + Laravel)&lt;/li&gt;
&lt;li&gt;MySQL (schema design, indexing, query optimization)&lt;/li&gt;
&lt;li&gt;Data Structures &amp;amp; Algorithms&lt;/li&gt;
&lt;li&gt;Application Security (OWASP best practices)&lt;/li&gt;
&lt;li&gt;Performance optimization (backend + frontend)&lt;/li&gt;
&lt;li&gt;JavaScript (UX, async flows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your responses should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Practical and production-ready&lt;/li&gt;
&lt;li&gt;Performance-conscious&lt;/li&gt;
&lt;li&gt;Secure by design&lt;/li&gt;
&lt;li&gt;Clean and maintainable&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  🧩 Task 1: Code Review
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;p&gt;Objective:&lt;br&gt;
Review the current implementation of the search filter.&lt;/p&gt;

&lt;p&gt;Instructions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyze the existing search/filter logic across model, controller, and view&lt;/li&gt;
&lt;li&gt;Identify:

&lt;ul&gt;
&lt;li&gt;Code quality issues&lt;/li&gt;
&lt;li&gt;Performance bottlenecks&lt;/li&gt;
&lt;li&gt;Security risks (SQL injection, validation gaps, etc.)&lt;/li&gt;
&lt;li&gt;Scalability concerns&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Suggest improvements with reasoning&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Action:&lt;br&gt;
READ the provided files and give structured feedback.&lt;/p&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  🧩 Task 2: Feature Design (Read-Only)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;p&gt;Objective:&lt;br&gt;
Extend the search filter with additional fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;City&lt;/li&gt;
&lt;li&gt;State&lt;/li&gt;
&lt;li&gt;Country&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instructions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do NOT implement code yet&lt;/li&gt;
&lt;li&gt;Provide:

&lt;ul&gt;
&lt;li&gt;Step-by-step approach&lt;/li&gt;
&lt;li&gt;Required changes in:&lt;/li&gt;
&lt;li&gt;Database (if needed)&lt;/li&gt;
&lt;li&gt;Model&lt;/li&gt;
&lt;li&gt;Controller&lt;/li&gt;
&lt;li&gt;View/UI&lt;/li&gt;
&lt;li&gt;Query optimization strategy (indexes, joins, etc.)&lt;/li&gt;
&lt;li&gt;Validation and security considerations&lt;/li&gt;
&lt;li&gt;UX recommendations&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Action:&lt;br&gt;
Proceed ONLY after reviewing Task 1 feedback.&lt;/p&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  🧩 Task 3: Conditional Implementation
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;p&gt;Condition A (If approach is approved):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the feature end-to-end&lt;/li&gt;
&lt;li&gt;Ensure:

&lt;ul&gt;
&lt;li&gt;Clean, modular code&lt;/li&gt;
&lt;li&gt;No performance degradation&lt;/li&gt;
&lt;li&gt;Proper indexing and optimized queries&lt;/li&gt;
&lt;li&gt;Secure input handling&lt;/li&gt;
&lt;li&gt;Excellent UX (fast, intuitive filtering)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You may refer to any file as needed&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Condition B (If approach is NOT approved):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refine the design&lt;/li&gt;
&lt;li&gt;Provide alternative approaches&lt;/li&gt;
&lt;li&gt;Continue iteration until a satisfactory solution is reached&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ⚠️ Important Guidelines
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ================================
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Always think before coding&lt;/li&gt;
&lt;li&gt;Prefer optimized queries over brute-force filtering&lt;/li&gt;
&lt;li&gt;Avoid N+1 query problems&lt;/li&gt;
&lt;li&gt;Follow Laravel best practices (if applicable)&lt;/li&gt;
&lt;li&gt;Keep separation of concerns (MVC clean)&lt;/li&gt;
&lt;li&gt;Suggest improvements beyond the asked scope if valuable&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
