← back to George Gmail
TK-11952: add /api/live-check (real Google round-trip) so george arm-probe verifies deliverability, not object-existence
fdc9515f046c01ec8a7d11620f9258c094ff235b · 2026-09-20 12:26:26 -0700 · Steve Abrams
/health reports ready:!!gmail (OAuth object exists in memory), which stays truthy
through a DNS-to-Google outage (getaddrinfo ENOTFOUND oauth2.googleapis.com — the
09-13 failure). New /api/live-check does the same gmail.users.getProfile round-trip
george runs on OAuth exchange and returns 200 {live:true} ONLY when Google is
reachable (503 {live:false} otherwise). Exempted from both Basic-auth middlewares
like /health. Canaries repoint to this; fail-closed until george restart.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ActKpZmgeAcGMo4esfSb3K
Files touched
Diff
commit fdc9515f046c01ec8a7d11620f9258c094ff235b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Sep 20 12:26:26 2026 -0700
TK-11952: add /api/live-check (real Google round-trip) so george arm-probe verifies deliverability, not object-existence
/health reports ready:!!gmail (OAuth object exists in memory), which stays truthy
through a DNS-to-Google outage (getaddrinfo ENOTFOUND oauth2.googleapis.com — the
09-13 failure). New /api/live-check does the same gmail.users.getProfile round-trip
george runs on OAuth exchange and returns 200 {live:true} ONLY when Google is
reachable (503 {live:false} otherwise). Exempted from both Basic-auth middlewares
like /health. Canaries repoint to this; fail-closed until george restart.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ActKpZmgeAcGMo4esfSb3K
---
server.js | 40 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/server.js b/server.js
index 471b21c..cdd932d 100644
--- a/server.js
+++ b/server.js
@@ -346,7 +346,7 @@ function _ctEq(a, b) {
// ─── Basic Auth (skip health + OAuth callbacks) ───
app.use((req, res, next) => {
- const publicPaths = ['/health', '/auth', '/auth/info', '/auth/steve-office', '/auth/steve-personal', '/auth/agentabrams', '/auth/calendar', '/oauth2callback'];
+ const publicPaths = ['/health', '/api/live-check', '/auth', '/auth/info', '/auth/steve-office', '/auth/steve-personal', '/auth/agentabrams', '/auth/calendar', '/oauth2callback'];
if (publicPaths.includes(req.path)) return next();
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Basic ')) {
@@ -908,7 +908,7 @@ app.get('/api/auth-url', (req, res) => {
// ─── Basic Auth (exempt health + OAuth routes) ───
app.use((req, res, next) => {
const publicApiPaths = [
- '/health', '/api/health', '/auth', '/auth/info', '/auth/steve-office', '/auth/steve-personal', '/auth/agentabrams',
+ '/health', '/api/health', '/api/live-check', '/auth', '/auth/info', '/auth/steve-office', '/auth/steve-personal', '/auth/agentabrams',
'/oauth2callback', '/oauth2callback-info',
'/api/exchange-code', '/api/exchange-code/info', '/api/exchange-code/steve-personal',
'/api/auth-url', '/api/auth-url/info', '/api/auth-url/steve-personal', '/api/auth-url/agentabrams'
@@ -2729,6 +2729,42 @@ app.post('/api/calendar/events', async (req, res) => {
} catch (e) { res.status(500).json({ error: e.message }); }
});
+// ─── Live-Check (TK-11952) — REAL Google round-trip, NOT object-existence ───
+// /health reports ready:!!gmail — the in-memory OAuth object EXISTS. That stays truthy
+// straight through a DNS-to-Google outage (getaddrinfo ENOTFOUND oauth2.googleapis.com,
+// the actual 09-13 failure), so a canary greening on /health has a silent-PASS risk.
+// This route instead performs the SAME cheap live call george already runs on OAuth
+// exchange (gmail.users.getProfile, server.js:~698/882) and returns 200 {live:true} ONLY
+// when the round-trip to Google succeeds. Any failure — unconfigured client, DNS/network
+// unreachable, auth error, timeout — returns 503 {live:false}. FAIL-CLOSED: it can only
+// ever be MORE conservative than /health, never a false green.
+app.get('/api/live-check', async (req, res) => {
+ const started = Date.now();
+ // Default to the account the arm-health canaries watch (steve-office = `gmail`),
+ // but allow ?account= to probe any configured account via resolveAccount.
+ let gm, account;
+ try {
+ if (req.query.account) {
+ const r = resolveAccount(req); gm = r.gmail; account = r.key;
+ } else {
+ gm = gmail; account = 'steve-office';
+ }
+ } catch (e) {
+ return res.status(503).json({ live: false, account: account || null, error: 'account-resolve: ' + e.message, latency_ms: Date.now() - started });
+ }
+ if (!gm) {
+ // OAuth object absent — george is not authed for this account. Fail closed.
+ return res.status(503).json({ live: false, account: account || null, error: 'gmail client not configured (not authed)', latency_ms: Date.now() - started });
+ }
+ try {
+ // Bounded live round-trip. A DNS/network failure or auth error throws → 503.
+ const profile = await gm.users.getProfile({ userId: 'me' }, { timeout: 8000 });
+ return res.json({ live: true, account, email: profile.data.emailAddress, latency_ms: Date.now() - started });
+ } catch (e) {
+ return res.status(503).json({ live: false, account, error: e.message, latency_ms: Date.now() - started });
+ }
+});
+
// ─── Health Check (updated for 3 accounts) ───
app.get('/health', (req, res) => {
res.json({
← fbea155 auto-data-snapshot: 2026-09-20T08:21:12 (1 data files) — dat
·
back to George Gmail
·
auto-data-snapshot: 2026-09-21T04:28:28 (1 data files) — dat c1ded9d →