j-riv@localhost:~$

google-chat-notifications-service

internal

A backend microservice that centralizes operational alerting by receiving signed webhooks from multiple internal and third-party systems (deployment, error monitoring, database, and ERP platforms) and relaying them as formatted notification cards into Google Chat spaces. Also acts as a Google Chat app, responding to slash commands and space events.

TypeScriptNode.jsExpressGoogle Chat APIZodJSON Web Tokens (JWT/JWKS)AWS ECRAWS EC2
cat architecture.md

Containerized Deployment - AWS (ECR + EC2)

  • Docker image built from the Node.js app and pushed to Amazon ECR for versioned image storage
  • Deployed and run on an EC2 instance pulling the image from ECR, with a fixed timezone for consistent log/event timestamps
  • Compiles TypeScript to a production dist/ bundle inside the image and runs the compiled server on container start
  • Exposes a single HTTP port on the instance for all inbound traffic

External Systems (Webhook Sources)

  • Deployment platform sends build/deploy event webhooks
  • Error monitoring platform sends issue/alert webhooks
  • Database platform sends alert webhooks
  • ERP system (custom module) sends business record alerts (e.g. new leads, order events)

Express API Layer

  • Exposes per-provider webhook routes scoped to a target chat space, e.g. /v1/webhook/:space/{provider}
  • Applies Helmet security headers, CORS, and a locked-down robots.txt
  • Captures the raw request body during JSON parsing so signatures can be verified against the exact bytes received
  • Returns a JSON status/uptime endpoint and a rendered HTML status page for health checks

Authentication & Request Verification

  • Each provider webhook is verified before reaching business logic, rejecting unverified requests with 403
  • Deployment webhook payloads are verified via signed JWT with issuer validation
  • Monitoring, database, and ERP webhooks are verified via per-provider HMAC signatures (SHA-1/SHA-256) using shared secrets
  • Inbound Google Chat app events are verified using Google-issued JWTs validated against Google’s public JWKS endpoint and the configured Cloud project number

Payload Validation

  • ERP alert payloads are validated with a schema enforcing required fields (record type, ID, event type, title, severity) before processing
  • Invalid payloads are rejected with 400 before any external call is made

Google Chat Integration

  • Uses a Google Cloud service account with app-level OAuth credentials to authenticate as the Chat app
  • Formats each provider event into a provider-specific Google Chat card and posts it to the target space via the Chat API
  • Supports interactive slash commands (e.g. echoing the triggering event, returning service uptime)

Notification Delivery

  • Successful deliveries return 200; failures to reach Google Chat return 502 so upstream systems can detect delivery issues
  • Notification routing target is derived from a URL path parameter, keeping space selection decoupled from the payload itself
# Why per-provider signature verification instead of a shared secret?
Each upstream system (deployment platform, monitoring, database, ERP) uses its own native signing scheme (JWT vs. HMAC, SHA-1 vs. SHA-256, hex vs. base64). Verifying against the raw captured request body with provider-specific config prevents forged notifications and keeps each integration independently rotatable without affecting the others.
# Why a stateless relay with no database?
The service only transforms and forwards event data; it does not need to persist notification history or state, which keeps the deployment simple, reduces attack surface, and avoids managing a data store for what is essentially a fire-and-forget integration layer.
# Why validate ERP payloads with a schema before building chat cards?
Business system alerts have the most variable and highest-stakes payloads (record data, severity, links). Schema validation fails fast with clear error detail, preventing malformed or unexpected data from silently producing broken notification cards.
# Why authenticate Google Chat app events separately from webhook events?
Inbound webhooks and inbound Chat app events have different trust boundaries and identity providers, so the service verifies Chat events against Google’s own JWKS-issued tokens and project number rather than reusing the shared-secret scheme used for third-party webhooks.
# Why route notifications by space ID in the URL rather than the payload?
Keeping the destination as a path parameter (rather than trusting a space field in the request body) ensures the routing decision is controlled by the service configuration/URL registered with each upstream system, reducing the risk of a compromised or misconfigured payload redirecting alerts to the wrong space.