Continuous Monitoring

zGovern's continuous monitoring engine automatically scans your connected integrations every 6 hours, surfaces compliance failures in real time, and keeps your risk register up to date without manual intervention.

How Continuous Monitoring Works

When you connect an integration (e.g., AWS, GitHub, Okta), zGovern schedules recurring automated checks using node-cron. Each check inspects a specific configuration or security control — for example, whether S3 bucket logging is enabled or whether all Okta users have MFA enrolled.

Check results are stored as IntegrationCheck records and are immediately visible in the Monitoring page. The results feed into:

  • The Compliance Score shown on the Dashboard (cached for up to 7 hours)
  • The Risk Register (auto-creates risks for FAIL + HIGH severity checks)
  • The Audit Workspace (auto-evidence tagged [Auto])
  • Email alerts to ADMIN users for newly failing checks

Scan Schedule

Job Schedule Cron Expression Description
Full integration scan Every 6 hours 0 */6 * * * Runs all checks for all active integrations across all organizations
Weekly digest Monday, 8:00 AM 0 8 * * 1 Sends a summary email to all ADMIN users with pass/fail counts and trend direction
Manual scan available You can trigger an immediate scan at any time from the Monitoring page (click Run Now) or via the API: POST /api/monitoring/run-now. This is useful after fixing a compliance issue to verify resolution without waiting up to 6 hours.

Check Statuses

Each automated check produces one of four status values:

Status Meaning Action Required?
PASS The control is properly configured and compliant No — monitor for regressions
FAIL The control is misconfigured or non-compliant Yes — a risk is auto-created; remediate immediately
WARNING The control is partially compliant or a best practice is not followed Recommended — review and assess risk
INFO Informational data collected — no compliance judgment No — for context only

Trend Analysis

The Trend Analysis tab in the Monitoring page shows a sparkline table for each check, displaying its status history across the last 10 scan runs. This lets you quickly identify:

  • Flapping checks — oscillating between PASS and FAIL (indicates intermittent issues)
  • Improving checks — recently moved to PASS after remediation
  • Degrading checks — recently moved from PASS to FAIL
  • Stable passing checks — consistently green (high confidence)

The API returns trend data with a direction indicator:

GET /api/monitoring/trends — response
{
  "success": true,
  "data": [
    {
      "checkName": "AWS IAM MFA Enforcement",
      "integration": "AWS Production",
      "latestStatus": "PASS",
      "trend": "improving",
      "sparkline": ["FAIL", "FAIL", "FAIL", "PASS", "PASS", "PASS", "PASS", "PASS", "PASS", "PASS"]
    },
    {
      "checkName": "GitHub Branch Protection",
      "integration": "GitHub Org",
      "latestStatus": "FAIL",
      "trend": "degrading",
      "sparkline": ["PASS", "PASS", "PASS", "PASS", "PASS", "PASS", "PASS", "FAIL", "FAIL", "FAIL"]
    }
  ]
}

Auto-Risk Creation

When a check transitions to FAIL status with a HIGH or CRITICAL severity, zGovern automatically creates a risk entry in the Risk Register. This ensures no compliance failure goes untracked.

Check Severity Auto-Risk Created? Risk Severity
CRITICAL Yes CRITICAL
HIGH Yes HIGH
MEDIUM No (manual)
LOW No (manual)

The auto-created risk includes:

  • title: derived from the check name (e.g., "AWS S3 Public Bucket Exposure")
  • description: the check's failure detail message
  • sourceCheckId: foreign key linking back to the IntegrationCheck record
  • likelihood and impact: set based on the check's predefined values

Alert Deduplication

zGovern tracks which checks were already failing in the previous scan run. Email alerts are only sent for newly failing checks — checks that were PASS in the last run and are now FAIL. This prevents alert fatigue from repeated notifications about the same known issue.

💡
Suppressing known failures If a check is failing due to a known, accepted architectural decision (e.g., a public S3 bucket intentionally used for a static website), create a risk with status ACCEPTED in the Risk Register. This acknowledges the finding without requiring remediation.

Risk Re-open Behavior

When a risk is marked RESOLVED or CLOSED but its linked source check subsequently fails again, zGovern automatically:

  1. Reopens the risk (sets status back to OPEN)
  2. Increments the reopenCount field
  3. Updates lastReopenedAt to the current timestamp
  4. Sends an email alert noting the risk has been reopened

This behavior is visible in the Risk Register — risks with a non-zero reopenCount display a badge indicating how many times they have been reopened.

Email Alerts

zGovern can send email notifications using any SMTP provider. Configure the following environment variables in docker-compose.yml:

Variable Required Description
SMTP_HOST Yes (for email) SMTP server hostname (e.g., smtp.sendgrid.net)
SMTP_PORT Yes (for email) SMTP port — typically 587 (TLS) or 465 (SSL)
SMTP_USER Yes (for email) SMTP authentication username
SMTP_PASS Yes (for email) SMTP authentication password or API key
SMTP_FROM Yes (for email) From address (e.g., alerts@zgovern.com)
APP_URL Recommended Public URL of your app — used in email links (e.g., https://app.example.com)
Console fallback If SMTP_HOST is not set, all emails are logged to the backend console in the format they would be sent. This is useful for development and testing.

Email Types

Email Recipients Trigger
New scan failure alert All ADMIN users A check transitions from PASS → FAIL
Risk reopened alert All ADMIN users + risk owner A resolved/closed risk is reopened by a failing check
Password reset The requesting user POST /api/auth/forgot-password
Weekly digest All ADMIN users Every Monday at 8:00 AM

Manual Scan API

Trigger an immediate scan programmatically:

bash
curl -X POST https://app.zgovern.com/api/monitoring/run-now \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json"
Response
{
  "success": true,
  "message": "Monitoring scan started",
  "data": {
    "jobId": "scan_1709900400000",
    "integrationsQueued": 3
  }
}